From 1adbed9f8b94521befd237c14d36325a55037a41 Mon Sep 17 00:00:00 2001 From: Nicholas Tay Date: Sun, 17 Jul 2022 02:13:04 +1000 Subject: Experiment with async/await Note that URLSession async only works >=iOS 15. I changed the target for now, but may mess with continuations. --- foray.xcodeproj/project.pbxproj | 4 +- .../xcshareddata/xcschemes/foray.xcscheme | 78 ++++++++++++++++++++++ foray/Fetchers/ForayFetcher.swift | 27 +++----- foray/Presenters/PenguinItemPresenter.swift | 19 +++--- foray/Scenes/ForayTableViewController.swift | 16 +++-- 5 files changed, 111 insertions(+), 33 deletions(-) create mode 100644 foray.xcodeproj/xcshareddata/xcschemes/foray.xcscheme diff --git a/foray.xcodeproj/project.pbxproj b/foray.xcodeproj/project.pbxproj index 3d24015..3901dc6 100644 --- a/foray.xcodeproj/project.pbxproj +++ b/foray.xcodeproj/project.pbxproj @@ -371,7 +371,7 @@ INFOPLIST_KEY_UIMainStoryboardFile = ""; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -401,7 +401,7 @@ INFOPLIST_KEY_UIMainStoryboardFile = ""; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/foray.xcodeproj/xcshareddata/xcschemes/foray.xcscheme b/foray.xcodeproj/xcshareddata/xcschemes/foray.xcscheme new file mode 100644 index 0000000..09e83ac --- /dev/null +++ b/foray.xcodeproj/xcshareddata/xcschemes/foray.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/foray/Fetchers/ForayFetcher.swift b/foray/Fetchers/ForayFetcher.swift index d8df037..e4c6fd9 100644 --- a/foray/Fetchers/ForayFetcher.swift +++ b/foray/Fetchers/ForayFetcher.swift @@ -27,23 +27,18 @@ class ForayFetcher { return jd }() - func fetch(url: String, - receiver: @escaping (T) -> ()) { - // Fetch on a background thread - DispatchQueue.global(qos: .background).async { - var request = URLRequest(url: URL(string: url)!) - request.cachePolicy = .reloadRevalidatingCacheData // Needed otherwise default caching policy seems not to check properly - - // Basic auth if required - if (self.basicUsername != nil && self.basicPassword != nil) { - let authData = (self.basicUsername! + ":" + self.basicPassword!).data(using: .utf8)!.base64EncodedString() - request.addValue("Basic \(authData)", forHTTPHeaderField: "Authorization") - } + func fetch(url: String) async throws -> T { + var request = URLRequest(url: URL(string: url)!) + request.cachePolicy = .reloadRevalidatingCacheData // Needed otherwise default caching policy seems not to check properly - URLSession.shared.dataTask(with: request, completionHandler: { data, response, error -> Void in - let items = try! self.jsonDecoder.decode(T.self, from: data!) - receiver(items) - }).resume() + // Basic auth if required + if (self.basicUsername != nil && self.basicPassword != nil) { + let authData = (self.basicUsername! + ":" + self.basicPassword!).data(using: .utf8)!.base64EncodedString() + request.addValue("Basic \(authData)", forHTTPHeaderField: "Authorization") } + + let (data, _) = try await URLSession.shared.data(for: request) + let items = try jsonDecoder.decode(T.self, from: data) + return items } } diff --git a/foray/Presenters/PenguinItemPresenter.swift b/foray/Presenters/PenguinItemPresenter.swift index c4553ae..1d617bf 100644 --- a/foray/Presenters/PenguinItemPresenter.swift +++ b/foray/Presenters/PenguinItemPresenter.swift @@ -8,18 +8,19 @@ import Foundation class PenguinItemPresenter { + + private struct Constants { + static let apiEndpoint = "https://users.windblume.net/~nick/upload/dummy.json" + } let fetcher = ForayFetcher() - func fetch(receiver: @escaping ([PenguinItemViewModel]) -> ()) { - fetcher.fetch(url: "https://users.windblume.net/~nick/upload/dummy.json") { [weak self] (apiItems: [PenguinItemModel]) in - // Callback to main thread here - // There probably is a nicer way to do it, but we will DispatchQueue it back - // from the Presenter-level for now (main thread from VC onwards) - DispatchQueue.main.async { - guard let self = self else { return } - receiver(self.transform(models: apiItems)) - } + func fetch() async -> [PenguinItemViewModel] { + do { + let apiItems: [PenguinItemModel] = try await fetcher.fetch(url: Constants.apiEndpoint) + return transform(models: apiItems) + } catch { + return [] } } diff --git a/foray/Scenes/ForayTableViewController.swift b/foray/Scenes/ForayTableViewController.swift index a29088c..849553c 100644 --- a/foray/Scenes/ForayTableViewController.swift +++ b/foray/Scenes/ForayTableViewController.swift @@ -42,21 +42,25 @@ class ForayTableViewController: UITableViewController, Coordinated { self.refreshControl?.addTarget(self, action: #selector(doRefresh), for: UIControl.Event.valueChanged) } - @objc func doRefresh(sender: AnyObject) { + @objc + func doRefresh(sender: AnyObject) { reloadApiData() } func reloadApiData() { - presenter.fetch { [weak self] (data: [PenguinItemViewModel]) in - guard let self = self else { return } - - let groups = Dictionary(grouping: data) { $0.year } + Task(priority: .medium) { [weak self] in + let models = await self?.presenter.fetch() + + guard let self = self, + let models = models else { return } + + let groups = Dictionary(grouping: models) { $0.year } self.sections = groups.map { (key, values) in return YearSection(year: key, items: values) } // Sort the sections from oldest year to newest self.sections.sort { (lhs, rhs) in lhs.year < rhs.year } - + self.tableView.reloadData() self.refreshControl?.endRefreshing() self.coordinator?.hideLoading() -- cgit