ios: Queue blocks in background and execute when network becomes available
Asked Answered
M

5

14

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?

Malone answered 24/5, 2012 at 8:15 Comment(0)
K
16

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.

Kinsman answered 25/5, 2012 at 15:46 Comment(1)
I'm trying an approach like this but I'm having troubles with "out-of-scope dealloc'd" blocks when connectivity returns.. Did you have any troubles invoking your blocks a little bit later? Seems like: "could not restore current frame"Grosvenor
I
2

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.

Interrelated answered 30/5, 2012 at 1:1 Comment(0)
C
1

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

Christly answered 24/5, 2012 at 17:26 Comment(2)
Thanks. I had looked at the AFNetworking lib but it seems to be more for NSURL requests and not any arbitrary blocks which I want to start executing only when internet is available (which might use the net connection only for some part of block execution).Malone
I see what you mean... the Reachability answer would have been my next thought, btu I see someone already fleshed out that idea.Christly
B
0

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).

Blackboard answered 25/5, 2012 at 8:27 Comment(0)
T
0

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())
Ti answered 27/3, 2018 at 18:54 Comment(2)
Add the appropriate code to your answer instead of linking it pleaseHydroid
Sorry about that, added a simple use casePhosphorate

© 2022 - 2024 — McMap. All rights reserved.