NSURLSessionUploadTask get response data
You don't mention implementing URLSessionDidFinishEventsForBackgroundURLSession
(a NSURLSessionDelegate
method). You really want to implement that, too. The basic process is:
In app delegate's
handleEventsForBackgroundURLSession
, you should:- start the background
NSURLSession
(which will start receiving delegate method calls associated with all of the uploads); and - save the completion handler (but do not call it, yet).
- start the background
Then, in
URLSessionDidFinishEventsForBackgroundURLSession
(when you're done processing all of the responses), you call the completion handler you saved inhandleEventsForBackgroundURLSession
. (Make sure to dispatch that to the main queue.)
If you're doing all of that, when the background session is restarted, the didReceiveData
calls will come in with the responses to your various uploads.
I just did a quick test, uploading five 20mb images and immediately terminating the app. Then, even though the app wasn't running, I watched the five files slowly show up on my server (obviously handled by the daemon process). When all five were done, by app was transparently restarted in the background, the handleEventsForBackgroundURLSession
was called (which restarted the session), it let all of the didReceiveData
calls quickly get called, and when that was done, URLSessionDidFinishEventsForBackgroundURLSession
was called and my app only then called the saved completion handler.
In terms of why this isn't working for you, there's not enough to diagnose the problem. Possibilities include:
Maybe you terminated the app inappropriately. You can't kill the app by double tapping the home button and terminating the app there; you have to let it naturally terminate on it's own, or for diagnostic/testing purposes, I force it to terminate by calling
exit(0)
in code.Maybe you didn't restart the session when
handleEventsForBackgroundURLSession
was called.Maybe you called the supplied completion handler too soon (i.e. before
URLSessionDidFinishEventsForBackgroundURLSession
was called).
It's hard to say, but I suspect that there's something buried inside your implementation that isn't quite right and it's hard to say what it is on the basis of the information provided (assuming it isn't one of the above points). Unfortunately, debugging this background sessions is vexingly complicated because when the app terminates, it is no longer attached to the debugger, so you can't easily debug what happens after the app is restarted automatically by iOS). Personally, I either NSLog
messages and just watch the device console (as well as watching what appears on the server), or I build some persistent logging mechanism into the app itself.
How to get backgroundSession NSURLSessionUploadTask response
The URLSession:task:didCompleteWithError:
method of NSURLSessionTaskDelegate
should be called when your upload is done. Refer to the task.response
object, which should be the NSHTTPURLResponse
object.
I'm sure you're doing this, but the standard background upload task components are:
Make a background session:
NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.domain.app"];
NSURLSession *session = [NSURLSession sessionWithConfiguration:backgroundConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];Use the
NSURLSession
methoduploadTaskWithRequest:fromFile:
method:NSURLSessionTask *task = [session uploadTaskWithRequest:request fromFile:fileURL];
[task resume];With a background session, you must:
Use
NSURLSessionUploadTask
;Use file-based rendition (you cannot use
NSData
based version);Use delegate-based rendition
cannot use data tasks; (b)NSData
rendition of theNSURLSessionUploadTask
; nor (c) a completion block rendition of theNSURLSessionUploadTask
.
With upload tasks, make sure to not call
setHTTPBody
of aNSMutableRequest
. With upload tasks, the body of the request cannot be in the request itself.Make sure you implement the appropriate
NSURLSessionDelegate
,NSURLSessionTaskDelegate
methods.Make sure to implement
application:handleEventsForBackgroundURLSession:
in your app delegate (so you can capture thecompletionHandler
, which you'll call inURLSessionDidFinishEventsForBackgroundURLSession
).
NSURLSession: method dataTaskWithRequest never reach completion callback on lengthy responses
When fetching large resources, you should use download task. A data task will attempt to load the entire response in a single NSData
object. Loading a large asset in memory at the same time is not only inefficient, but if it is extraordinarily large, can cause problems.
A download task is well suited for these tasks, because it will stream the asset to a temporary file for you, reducing the peak memory usage. (Admittedly, you can manually achieve the same with data task with delegate pattern, but download tasks do this for you.)
You said:
I saw that there's an alternative method in
NSURLSession
dedicated for downloading files calleddownloadTaskWithRequest
but it doesn't have an async completion block.
Two observations:
There is a rendition,
dataTaskWithRequest:completionHandler:
, that has a completion block:NSURLSession* session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
...
}];
[task resume];Note, I would advise using
sharedSession
if you are not setting a delegate, or otherwise customizing yourNSURLSession
. You do not want to instantiateNSURLSession
objects unnecessarily. And if you really must instantiate aNSURLSession
, re-use it for subsequent tasks and/or make sure to callfinishTasksAndInvalidate
after submitting the last task for that session, or else theNSURLSession
will leak. And, if you instantiate your ownNSURLSession
, you do not have to instantiate your own operation queue, as it will, by default, create a serial queue for you if you do not supply an operation queue.The rendition without a block parameter,
downloadTaskWithURL:
, works, too. All you need to do is to specify adelegate
for yourNSURLSession
and then and implementURLSession:downloadTask:didFinishDownloadingToURL:
.The reason I suggest this is that, often, when we are downloading very large assets (especially over cellular), we realize that the users may want to leave our app and let the download complete in the background. In those situations, we would use a background
NSURLSessionConfiguration
. And when using background sessions, you must use this delegate-based approach. So, if you think you might eventually adopt background sessions for long downloads, then adopting a delegate-based approach now is not a bad idea.For more information, see Downloading Files in the Background.
How to get data from blocks using NSURLSession?
-(void)getJsonResponse:(NSString *)urlStr success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure
{
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:urlStr];
// Asynchronously API is hit here
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"%@",data);
if (error)
failure(error);
else {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(@"%@",json);
success(json);
}
}];
[dataTask resume]; // Executed First
}
call this:
[self getJsonResponse:@"Enter your url here" success:^(NSDictionary *responseDict) {
NSLog(@"%@",responseDict);
} failure:^(NSError *error) {
// error handling here ...
}];
Related Topics
Swift Override Function in Extension
Set Custom Images to the Uibarbuttonitem But It Doesn't Show Any Image
What Is Build Object File Extension in iOS
Why Is This Code Not Recognising the Nsstring as Being Equal
Datefromstring Returns Nil for Some Values
Async Request Does Not Enter Completion Block
"Too Many Symbol Files" After Successfully Submitting My Apps
Run iPhone as an Ibeacon in the Background
How to Invoke iPhone Maps for Directions with Current Location as Start Address
Is There Any Way of Asking an iOS View Which of Its Children Has First Responder Status
How to Hide 'Back' Button on Navigation Bar on Iphone
How to Present View Controller from Right to Left in iOS Using Swift
How to Limit Uitableview Row Reordering to a Section
iPhone Uitableview. How Do Turn on the Single Letter Alphabetical List Like the Music App