If you want downloads to progress after your app is no longer in foreground, you have to use background session. The basic constraints of background sessions are outlined in Downloading Files in Background, and are essentially:
Use delegate-based URLSession
with background URLSessionConfiguration
.
Use upload and download tasks only, with no completion handlers.
In iOS, Implement application(_:handleEventsForBackgroundURLSession:completionHandler:)
app delegate, saving the completion handler and starting your background session.
Implement urlSessionDidFinishEvents(forBackgroundURLSession:)
in your URLSessionDelegate
, calling that saved completion handler to let OS know you're done processing the background request completion.
So, pulling that together:
func startRequest(for urlString: String, method: String, parameters: String) {
let url = URL(string: urlString)!
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 20)
request.httpMethod = method
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpBody = parameters.data(using: .utf8)
BackgroundSession.shared.start(request)
}
Where
class BackgroundSession: NSObject {
static let shared = BackgroundSession()
static let identifier = "com.domain.app.bg"
private var session: URLSession!
#if !os(macOS)
var savedCompletionHandler: (() -> Void)?
#endif
private override init() {
super.init()
let configuration = URLSessionConfiguration.background(withIdentifier: BackgroundSession.identifier)
session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}
func start(_ request: URLRequest) {
session.downloadTask(with: request).resume()
}
}
extension BackgroundSession: URLSessionDelegate {
#if !os(macOS)
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
self.savedCompletionHandler?()
self.savedCompletionHandler = nil
}
}
#endif
}
extension BackgroundSession: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
// handle failure here
print("(error.localizedDescription)")
}
}
}
extension BackgroundSession: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
do {
let data = try Data(contentsOf: location)
let json = try JSONSerialization.jsonObject(with: data)
print("(json)")
// do something with json
} catch {
print("(error.localizedDescription)")
}
}
}
And the iOS app delegate does:
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
BackgroundSession.shared.savedCompletionHandler = completionHandler
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…