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?

Devang
  • 1,531
  • 3
  • 22
  • 38

5 Answers5

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.

Mr Rogers
  • 6,091
  • 2
  • 32
  • 34
Christopher Pickslay
  • 17,523
  • 6
  • 79
  • 92
  • 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" – Carlos Ricardo Jul 09 '12 at 14:10
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.

Thomas Bouldin
  • 3,707
  • 19
  • 21
1

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

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • 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). – Devang May 25 '12 at 06:13
  • I see what you mean... the Reachability answer would have been my next thought, btu I see someone already fleshed out that idea. – Kendall Helmstetter Gelner May 25 '12 at 21:50
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).

Tobias Kräntzer
  • 1,694
  • 1
  • 13
  • 23
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())