ios: Queue blocks in background and execute when network becomes available - ios

I am developing an app using parse.com API (hosted backend which provides API to save data on their servers). I want to be able to use the app seamlessly online and offline.
For this I would need to use a queue where I can place blocks that require network access. When network does become available, the blocks should be executed serially and when the network goes offline, then the queue processing should be suspended.
I was thinking of using GCD with suspend/resume as the network becomes available/unavailable.
I was wondering if there are any better options? Will this work if the app is put in the background? Case in point here is that a user saves some data when the network is unavailable (which gets queued) and then puts the app in the background. Now when the network becomes available, is it possible to do the saving in the background automagically?

I do exactly what you're aiming for using an NSOperationQueue. First, create a serial queue and suspend it by default:
self.operationQueue = [[[NSOperationQueue alloc] init] autorelease];
self.operationQueue.maxConcurrentOperationCount = 1;
[self.operationQueue setSuspended:YES];
Then, create a Reachability instance and register for the kReachabilityChangedNotification:
[[NSNotificationCenter defaultCenter] addObserver:manager
selector:#selector(handleNetworkChange:)
name:kReachabilityChangedNotification
object:nil];
[self setReachability:[Reachability reachabilityWithHostName:#"your.host.com"]];
[self.reachability startNotifier];
Now, start and stop your queue when the network status changes:
-(void)handleNetworkChange:(NSNotification *)sender {
NetworkStatus remoteHostStatus = [self.reachability currentReachabilityStatus];
if (remoteHostStatus == NotReachable) {
[self.operationQueue setSuspended:YES];
}
else {
[self.operationQueue setSuspended:NO];
}
}
You can queue your blocks with:
[self.operationQueue addOperationWithBlock:^{
// do something requiring network access
}];
Suspending a queue will only prevent operations from starting--it won't suspend an operation in progress. There's always a chance that you could lose network while an operation is executing, so you should account for that in your operation.

Check out -[PFObject saveEventually]. This should do what you're trying to do automatically and has the additional benefit of being resilient against app termination.

Have you looked at using the AFNetworking library? I believe it has hooks into Reachabiltiy and can behave exactly as you want.

I'm a big fan of GCD and Blocks but for this I would build a solution using NSOperationQueue. GCD is in my opinion more for the low level stuff. With NSOperationQueue you have the ability to cancel certain operations. Also you can express dependencies to other operations (if this is needed in you your application).

We had a similar issue on our internal projects, so I wrote a pod called OfflineRequestManager that wraps any network request and allows it to be enqueued regardless of connectivity. If you wrap the Parse request (or whatever) in an object conforming to OfflineRequest, the manager will enqueue it and ensure that it goes out whenever connectivity allows.
The simplest use case would look something like
import OfflineRequestManager
class SimpleRequest: OfflineRequest {
func perform(completion: #escaping (Error?) -> Void) {
doMyParseThing(withCompletion: { response, error in
handleResponse(response)
completion(error)
})
}
}
///////
OfflineRequestManager.defaultManager(queueRequest: SimpleRequest())

Related

Listening for NSNotifications in an NSOperation subclass?

I'm writing an app where I've got a long running server-syncronization task running in the background, and I'd like to use NSOperation and NSOperationQueue for this. I'm leaning this way, since I need to ensure only one synchronisation operation is running at once.
My question arises since my architecture is built around NSNotifications; my synchronisation logic proceeds based on these notifications. From what I can see, NSOperation logic needs to be packed into the main method. So what I'm wondering is if there is any way to have an NSOperation finish when a certain notification is received. I suspect this is not the case, since I haven't stumbled upon any examples of this usage, but I figured I'd ask the gurus in here. Does an NSOperation just finish when the end of the main method is reached?
There is no reason a NSOperation cannot listen for a notification on the main thread, but either the finish logic must be thread safe, or the operation must keep track of its current thread.
I would recommend a different approach. Subclass NSOperation to support a method like -finishWithNotification: Have a queue manager that listens for the notification. It can iterate through its operations finishing any operations which respond to -finishWithNotification:.
- (void)handleFinishNotification:(NSNotification *)notification
{
for (NSOperation *operation in self.notificationQueue) {
if ([operation isKindOfClass:[MYOperation class]]) {
dispatch_async(self.notificationQueue.underlyingQueue), ^{
MYOperation *myOperation = (MYOperation *)operation;
[myOperation finishWithNotification:notification];
});
}
}
}
If I understood you correctly concurrent NSOperation is what you need.
Concurrent NSOperation are suitable for long running background/async tasks.
NSOperation Documentation
See: Subclassing Notes & Operation Dependencies Section
EDIT:(Adding more explanation)
Basically concurrent operations do not finish when main method finishes. Actually what concurrent operation mean is that the control will return to calling code before the actual operation finishes. The typical tasks that are done in start method of concurrent operation are: Mark operation as executing, start the async work(e.g. NSURLConnection async call) or spawn a new thread which will perform bulk of the task. And RETURN.
When the async task finishes mark the operation as finished.

ReactiveCocoa conditional async signals

I have a merge operation that depends on the result of two asynchronous operations. The first is a network operation, the second is a success or failure of location authorization. I don't care about the values of these operations, just that both have completed.
This is what it looks like:
RACSignal *networkCallReturned = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:kNetworkCallReturned object:nil] take:1];
RACSignal *locationPermission = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:kLocationManagerGotLocationPermission object:nil] take:1];
#weakify(self);
[[RACSignal merge:#[ networkCallReturned, locationPermission ]
subscribeCompleted:^{
#strongify(self);
// Do something else here
}];
The problem I am having is that the network call is not made when I do not have reachability. This is not something I can change either. How can I conditionally fire the networkCallReturned signal if I do not have reachability?
Do I have to setup another signal that monitors reachability and then take the first value sent from either networkCallReturned or the reachability signal?
You could monitor reachability, but it's infamously fraught with races and edge cases. It seems like you'd be much better served by catching the errors that from from not being able to complete the network call, or timing out the network call.

Using multithread to save data on iOS

I am developing an iPhone app which keeps some data. I am using archiving method from NSKeyedArchiver class to save the data to disk. I would like to periodically save the data to disk. The problem is, when the data grows bigger, it takes more time and it actually interrupts the user's current actions.
Hence, I want to use multithreading to solve this problem. From my understanding of multithreading, when I want to save the data to disk, I should create a new thread, run the saving task on the new thread, then terminate the thread. I should also make the thread so that it won't immediately terminate when the app terminates, to finish saving data. This way, the user can continue to interact with the interface.
That being said, I am not familiar with the actual code that does these work...what would the above look like in code?
A couple of thoughts.
You want to use a serial dispatch queue or operation queue.
Note, we probably want it to write to persistent storage serially (if you're saving it to the same filename, for example), i.e. not permit another save to be initiated until the prior save is finished. I suspect that's exceedingly unlikely that your infrequent saves could ever trigger a save while the prior one is still in progress, but as a general principle you should not use concurrent queues unless you write code that supports concurrent operation (which we're not doing here). This means that you do not use the GCD global queues.
For example, to create serial dispatch queue using Grand Central Dispatch (GCD) would be:
#property (nonatomic, strong) dispatch_queue_t queue;
Then instantiate this (e.g. in viewDidLoad):
self.queue = dispatch_queue_create("com.domain.app.savequeue", 0);
Then use this queue
dispatch_async(self.queue, ^{
// do your saving here
});
For a review of concurrency technologies, see the Concurrency Programming Guide. Both dispatch queues (GCD) and operation queues are solid choices.
You might want to be careful about synchronization issues. What if your app proceeds to start changing the data while the save is in progress? There are a bunch of options here, but the easiest is to copy the data to some temporary object(s) in the main queue before you dispatch the save task to the background queue:
// copy the model data to some temporary object(s)
dispatch_async(self.queue, ^{
// save the temporary object(s) here
});
Or, instead of creating a copy of the model, you can alternatively (and this is a little more complicated if you're not familiar with GCD) use a variation of the "reader-writer" pattern that Apple discusses in WWDC 2012 video Asynchronous Design Patterns with Blocks, GCD, and XPC. Bottom line, you can queue to not only perform asynchronous write to persistent storage, but also to synchronize your updates to your model using a "barrier" (see Using Barriers in the GCD reference):
self.queue = dispatch_queue_create("com.domain.app.modelupdates", DISPATCH_QUEUE_CONCURRENT);
Then, when you want to save to disk, you can do
dispatch_async(self.queue, ^{
// save model to persistent storage
});
But, whenever you want to update your model, you should use barrier so that the updating of the model will not happen concurrently with any read/save tasks:
dispatch_barrier_async(self.queue, ^{
// update model here
});
And, whenever you read from your model, you would:
dispatch_sync(self.queue, ^{
// read from model here
});
Theoretically, if you're worried about the possibility that you could conceivably do your save operations so frequently that one save could still be in progress when you initiate the next one, you might actually employ two queues, one serial queue for the saving operation (point 1, above), and the concurrent queue outlined here for the synchronization process.
Finally, Putz1103 is correct, that if it's possible that the app can be terminated while a save is in progress, you might want to add the code to allow the write to persistent storage to complete:
dispatch_async(self.queue, ^{
UIBackgroundTaskIdentifier __block taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void) {
// handle timeout gracefully if you can
[[UIApplication sharedApplication] endBackgroundTask:taskId];
taskId = UIBackgroundTaskInvalid;
}];
// save model to persistent storage
// when done, indicate that the task has ended
if (taskId != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:taskId];
taskId = UIBackgroundTaskInvalid;
}
});
Adding multi-threading to an application where data is shared between multiple threads (in this case, the data being created by the user and the data you are saving) is a difficult task to manage.
Instead of creating a thread or trying to save off all the data at once, put data to be saved into an internal "this must be saved off" list, and then work it off one N elements at a time periodically in your main thread.
If you get to the point where the user is leaving the screen or the app, then save off all the work that is left in the queue to the database immediately.
You can create a simple timed event (a few times per second) to do the work, which is a very simple approach.
You can explicit control over how many items you save per update.
You should never have concurrency issues.
You should never have to worry about thread start/stop/termination
issues or mutexes.
To create it:
-(void)viewDidAppear:(BOOL)animated
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(saveData) userInfo:nil repeats:YES];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(saveAllRemainingItems) name:#"APP EXITING KEY" object:nil]
}
Your update function:
-(void)saveData
{
if([itemsToSave count] > 0)
{
....save off N items, remove from the list
}
}
-(void)saveAllRemainingItems
{
while([itemsToSave count] > 0)
{
...save first item.
[itemsToSave removeObjectAtIndex:0];
}
}
When you leave:
-(void)viewWillDisappear:(BOOL)animated
{
[self.timer invalidate];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self saveAllRemainingData];
}
To make sure you handle the "app is closing" situation, in your app delegate:
- (void)applicationWillTerminate:(UIApplication *)application
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"APP EXITING KEY" object:nil];
...OTHER CLEANUP ACTIVITIES
}
You can achive mulithreading in IOS with different ways, like NSThread, Operation Queues and GCD.
GCD is the best approch now a days, it uses block. You can execute a code in different thread like this. You can use this in any method.
void performArchiveData{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Now you are in different thread. You can add your code in here.
});
}

Using NSUrlConnection inside either NSThread or NSOperation

I am developing a static library that needs to do some stuff in the background, without interacting with the main thread. To give you an idea, think of just logging some user events. The library must keep doing this stuff until the user exits the app or sends it to the background (pushes the home button) - in other words it needs to keep doing stuff inside a loop.
The only interaction between the main app thread and the spawned thread is that occasionally the main app thread will put some stuff (an event object) into a queue that the spawned thread can read/consume. Other than that, the spawned thread just keeps going until the app exists or backgrounds.
Part of what the spawned thread needs to do (though not all of it) involves sending data to an HTTP server. I would have thought that it would be easy to subclass NSThread, override its main method, and just make a synchronous call to NSUrlConnection with some sort of timeout on that connection so the thread doesn't hang forever. For example, in Java/Android, we just subclass Thread, override the start() method and call a synchronous HTTP GET method (say from Apache's HttpClient class). This is very easy and works fine. But from what I have seen here and elsewhere, apparently on iOS it is much more complicated than this and I'm more than a bit confused as to what the best approach is that actually works.
So should I subclass NSThread and somehow use NSUrlConnection? It seems the asynchronous NSUrlConnection does not work inside NSThread because delegate methods don't get called but what about the synchronous method? Do I somehow need to use and configure the RunLoop and set up an autorelease pool? Or should I use an NSOperation? It seems to me that what I am trying to do is pretty common - does anyone have a working example of how to do this properly?
As I understand it, to use NSURLConnection asynchronously you need a runloop. Even if you use an NSOperation you still need a runloop.
All the examples I have seen use the Main Thread to start NSURLConnection which has a runloop. The examples using NSOperation are set up so the operation is Concurrent which tells NSOperationQueue not to provide it's own thread, they then make sure that NSURLConnection is started on the main thread, for example via a call to performSelectorOnMainThread:
Here is an example:
Pulse Engineering Blog: Concurrent Downloads using NSOperationQueues
You can also search the Apple documentation for QRunLoopOperation in the LinkedImageFetcher sample which is an example class showing some ins and outs of this kind of thing.
(Although I'm not sure I actually saw any code that example showing how to run your own runloop, again this example relies on the main thread.)
I've used the grand central dispatch (GCD) methods to achieve this. Here is an example that worked for me in a simple test app (I'm not sure if it applies in a static library, but may be worth a look). I'm using ARC.
In the example, I am kicking off some background work from my viewDidLoad method, but you can kick it off from anywhere. The key is that "dispatch_async(dispatch_get_global_queue…" runs the block in a background thread. See this answer for a good explanation of that method: https://stackoverflow.com/a/12693409/215821
Here is my viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL),
^(void) {
[self doStuffInBackground];
});
}
The doStuffInBackground method is running in the background at this point, so you can just use NSURLConnection synchronously. In my example here, the method loops making network calls until presumably some other code sets backgroundStuffShouldRun = false. A network call is made with a 10 second timeout. After the call, I'm updating a UI label just to show progress. Note that the UI update is performed with "dispatch_async(dispatch_get_main_queue()…". This runs the UI update on the UI thread, as required.
One potential issue with this background work: there isn't a way to cancel the http request itself. But, with a 10 second timeout, you'd be waiting a max of 10 seconds for the thread to abort itself after an outsider (likely some event in your UI) sets backgroundStuffShouldRun = false.
- (void)doStuffInBackground
{
while (backgroundStuffShouldRun) {
// prepare for network call...
NSURL* url = [[NSURL alloc] initWithString:#"http://maps.google.com/maps/geo"];
// set a 10 second timeout on the request
NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLCacheStorageAllowed timeoutInterval:10];
NSError* error = nil;
NSURLResponse *response = nil;
// make the request
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// were we asked to stop the background processing?
if (!backgroundStuffShouldRun) {
return;
}
// process response...
NSString* status = #"Success";
if (error) {
if (error.code == NSURLErrorTimedOut) {
// handle timeout...
status = #"Timed out";
}
else {
// handle other errors...
status = #"Other error";
}
}
else {
// success, handle the response body
NSString *dataAsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", dataAsString);
}
// update the UI with our status
dispatch_async(dispatch_get_main_queue(), ^{
[statusLabel setText:[NSString stringWithFormat:#"completed network call %d, status = %#", callCount, status]];
});
callCount++;
sleep(1); // 1 second breather. not necessary, but good idea for testing
}
}

Serializing NSURLConnection Requests (iOS) - Use Synchronous Request?

I'm looping through a list of dates and making a request to a web server for each date in the list.
I would like each date to be processed completely before the subsequent request is sent to the server. To do this, I have set up a serial dispatch queue using GCD. Each time through the date loop, a block is added to the queue.
The problem I am having is that my NSURLConnection is set up using the standard asynchronous call. This results in requests not blocking any subsequent requests. They are thus overrunning each other.
My question: Is this a case where it would make sense for me to use the synchronous NSURLConnection (within the dispatch queue) or is there some other way to make it work using the standard asynchronous call?
There are number of ways to do this. Whatever method you choose, starting the connection needs to be tied to completion of your processing task.
In each block you add to your serial queue, use a synchronous request. This is probably the quickest solution given your current implementation as long as you're ok with the limited error handling of a synchronous request.
Don't use a serial queue. Start the first asynchronous connection and process the response. When processing is complete start the next asynchronous connection. Rinse and repeat.
I think that using the synchronous NSURLConnection API is a fine idea. You have a few other options. One would be to write a wrapper object around NSURLConnection that used the asynchronous NSURLConnection APIs, so you get the nice information that the asynchronous API callbacks provide, including download progress, you can easily continue to update your UI while the request is happening, but which presents its own synchronous method for doing whatever it is you need to do. Essentially, something like:
#implementation MyURLConnectionWrapper
- (BOOL)sendRequestWithError:(NSError **)error
{
error = error ? error : &(NSError *){ nil };
self.finishedLoading = NO;
self.connectionError = nil;
self.urlConnection = [][NSURLConnection alloc] init...]
while (!self.finishedLoading)
{
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]];
}
if (self.connectionError != nil)
{
*error = self.connectionError;
return NO;
}
return YES;
}
#end
(This is all typed off the top of my head, and is heavily abbreviated, but should give you the basic idea.)
You could also do something like fire off each request in the completion delegate method for the previous one, forgoing the use of a serial dispatch queue altogether:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
{
[self sendNextRequest];
}
Either way, you need to think about how to handle connection errors appropriately. I've used both approaches in different places with good success.

Resources