URLSession.datatask with request block not called in background
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 backgroundURLSessionConfiguration
.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 yourURLSessionDelegate
, 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
}
Swift background data task
you will need to implement AppDelegate
method
- application(_:handleEventsForBackgroundURLSession:completionHandler:)
and this into your delegate
- urlSessionDidFinishEvents(forBackgroundURLSession:)
Also, you should implement background modes. A relative answer to your question could be found here. URLSession.datatask with request block not called in background
Does NSURLSession for HTTP data task (NSURLSessionDataTask) runs in background thread or we will have to provide the queue?
No, you don't need to use GCD to dispatch this to background queue. In fact, because the completion block runs on background thread, the exact opposite is true, that if you need anything in that block to run on the main queue (e.g., synchronized updates to model objects, UI updates, etc.), you have to manually dispatch that to the main queue yourself. For example, let's imagine that you were going to retrieve a list of results and update the UI to reflect this, you might see something like:
- (void)viewDidLoad
{
[super viewDidLoad];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:@"https://itunes.apple.com/search?term=apple&media=software"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// this runs on background thread
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
// detect and handle errors here
// otherwise proceed with updating model and UI
dispatch_async(dispatch_get_main_queue(), ^{
self.searchResults = json[@"results"]; // update model objects on main thread
[self.tableView reloadData]; // also update UI on main thread
});
NSLog(@"%@", json);
}];
[dataTask resume];
}
Related Topics
Changed Project Name in Xcode Causing Naming Error
Wkwebview in Interface Builder
How to Disable Uitextfield Editing But Still Accept Touch
Black Screen After Adding Scenedelegate and Updating Info.Plist
How to Remove Cache in Wkwebview
How to Hide/Show Tab Bar of a View with a Navigation Bar in iOS
Swift iOS Check If Remote Push Notifications Are Enabled in iOS9 and iOS10
How to Use a 'Container View' in iOS
Prevent Dismissal of Modal View Controller in Swiftui
How to Use Addtarget Method in Swift 3
Change Language of Alert in Banner of Push Notification
How to Set Kerning in iPhone Uilabel
Xcode 10: Unable to Attach Db Error
Swift - Download a Video from Distant Url and Save It in an Photo Album
Making a Phone Call in an iOS Application
Create a Copy of a Uiview in Swift
Restricting App Installations from Appstore Only to Users with iPhone 5/5S/5C