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
Related
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 have a method that accepts a block and a completion block. The first block should run in the background, while the completion block should run in whatever queue the method was called.
For the latter I always used dispatch_get_current_queue(), but it seems like it's deprecated in iOS 6 or higher. What should I use instead?
The pattern of "run on whatever queue the caller was on" is appealing, but ultimately not a great idea. That queue could be a low priority queue, the main queue, or some other queue with odd properties.
My favorite approach to this is to say "the completion block runs on an implementation defined queue with these properties: x, y, z", and let the block dispatch to a particular queue if the caller wants more control than that. A typical set of properties to specify would be something like "serial, non-reentrant, and async with respect to any other application-visible queue".
** EDIT **
Catfish_Man put an example in the comments below, I'm just adding it to his answer.
- (void) aMethodWithCompletionBlock:(dispatch_block_t)completionHandler
{
dispatch_async(self.workQueue, ^{
[self doSomeWork];
dispatch_async(self.callbackQueue, completionHandler);
}
}
This is fundamentally the wrong approach for the API you are describing to take. If an API accepts a block and a completion block to run, the following facts need to be true:
The "block to run" should be run on an internal queue, e.g. a queue which is private to the API and hence entirely under that API's control. The only exception to this is if the API specifically declares that the block will be run on the main queue or one of the global concurrent queues.
The completion block should always be expressed as a tuple (queue, block) unless the same assumptions as for #1 hold true, e.g. the completion block will be run on a known global queue. The completion block should furthermore be dispatched async on the passed-in queue.
These are not just stylistic points, they're entirely necessary if your API is to be safe from deadlocks or other edge-case behavior that WILL otherwise hang you from the nearest tree someday. :-)
The other answers are great, but for the me the answer is structural. I have a method like this that's on a Singleton:
- (void) dispatchOnHighPriorityNonMainQueue:(simplest_block)block forceAsync:(BOOL)forceAsync {
if (forceAsync || [NSThread isMainThread])
dispatch_async_on_high_priority_queue(block);
else
block();
}
which has two dependencies, which are:
static void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}
and
typedef void (^simplest_block)(void); // also could use dispatch_block_t
That way I centralize my calls to dispatch on the other thread.
You should be careful about your use of dispatch_get_current_queue in the first place. From the header file:
Recommended for debugging and logging purposes only:
The code
must not make any assumptions about the queue returned, unless it
is one of the global queues or a queue the code has itself created.
The code must not assume that synchronous execution onto a queue is
safe from deadlock if that queue is not the one returned by
dispatch_get_current_queue().
You could do either one of two things:
Keep a reference to the queue you originally posted on (if you created it via dispatch_queue_create), and use that from then on.
Use system defined queues via dispatch_get_global_queue, and keep a track of which one you're using.
Effectively whilst previously relying on the system to keep track of the queue you are on, you are going to have to do it yourself.
Apple had deprecated dispatch_get_current_queue(), but left a hole in another place, so we still able to get current dispatch queue:
if let currentDispatch = OperationQueue.current?.underlyingQueue {
print(currentDispatch)
// Do stuff
}
This works for main queue at least.
Note, that underlyingQueue property is available since iOS 8.
If you need to perform the completion block in the original queue, you also may use OperationQueue directly, I mean without GCD.
For those who still need in queue comparing, you could compare queues by their label or specifies.
Check this https://stackoverflow.com/a/23220741/1531141
This is a me too answer. So I will talk about our use case.
We have a services layer and the UI layer (among other layers). The services layer runs tasks in the background. (Data manipulation tasks, CoreData tasks, Network calls etc). The service layer has a couple operation queues to satisfy the needs of the UI layer.
The UI layer relies on the services layer to do its work and then run a success completion block. This block can have UIKit code in it. A simple use case is to get all messages from the server and reload the collection view.
Here we guarantee that the blocks that are passed into the services layer are dispatched on the queue on which the service was invoked on. Since dispatch_get_current_queue is a deprecated method, we use the NSOperationQueue.currentQueue to get the caller's current queue. Important note on this property.
Calling this method from outside the context of a running operation
typically results in nil being returned.
Since we always invoke our services on a known queue (Our custom queues and Main queue) this works well for us. We do have cases where serviceA can call serviceB which can call serviceC. Since we control where the first service call is being made from, we know the rest of the services will follow the same rules.
So NSOperationQueue.currentQueue will always return one of our Queues or the MainQueue.
I am using NSURLConnection to make some http requests to my webservice, which returns a large text file. Obviously this blocks the main thread and adds lag, so I want to process this in the background. BTW, I realize that there are third party frameworks out there handle this particular job/situation, but I'm coding this myself because I need to learn about multithreading on iOS.
I can either detach an NSThread or pass the NSURLConnection code to GCD in a block. Both ways work fine initially (to make the http connection and send the request). The problem is how to get the data back from the server. Let me explain.... when my webservice sends the data back, my app is informed via callbacks from the NSURLConnectionDelegate protocol. But by the time this happens, either my detached thread has exited it's target method, or the block has already been processed by GCD an it's off the queue (depending on which of the approaches I have used). Basically, the callback goes unnoticed by my app (unless of course I'm using dispatch_main_queue).
What is the "correct" objective-c way to solve this with each case?
Thanks for any direction
For networking code I would use the asynchronous method on NSULRConnection and process the data I get back on a background queue. Only the data that modifies the UI and needs to be on the main queue would be dispatched (using GCD) to the main queue.
NSOperationQueue *yourQueue = [[NSOperationQueue alloc] init];
[NSULRConnection sendAsynchronousRequest:yourRequest
queue:yourQueue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
// The code inside here gets processed once the url request completes ...
// It happens in the background on "yourQueue".
[self doSomeExpensiveDataProcessingWithData:data];
dispatch_sync(dispatch_get_main_queue(), ^{
// Update UI on the main thread and wait to continue processing the data
[self updateUI];
});
[self doSomeMoreDataProcessing];
dispatch_sync(dispatch_get_main_queue(), ^{
// Update UI on the main thread and wait to continue processing the data
[self finalUpdateOfUI];
});
}];
Outside the example of networking code I generally like asynchronous callbacks as a design pattern. It becomes easy to test different callbacks in isolation and it divides the different callbacks (like error handling and data processing) into different methods which gives more focused code inside those methods.
GCD is very good for quickly doing a few lines of code on another thread or dispatching some work asynchronously.
NSThread is rarely used anymore.
Sounds like a NSRunLoop problem ... Read this for example ...
http://www.sortedbits.com/nsurlconnection-in-its-own-thread/
http://www.cocoaintheshell.com/2011/04/nsurlconnection-synchronous-asynchronous/
... Google for more ...
... why do you want to do it in this way? It's enough to use NSURLConnection asynchronously as it is (on main thread) and then dispatch your heavy processing of these data when they're received. In other words, NSURLConnection delegate methods will be called on the main thread, but then dispatch data processing only to not block main thread.
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.
I am creating a set of classes which interface with a web service. At the core of this, the data is retrieved from the service using an asynchronous NSUrlConnection. In my mind, it is important that it is asynchronous, as a client of these web service interfaces has to have the ability to cancel a request that is in progress (i.e. cancel an NSUrlConnection).
The web service calls return JSON data, potentially lots of it, and this is parsed and the classes I am creating will create proper data structures out of them. Depending on which web service method is called, the request can end up being expensive - too expensive to run on the main thread, so I would like to either add the option of running the service requests asynchronously, or not giving the option, and forcing asynchronous calls.
Async calls are all well and good, but I am having problems starting an NSUrlConnection asynchronously on a runloop that isn't the main one. The problem I'm describing seems to be fairly well documented: I am led to believe the delegate of the NSUrlConnection is not called because the runloop that launches the connection has terminated, and therefore the calls back to the delegate cannot be scheduled on its runloop.
What is the best way to go about solving this issue?
I have tried using:
while (!self.isRequestComplete && !self.isRequestCancelled)
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
It seems to work ok from the basic trial I have done, except if the runloop that this is being executed on is actually the main runloop, for which I have had a few crashes...
Would an option be to offer asynchronous calls to clients, and then use the above method if the option is utilised? Is there a better way of achieving what I am trying to do?
What I am aiming to achieve is for a package of classes that allow interfacing with my specific web service, where the clients of my code do not need to worry about whether their own delegates (which my classes hold references to) will be called on different threads. I want them to be called on the exact same runloop that they called my code on - basically, exactly how NSUrlConnection operates!
Thanks in advance!
Nick
I think you may have "gone up the wrong creek" so to speak. Generally speaking you don't need to worry about run loops unless you are doing something rather odd. It sounds like you need to do some reading on multi-threading, particularly Grand Central Dispatch.