Reputation: 899
My end goal is to make http requests in the background while handling the response in memory. From my understanding, background requests have to be made with a custom delegate (which means I can't use dataTaskWithRequest(request, completionHandler)
), and to deal with the response in memory I have to use a data task (which means I can't use a download task along with URLSession(session, downloadTask, didFinishDownloadingToURL)
).
According to this: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/NSURLSessionConcepts/NSURLSessionConcepts.html#//apple_ref/doc/uid/10000165i-CH2-SW1 it doesn't look like there are any delegate methods that get called for data tasks upon completion. Is the only way to work with the response through a delegate to work with individual NSData fragments through URLSession(session, dataTask, data)
? https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSessionDataDelegate_protocol/index.html#//apple_ref/occ/intfm/NSURLSessionDataDelegate/URLSession:dataTask:didReceiveData: There's no delegate method for handling the entire final response as a single NSData instance?
Upvotes: 3
Views: 2523
Reputation: 5954
If I correctly understand the docs you're supposed to implement the
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
method from URLSessionTaskDelegate
. It "tells the delegate that the task finished transferring data" (per docs).
It will be called with nil
value for error
, but you also have to check task.response
for potential problems.
Before that you should manually collect pieces of your data coming via the URLSession:dataTask:didReceiveData:
calls:
This delegate method may be called more than once, and each call provides only data received since the previous call. The app is responsible for accumulating this data if needed.
There's no delegate methods that would combine all the data into a single object for you, you have to do it manually.
Upvotes: 0
Reputation: 2678
For small quantities of data like API calls or small images like avatars meant to be displayed immediately, dataTaskWithRequest(request, completionHandler)
is the method you want. It sets up an asynchronous task, meaning that when you start the task, execution will return to your code immediately and the task will take care of downloading the data and buffering it in memory "in the background" while your app is running. Once all the download task is complete, it will call your completionHandler to let it know that it's done and that the data is ready. It will pass the data to your handler as an argument.
For larger files like podcasts, videos and large images, you'll want iOS to download the file for you even when the user starts looking at another app and your app is suspended. You will then want to use a NSURLSessionDownloadTask with a background session configuration backgroundSessionConfigurationWithIdentifier:
and a custom delegate. Your custom delegate will need to implement the method URLSession:downloadTask:didFinishDownloadingToURL:
. When this method is called, you can read the contents of the file at the url
that will be passed to you using code like this:
let data = NSData(contentsOfURL: url)
The reason background downloads that persist after an iOS app is quit are handled like this is that iOS wants to be able to continue downloading multiple files on behalf of different apps like podcasts, videos, etc. If the user is on a high speed network, downloading multiple large files in memory can quickly consume all of the devices memory, so they get stored as they are downloaded instead. In the same vein, you should keep your file sizes in mind before reading the entire file into memory with NSData(contentsOfURL:)
.
Here's a working example of how everything fits together. Paste this in an iOS playground and look at the image you'll get:
import UIKit
class MyDelegate: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate {
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
if let data = NSData(contentsOfURL: location) {
// work with data ...
UIImage(data: data)
}
}
}
let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("my-session-identifier")
let delegate = MyDelegate()
let session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
let url = NSURL(string: "https://pbs.twimg.com/profile_images/473550724433858560/tuHsaI2U.png")!
let task = session.downloadTaskWithURL(url)
task.resume()
// this allows the code to run in a playground
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
Upvotes: 3