4

I have an method (let's call it method1) that makes an asynchronous request to a server (POST) to create a CommunicationLink object with a given ID. Before sending the request, the method checks if the CommunicationLink already exists on the server.

I have a second method (let's call it method2) that fetches a list of "Topic" objects associated with a given CommunicationLink ID. Just like method1, method2 checks if the CommunicationLink already exists on the server, and if not, it creates it (again, by sending a asynchronous request to the server).

The problem I have is that when I start my application, method1 and method2 are called one after the other, so I end up with something like this:

method1 checks if CommunicationLink exists ---> NO
  method1 sends a request to create CommunicationLink (async call)

method2 checks if CommunicationLink exist ---> NO because the previous call is not done yet
  method2 sends a request to create CommunicationLink

  method1 done
  method2 done

So on the server, I end up with 2 CommunicationLinks for the same ID (I don't have control of what's happening on the server side)

What would be the best way to "pause" method2 if method1 already sent a request to the server? I can set a boolean to true before the call in method1 and set it back to false once the request is done and have a while (!myValue); in method2, but I have a feeling that there's a better way to do this.

Edit

This code if part of a static library I'm working on to deal with push notifications. I"m also working on a test app that uses the library. method1 is called in - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken; and method2 is called in my root view's viewDidLoad

Method1

- (void)sendProviderDeviceToken:(NSData *)deviceToken contextUUID:(NSString *)contextUUID sourceUUID:(NSString *)sourceUUID topicUUID:(NSString *)topicUUID language:(NSString *)language;

Method that creates CommLink

- (void)createCommunicationLinkWithSuccessHandler:(void (^)(NSNumber *))successHandler errorHandler:(void (^)(NSError *))errorHandler;

Method2

- (void)retrieveSubscriptionForContext:(NSString *)contextUUID notificationTopic:(NSString *)notificationTopic completion:(void (^)(NSDictionary *))completion;

I would like to be able to handle this case directly in my library, and not have to modify my test app. method1 and method2 are called on the same thread.

Thanks

Jean-Francois Gagnon
  • 3,141
  • 4
  • 20
  • 27

3 Answers3

2

The main point here is how your are dealing with:

"method1 sends a request to create CommunicationLink (async call)"

you should provide there some kind of callback. The callback would then execute/trigger the second step.

If you are using NSURLResponse/Connection or other networking framework (AFNetworking, etc.) you will have such kind of callback mechanism available to you (this would take the form of some delegate method, or some completion block you pass into the network request). Please, provide more detail for more info.

Another approach altogether would be for you to manage the workflow method1/method2 on a separate thread, so you can wait without blocking the UI, but this would be far more complex.

EDIT:

You can use your

(^)(NSNumber *))successHandler

argument to method1 to chain the execution of method2.

Or, what you could do is following:

  1. when running method1, set a flag saying that your app is waiting a response;

  2. when the response is received (i.e., in successHandler or errorHandler), reset the flag;

  3. in viewDidLoad, before running method2, check for the status of the flag and execute method2 only if you are not waiting for a response.

You would need to create some kind of status information accessible both from the delegate and your view controller.

Hope it helps.

EDIT 2:

To keep into account the fact that you could load your view controller before the first network request has finished, I would suggest this:

  1. factor out of viewDidLoad the method2 network request and its further handling (so you can call them from within viewDidLoad and outside of it);

  2. register the method defined above as observer for a NSNotificationCenter notification;

  3. in viewDidLoad, do not call the method defined at step 1, if the method1 network request is still ongoing;

  4. when you call method1 from you app delegate, make the successHandler post a notification, so that any interested party knows that the first connection has been established;

    4b. thanks to the notification observer registered at step 2, you view controller will be able to update its state (and eventually call method2).

This will requires some changes in your app; i.e., your view controller will need to support an "offline" mode, where it cannot execute method2 and show some information to the user depending on the overall state: no network, waiting for response, no response, etc.

sergio
  • 68,819
  • 11
  • 102
  • 123
  • I edited my post with more details and the signatures of method1 and method2 – Jean-Francois Gagnon Feb 27 '13 at 15:18
  • Thanks. What you are suggestion is exactly what I wanted to do. The only thing I have a problem with is this: "execute method2 only if you are not waiting for a response." If I am waiting for a response, I need a way to tell method2 to "wait" until I get a response, then execute its instructions. And this would have to be done in my library, not in my app – Jean-Francois Gagnon Feb 27 '13 at 15:29
  • I see. I have added a few more ideas. the main point, if I understand correctly your design is that your view controller shall be able to execute `method2` at a later time (in the scenario when it is created while a first method1 request has not completed yet). – sergio Feb 27 '13 at 15:52
  • Thank you for your help, it's really appreciated. And yes, you understood correctly. The only problem with what you are suggesting, is that I would rather not have to modify my test app to handle this. It should be done in the library (so within method1 and method2) – Jean-Francois Gagnon Feb 27 '13 at 16:13
  • then implement the notification mechanism inside of the library and have `method1` post the notification; make `method2` register the observer for that notification. a third method would then handle the second network exchange. I think this should work. but your controller will need to be able to "update" itself. I hope you see that either you make `method2` wait on `method1`, thus ruling out the possibility of using `method2` on the main thread; or you find an alternative async scheme like the one I am suggesting. But your app must support this kind of async behaviour. – sergio Feb 27 '13 at 16:29
1

If you don't really have to have a second method waiting, KVO is a convenient way to do this. Have your object wait for a property on it or another object to change with observeValueForKeyPath:ofObject:change:context:.

Peter DeWeese
  • 18,141
  • 8
  • 79
  • 101
0

Of course the best way is to use a mutex: this is the standard approach for this kind of problems.

You can either use pthread mutexes or the @synchronized directive (take a look at apple's Threading Programming Guide).

Manlio
  • 10,768
  • 9
  • 50
  • 79
  • The problem is that my two methods are called on the same thread. I'm working with a singleton class. – Jean-Francois Gagnon Feb 27 '13 at 15:00
  • ...And I guess you don't have any control over the server, right? Does the server send you any message upon completion? – Manlio Feb 27 '13 at 15:03
  • No, I don't have any control over the server. And yes, the server sends me a message upon completion. Here's the real signature of `method1`: - (void)createCommunicationLinkWithSuccessHandler:(void (^)(NSNumber *))successHandler errorHandler:(void (^)(NSError *))errorHandler; I have my success handler where I would be able to set my boolean value back to false upon completion (if i decide to go with a while loop to block `method2`) – Jean-Francois Gagnon Feb 27 '13 at 15:14
  • i disagree that you should use mutex. it's 1 thread. for example you can have a completion block in task1, to resume other tasks if pending. you can have a queue of pending tasks and each time one is completed in the callback you take and execute the next one. – Ultrakorne Feb 27 '13 at 15:15