Convert URLSession.downloadTask to Combine

This commit is contained in:
Brandon Evans 2021-01-01 10:37:33 -07:00
parent 95ca2bc1c3
commit fa277ccaae
No known key found for this signature in database
GPG key ID: D58A4B8DB64F8E93
3 changed files with 26 additions and 20 deletions

View file

@ -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 = "<group>"; };
CABFA9B02592EEEA00380FEE /* Promise+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+.swift"; sourceTree = "<group>"; };
CABFA9B22592EEEA00380FEE /* Entry+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Entry+.swift"; sourceTree = "<group>"; };
CABFA9B32592EEEA00380FEE /* URLSession+Promise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+Promise.swift"; sourceTree = "<group>"; };
CABFA9B32592EEEA00380FEE /* URLSession+DownloadTaskPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+DownloadTaskPublisher.swift"; sourceTree = "<group>"; };
CABFA9B42592EEEA00380FEE /* Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = "<group>"; };
CABFA9B82592EEEA00380FEE /* FileManager+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+.swift"; sourceTree = "<group>"; };
CABFA9B92592EEEA00380FEE /* Downloads.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Downloads.swift; sourceTree = "<group>"; };
@ -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 */,

View file

@ -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)
}
}

View file

@ -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)
}
}
}