From fa277ccaae2799194c0bb214de07cf452aa39b79 Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Fri, 1 Jan 2021 10:37:33 -0700 Subject: [PATCH] Convert URLSession.downloadTask to Combine --- Xcodes.xcodeproj/project.pbxproj | 8 ++--- Xcodes/Backend/Environment.swift | 8 ++--- ...=> URLSession+DownloadTaskPublisher.swift} | 30 +++++++++++-------- 3 files changed, 26 insertions(+), 20 deletions(-) rename Xcodes/Backend/{URLSession+Promise.swift => URLSession+DownloadTaskPublisher.swift} (60%) diff --git a/Xcodes.xcodeproj/project.pbxproj b/Xcodes.xcodeproj/project.pbxproj index 2a053a2..7a04c70 100644 --- a/Xcodes.xcodeproj/project.pbxproj +++ b/Xcodes.xcodeproj/project.pbxproj @@ -41,7 +41,7 @@ CAA1CB4D255A5CFD003FD669 /* SignInPhoneListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAA1CB4C255A5CFD003FD669 /* SignInPhoneListView.swift */; }; CABFA9BB2592EEEA00380FEE /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9BA2592EEEA00380FEE /* DateFormatter+.swift */; }; CABFA9BD2592EEEA00380FEE /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A92592EEE900380FEE /* Environment.swift */; }; - CABFA9BF2592EEEA00380FEE /* URLSession+Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B32592EEEA00380FEE /* URLSession+Promise.swift */; }; + CABFA9BF2592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */; }; CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9A82592EEE900380FEE /* Version+.swift */; }; CABFA9C22592EEEA00380FEE /* Promise+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B02592EEEA00380FEE /* Promise+.swift */; }; CABFA9C32592EEEA00380FEE /* Downloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFA9B92592EEEA00380FEE /* Downloads.swift */; }; @@ -171,7 +171,7 @@ CABFA9AE2592EEE900380FEE /* Path+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Path+.swift"; sourceTree = ""; }; CABFA9B02592EEEA00380FEE /* Promise+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+.swift"; sourceTree = ""; }; CABFA9B22592EEEA00380FEE /* Entry+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Entry+.swift"; sourceTree = ""; }; - CABFA9B32592EEEA00380FEE /* URLSession+Promise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+Promise.swift"; sourceTree = ""; }; + CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+DownloadTaskPublisher.swift"; sourceTree = ""; }; CABFA9B42592EEEA00380FEE /* Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = ""; }; CABFA9B82592EEEA00380FEE /* FileManager+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+.swift"; sourceTree = ""; }; CABFA9B92592EEEA00380FEE /* Downloads.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Downloads.swift; sourceTree = ""; }; @@ -338,7 +338,7 @@ CABFA9B02592EEEA00380FEE /* Promise+.swift */, CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */, CABFA9AB2592EEE900380FEE /* URLRequest+Apple.swift */, - CABFA9B32592EEEA00380FEE /* URLSession+Promise.swift */, + CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */, CABFA9A82592EEE900380FEE /* Version+.swift */, CA9FF876259528CC00E47BAF /* Version+XcodeReleases.swift */, CABFA9A62592EEE900380FEE /* Version+Xcode.swift */, @@ -623,7 +623,7 @@ CA9FF9362595B44700E47BAF /* HelperClient.swift in Sources */, CABFA9CA2592EEEA00380FEE /* AppState+Update.swift in Sources */, CA44901F2463AD34003D8213 /* Tag.swift in Sources */, - CABFA9BF2592EEEA00380FEE /* URLSession+Promise.swift in Sources */, + CABFA9BF2592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift in Sources */, CABFA9BB2592EEEA00380FEE /* DateFormatter+.swift in Sources */, CABFA9BD2592EEEA00380FEE /* Environment.swift in Sources */, CABFA9C32592EEEA00380FEE /* Downloads.swift in Sources */, diff --git a/Xcodes/Backend/Environment.swift b/Xcodes/Backend/Environment.swift index 26555ad..48b4f96 100644 --- a/Xcodes/Backend/Environment.swift +++ b/Xcodes/Backend/Environment.swift @@ -113,10 +113,10 @@ public struct Network { dataTask(request) } - public var downloadTask: (URLRequestConvertible, URL, Data?) -> (Progress, Promise<(saveLocation: URL, response: URLResponse)>) = { AppleAPI.Current.network.session.downloadTask(with: $0, to: $1, resumingWith: $2) } - - public func downloadTask(with convertible: URLRequestConvertible, to saveLocation: URL, resumingWith resumeData: Data?) -> (progress: Progress, promise: Promise<(saveLocation: URL, response: URLResponse)>) { - return downloadTask(convertible, saveLocation, resumeData) + public var downloadTask: (URL, URL, Data?) -> (Progress, AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) = { AppleAPI.Current.network.session.downloadTask(with: $0, to: $1, resumingWith: $2) } + + public func downloadTask(with url: URL, to saveLocation: URL, resumingWith resumeData: Data?) -> (progress: Progress, publisher: AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) { + return downloadTask(url, saveLocation, resumeData) } } diff --git a/Xcodes/Backend/URLSession+Promise.swift b/Xcodes/Backend/URLSession+DownloadTaskPublisher.swift similarity index 60% rename from Xcodes/Backend/URLSession+Promise.swift rename to Xcodes/Backend/URLSession+DownloadTaskPublisher.swift index 04ee10e..d721712 100644 --- a/Xcodes/Backend/URLSession+Promise.swift +++ b/Xcodes/Backend/URLSession+DownloadTaskPublisher.swift @@ -1,6 +1,5 @@ +import Combine import Foundation -import PromiseKit -import PMKFoundation extension URLSession { /** @@ -8,40 +7,47 @@ extension URLSession { - Parameter saveLocation: A URL to move the downloaded file to after it completes. Apple deletes the temporary file immediately after the underyling completion handler returns. - Parameter resumeData: Data describing the state of a previously cancelled or failed download task. See the Discussion section for `downloadTask(withResumeData:completionHandler:)` https://developer.apple.com/documentation/foundation/urlsession/1411598-downloadtask# - - Returns: Tuple containing a Progress object for the task and a promise containing the save location and response. + - Returns: Tuple containing a Progress object for the task and a publisher of the save location and response. - Note: We do not create the destination directory for you, because we move the file with FileManager.moveItem which changes its behavior depending on the directory status of the URL you provide. So create your own directory first! */ - public func downloadTask(with convertible: URLRequestConvertible, to saveLocation: URL, resumingWith resumeData: Data?) -> (progress: Progress, promise: Promise<(saveLocation: URL, response: URLResponse)>) { + public func downloadTask( + with url: URL, + to saveLocation: URL, + resumingWith resumeData: Data? + ) -> (progress: Progress, publisher: AnyPublisher<(saveLocation: URL, response: URLResponse), Error>) { var progress: Progress! - let promise = Promise<(saveLocation: URL, response: URLResponse)> { seal in + // Intentionally not wrapping in Deferred because we need to return the Progress! immediately. + // Probably a sign that this should be implemented differently... + let promise = Future<(saveLocation: URL, response: URLResponse), Error> { promise in let completionHandler = { (temporaryURL: URL?, response: URLResponse?, error: Error?) in if let error = error { - seal.reject(error) + promise(.failure(error)) } else if let response = response, let temporaryURL = temporaryURL { do { try FileManager.default.moveItem(at: temporaryURL, to: saveLocation) - seal.fulfill((saveLocation, response)) + promise(.success((saveLocation, response))) } catch { - seal.reject(error) + promise(.failure(error)) } } else { - seal.reject(PMKError.invalidCallingConvention) + fatalError("Expecting either a temporary URL and a response, or an error, but got neither.") } } let task: URLSessionDownloadTask if let resumeData = resumeData { - task = downloadTask(withResumeData: resumeData, completionHandler: completionHandler) + task = self.downloadTask(withResumeData: resumeData, completionHandler: completionHandler) } else { - task = downloadTask(with: convertible.pmkRequest, completionHandler: completionHandler) + task = self.downloadTask(with: url, completionHandler: completionHandler) } progress = task.progress task.resume() } + .eraseToAnyPublisher() return (progress, promise) } -} \ No newline at end of file +}