NSUrlSessionDownloadTask - didCompleteWithError when go in background - ios

When I force my device to go in sleep mode by pressing the power button, my background task stops by calling the delegate method didCompleteWithError with the error :
The operation couldn’t be completed. Operation not permitted
How can I configure my NSURLSession to continue the download even in sleep mode?
Is it even possible? If not, what options do I have? I need to download a file of 300Mb, so with a low connection the application will go in sleep mode before the end of the download.
Here is the creation of my session :
static NSURLSession *backgroundSession;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
backgroundSession = [NSURLSession sessionWithConfiguration:
[NSURLSessionConfiguration backgroundSessionConfiguration:
#"com.myapp.mytask"] delegate:self.
myDelegate delegateQueue:self.myQueue];
});
NSURLSessionDownloadTask *task = [backgroundSession downloadTaskWithRequest:
self.urlRequest];
[task resume];

The problem is that the Data Protection Capability is activated. With that enabled all files are stored with NSFileProtectionComplete by default, even the temporary file used to download by the NSURLSession:
The default level of protection is complete protection, in which files
are encrypted and inaccessible when the device is locked. You can
programmatically set the level of protection for files created by your
app, as described in “Protecting Data Using On-Disk Encryption” in iOS
App Programming Guide.
With NSFileProtectionComplete activated on that file you cannot access it when the device is locked.
I'm not sure if the temporary download file can be configured to not use data protection, it seems like that is not exposed by NSURLSession.
Source: App Distribution Guide

Related

URLSession background upload task keeps resetting

I am experiencing some weird behaviour when using an uploadTask for a URLSessionConfiguration.background.
My custom delegate is implementing all of the delegate methods that belong to URLSessionDelegate, URLSessionTaskDelegate, and URLSessionDataDelegate. All of them has a print statement indicating that the method has been called.
I am trying to upload five images to a server, each of them has their own session with an id matching the image id.
The problem is that when uploading using a very slow connection "edge", the upload progress will reset before reaching 100% This happens whenever didFinishCollectingMetrics is called as you can see here: Data
This does not happen all the time when using a slow connection but only some of the time.
Anyone got any ideas as to what is happening here?
Edge networking is notoriously unreliable, and frequent upload failures are not atypical. The way you solve this is by replacing whole-file-based uploads with some form of chunked uploads so that you can continue the upload where you left off, but that requires server support.
Increase time-out of NSURLSession for request and resource:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
[configuration setTimeoutIntervalForRequest:120];
[configuration setTimeoutIntervalForResource:240];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
Use session to upload your image

NSUrlSession: is it possible to upload files in the background?

Using NSUrlSession with a background configuration let's me download files even if the app gets terminated by iOS. Being curious, I tried to add an upload task and noticed that it won't continue, even not if the app is only suspended.
Apple talks about "Downloading in the background" but they don't explicitly state that uploading would not be possible.
Can somebody confirm that uploads and background session configuration don't work together?
They DO work together.
What you need to do is the following:
Have a NSURLSessionConfiguration with background configuration
NSURLSessionConfiguration *conf = [NSURLSessionConfiguration backgroundSessionConfiguration:#"backgroundSession"];
Setup a NSURLSession (#property NSURLSession *urlSession)
Obtain the path to the file (fullPath)
Create the NSURLRequest (request)
Create a NSURLSessionTask
NSURLSessionTask*task = [self.urlSession uploadTaskWithRequest:request fromFile:fullPath];
[task resume];
And the task will run in background. You can get the status from NSURLSession delegate methods.
Cheers

NSURLSession background session callbacks on app termination

I am using below code to create background session for huge uploads,
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:delegate delegateQueue:nil];
and save the identifier to re-associate with the session later. But once app goes to background and terminated by OS then, upon relaunch I am not receiving any callbacks from that session even if I use same identifier to create session. It always creates new session that has no ongoing upload task.
The previous task can't finish this early as I have GBs to upload.
Am I missing anything here ? any additional setting etc.
Save the delegate as well in order to receive the call backs! The instance of the delegate is not retained after the App kill.
Make your delegate comply to NSCoding protocol and do the archiving and unarchiving!
Cheers:)

NSURLSession download task grows memory in debug with default session config

I've got an app that downloads files using NSURLSession on iOS 7 and later. I start the task like this:
//property declared: #property (retain) NSURLSession *session;
NSString *addr = #"http://www.example.com/path/to/nontrivial/file.dat";
NSURL *url = [NSURL URLWithString:addr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
[[self.session downloadTaskWithRequest:request] resume];
In the completion handler (URLSession:downloadTask:didFinishDownloadingToURL:), I move the file to my application data folder and call invalidateAndCancel on the session. In the progress handler (didWriteData:totalBytesWritten:totalBytesExpectedToWrite:), I simply log the progress.
As this runs in debug, memory usage increases quickly, to the tune of a few times the amount of data that has been downloaded. If I do it in a background task, though, memory usage is constant.
Is it bad to use the default session configuration with a download task? Is there some kind of cache property that I should be setting? What's going on here?
Update: As far as I can tell, this only happens when the debugger is attached. When I launch it in the simulator without the debugger, I don't see any memory usage increase in Activity Monitor.
Update 2: This appears to have been fixed in iOS 8.3. Yay!
This same behavior was happening to me. It was manifesting itself via hard crashes when memory became depleted. For anyone else who's curious if they're seeing this same problem, follow these steps...
Open activity monitor alongside the iOS simulator, go to the memory tab and enter your app's name in the search results.
Run your app in the simulator via xcode. Wait until you see the process listed in activity monitor, and then queue up a download or two. Activity monitor will show the memory usage for your app ballooning out of control.
Stop execution of your app in xcode. Restart the app in the simulator without the aid of xcode. Wait for your app to appear in activity monitor again, and queue up a few more downloads. When you do it this time, the memory usage for your app should basically remain the same.
This was a really great observation on your part. I never would have thought to investigate this path. Thanks!

How to work with large file uploads in ios?

My app requires upload of video files from users phone which will then be processed on server.
THe problem is the size of the file can go to 200 MB plus and the user won't keep the application open to wait for the file to upload. Since apple doesn't allow apps to run in background for more than a limited time. How can I ensure that my files are uploaded. I am using afnetworking to set up an upload task as given by ios 7 library.
Please if anyone can point me in the right direction or have any solution it would be greatly appreciated. I have banged my head on this for too long. Thanks.
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
[manager setTaskDidSendBodyDataBlock:^(NSURLSession *session,NSURLSessionTask *task ,int64_t bytesSent, int64_t totalBytesSent,int64_t totalBytesExpectedToSend){
CGFloat progress = ((CGFloat)totalBytesSent / (CGFloat)sensize);
NSLog(#"Uploading files %lld -- > %lld",totalBytesSent,totalBytesExpectedToSend);
[self.delegate showingProgress:progress forIndex:ind];
}];
dataTask = [manager uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
}
}];
My request is a normal multipart form request.
Use:
NSURLSessionConfiguration:backgroundSessionConfiguration:
instead of
NSURLSessionConfiguration:defaultSessionConfiguration
From the NSURLSessionConfiguration:backgroundSessionConfiguration: documentation:
Upload and download tasks in background sessions are performed by an external daemon instead of by the app itself. As a result, the transfers continue in the background even if the app is suspended, exits, or crashes.
So in your case, change:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
to:
NSString *appID = [[NSBundle mainBundle] bundleIdentifier];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:appID];
Implementing application:handleEventsForBackgroundURLSession:completionHandler: on your app delegate will allow your app to be woken up (ie. un-suspended or un-terminated in background mode) when an upload has completed (whether it has completed successfully or not).
Don't get confused with Background Fetching. You don't need it. Background Fetching simply wakes you app up to periodically give your app the chance to fetch small amounts of content regularly. It may however, be useful for restarting failed "background-mode" uploads periodically.
You should use a background session configuration instead if a default session configuration. This ensures that your data transfer will continue in the background once the user has exited your app.
Of course, this is correct as long as the user has background fetching enabled for your app in the Settings app of the device.
Be sure to enable the Background fetch capability on your project settings:
(source: migueldiazrubio.com)
(source: migueldiazrubio.com)
Then implement the application:handleEventsForBackgroundURLSession:completionHandler: in your App Delegate to be informed when the data transfer ends and do whatever you need to do (UI update…) inside your app with the received/sent file. Don't forget to call the completionHandler to inform the system that you have ended your tasks. iOS then will take a screenshot of the active screen of your app and update the one in the iOS 7 multitasking screen.

Resources