I'm using the NSURLSession background download service.
If my app is suspended and in background and a NSURLSessionDownloadTask has finisehd the NSURLSessionDownloadDelegate method
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
gets called as documented.
I observed that every time the delegate method is called the [UIApplication sharedApplication].backgroundTimeRemaining) decreases from 30 seconds at the start to 0 after some downloads. If it reaches 0 the app crashes with "has active assertions beyond permitted time:".
This means the total time I have to handle completed background downloads (unzip, move) is 30 seconds in total. This may work for a couple of files but not if the download consists of a lot or big zip files.
This time interval is not mentioned in any Apple documentation. Is this a limitation of the NSURLSEssion framework or did I implement it wrong?
thanks Christian
You can use -[UIApplication beginBackgroundTaskWithExpirationHandler:] and -[UIApplication endBackgroundTask:] to let your app run long running background tasks. This will likely give your app much more time in the background before it is terminated.
Another possibility to consider is chaining your download requests so that when one completes and your URLSession delegate is called you process the file and issue the next download request. That way, you never have more than one file to process at a time (presuming it doesn't take too long to process a single file).
Related
I am downloading multiple files using NSUrlSession in my iOS app. I want to download only one file at a time. But it is downloading multiple files at 1 time. Please suggest a way to download files one at a time.
I tried this property :
sessionConfiguration.HTTPMaximumConnectionsPerHost = 1
But it also download multiple files if they are hosted on different servers.Please suggest a way to do this
You have 2 options:
sync: Daisy chain your requests from the completionHandler, triggering a new NSURLSession resume upon the completion or failure of the previous one.
async: Each operation must complete before the next one can start, and waits until completion signals using a semaphore. The thread performing the scheduling of each NSURLSession resume will wait for that operation to complete. See https://stackoverflow.com/a/21205992/218152
sync/async clarification
Neither of these options are suggesting to run your operations on the main thread: sync/async is merely referring to the background thread responsible for the scheduling.
I have an iPad app that runs a regular server sync process - it runs every 10 seconds or so. The sync process downloads records that are inserted into a CoreData SQL-based store. On occasion the number of records being handled can run into the hundreds or thousands.
The current sync process is based upon an Asynchronous NSURLConnection triggered by the main thread. Once all NSData has been gathered by the async call then the main thread fires an NSOperation in the background to parse the NSData and insert it into the db.
So, the NSURLConnection is running asynchronously, and the db insert is running in a background NSOperation. However, the orchestration of the NSURLConnection and the NSOperation is done in the main thread. Given that there are large quantities of data being downloaded then I'm thinking that even this small amount of orchestration on the main thread may be impacting my UI responsiveness.
So, I'm thinking of refactoring the code into a single background NSOperation and making the NSURLConnection into a synchronous call. A single NSOperation would then synchronously download the NSData and manage the db inserts.
Before I embark on quite a major refactoring I would be interested in people's views on whether this is a good decision.
With the current mechanism I notice some occasional hesitation in the UI. By placing the entire mechanism in a background NSOperation I am hoping that the UI will remain responsive.
Any words of wisdom would be very much appreciated.
Thanks.
I wouldn't recommend switching to a synchronous connection. Asynchronous connection uses less memory and gives you more control over the download process. To avoid UI freeze, I would try to force background processing only inside NSURLConnection delegate methods, like so:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self.mutableData appendData:data];
});
}
EDIT:
As #jrturton stated in comments, you can specify the queue for NSURLConnection delegate methods. Use setDelegateQueue: method (iOS 5.0+).
I am using (and am required to use) a third-party framework to which I do not have source. The third-party framework handles creating an authenticated client/server connection and hands back a pair of open NSStreams. The challenge that I have is the NSStreams are scheduled on the main thread (creating situations where the UI may become unresponsive - which I would like to avoid).
At the point that the streams are handed off from the third party framework, no network traffic is in progress. So, I am wondering if I could just unschedule and reschedule the NSStreams.
Does anyone know if it is possible to unschedule an open NSStream and reschedule it on a different run loop on a different thread? Will that cause problems? Are there any code examples out there?
Thanks in advance!
Aaron
If I understand your application correctly, it means that your application receives references to a particular stream, and you are in charge of reading everything on the steams. Reading these streams should be something that you force into the background from your application via a NSThread, NSOperation or other threading mechanism.
Example:
In whatever file your tieing in this NSInputStream:
#property (strong, nonatomic) NSInvocationOperation *parseOp;
(id)startInputRead:(NSInputStream *)input {
if([input hasBytesAvailable]) {
self.parseOp = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(readAndStoreInput:) object:inputStream];
}
Where your reader is something like:
(void)readAndStoreInput:(NSInputSteam*) input{
//process your input steam into your system in the background
}
This is a short example of just how you would do this for the input side. You could also queue up work for the output steam in a similar fashion. This should make everything run concurrently and your app stay responsive.
What is the right way to check for data in remote database through http requests in objective c iOS. I am thinking of an nstimer that is called every 5 minutes. The nstimer will trigger a function with a thread in it. Is this the right way? Is this going to work when the app enters the background?
Any help appreciated.
The thread (as like all execution in your program) will pause when entering the background - and if it was waiting on a network response, that response will fail after the app returns to the foreground.
Moreover, you need to explicitly tell iOS when you are beginning a task that you would like to continue in the background (with beginBackgroundTaskWithExpirationHandler: on your UIApplication singleton) and when you have finished that task (with endBackgroundTask:). However, that is only up to a maximum of ten minutes, so I daresay you won't be able to, say, continue your NSTimers in the background. But yes, the method you have described is fine for when the application is in the foreground.
I need to implement posting some data to a web server in the background. Just to clarify, by "in the background", I don't mean the normal way of showing a spinning icon and posting data to a web service using something like an AsyncTask or ASIHTTPRequest's [request startAsynchronous] method. I need to maintain a queue of data that a Thread can asychronously start processing and posting to a Web service while the user is working in the application.
I'm looking for some help on designing a queue like that, especially in some edge cases like User receiving a call, logging out of the application while the the post is happening, user leaving the application to goto a different one while a post is happening and the like. How would you handle these cases? Is there any source code you can recommend that does this?
Thanks,
Teja.
I've started using NSOperationQueue in my own work lately, for controlling background network requests. NSOperation deals with most of the boilerplate code necessary for asynchronously running tasks (such as network operations) on threads in the background (or foreground, if necessary for UI updates).
It also allows dependencies across queues; for example, I use two queues in my application:
The first schedules image downloads, at a max concurrency of 2 at a time, in the background. Each image download has a corresponding completion handler (as an NSBlockOperation) that is dependent on the image download completing. These operations sit on the [NSOperationQueue mainQueue], which operates on the main thread, allowing them to update UI (specifically, the corresponding UIImageView).
Note that NSOperation and NSOperationQueue are not for network requests only, but any operation that can be divided into atomic tasks and scheduled concurrently.
Here are Apple's intro docs on the topic.
Having implemented something similar myself, I would recommend using a service and not a thread to do network calls. That way even if your activity gets killed you're sure your network calls will be executed.
Then to implement the queue i suggest you take a look into IntentService (http://developer.android.com/reference/android/app/IntentService.html)
from the docs:
IntentService is a base class for Services that handle asynchronous
requests (expressed as Intents) on demand. Clients send requests
through startService(Intent) calls; the service is started as needed,
handles each Intent in turn using a worker thread, and stops itself
when it runs out of work.
This "work queue processor" pattern is commonly used to offload tasks
from an application's main thread. The IntentService class exists to
simplify this pattern and take care of the mechanics. To use it,
extend IntentService and implement onHandleIntent(Intent).
IntentService will receive the Intents, launch a worker thread, and
stop the service as appropriate.
All requests are handled on a single worker thread -- they may take as
long as necessary (and will not block the application's main loop),
but only one request will be processed at a time.
If your application is simple enough you can use sendBroadCast() to share info and notifications between your activity and the IntentService
Create a singleton that encapsulate a thread :
In the initialisation of your object :
[NSThread detachNewThreadSelector:#selector(mainPosterThread) toTarget:self withObject:nil];
- (void)mainDownloaderThread
{
if( [NSThread respondsToSelector:#selector(setThreadPriority:)] )
{
[NSThread setThreadPriority:0.1];
}
NSString *urlToDownload = nil;
while(shouldRun)
{
// Take next data to post in a queue (lastObject of a NSArray for example)
if( nextDataToPost )
{
// Post
}
else
{
// Sleep for some time.
}
}
}
You can also have methods for stopping / starting the thread while the app go background / foreground on a multitask supported device. If no multitasking supported, save the post data in the queue at stop time (if not too long) and restore them at start. The biggest chalenge is to manage be able to cancel the current upload while app will ends.
This is an issue which I've been perfecting in every new application I write. Basically I wanted network functionality which is asynchronous and which was written by me using native functionality. I'd be happy to show you some of this code if you're interested.
First of all, I suggest that you make all network calls on the main thread asynchronously rather than synchronously, using a delegate. This way serialization/synchronization/concurrency is not an issue. And since classes are network delegates, I'd just set up one class to where a new connection has a new delegate instance.
[[NSURLConnection alloc] initWithRequest:request delegate:del] autorelease];
e.g.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data