This has been a hard one to search. I found a similar question, iOS 5 Wait for delegate to finish before populating a table?, but the accepted answer was 'Refresh the table view,' and that does not help me. The other results I found tended to be in c#.
I have an app that streams from iPhone to Wowza servers. When the user hits record, I generate a unique device id, then send it to a PHP script on the server that returns a JSON document with configuration settings (which includes the rtmp dump link).
The problem is, the delegate methods are asynchronous, but I need to get the config settings before the next lines of code in my - (IBAction)recordButtonPressed
method, since that code is what sets the profile settings, and then records based on those settings.
I've realized I could make the NSURLConnection
in -recordButtonPressed
like I am currently, and then continue the setup code inside the delegate method connectionDidFinishLoading
(or just encapsulate the setup and method call it from there) but that's sacrificing coherent design for functionality and that sucks.
Is there not some simple waitUntilDelegateIsFinished:(BOOL)nonAsyncFlag
flag I can send to the delegator so I can have sequential operations that pull data from the web?
You have analyzed and understood the situation and you have described its possible solutions perfectly. I just don't agree with your conclusions. This kind of thing happens all the time:
You can play various games with invocations to make this more elegant and universal, but my advice would be, don't fight the framework, as what you're describing is exactly the nature of being asynchronous. (And do not use a synchronous request on the main thread, since that blocks the main thread, which is a no-no.)
Indeed, in an event-driven framework, the very notion "wait until" is anathema.
Why not to use synchronous request?
Wrap your asynchronous NSURLConnection request in a helper method which has a completion block as a parameter:
-(void) asyncDoSomething:(void(^)(id result)completionHandler ;
This method should be implemented in the
NSURLConnectionDelegate
. For details see the example implementation and comments below.Elsewhere, in your action method:
Set the completion handler. The block will dispatch further on the main thread, and then perform anything appropriate to update the table data, unless the result was an error, in which case you should display an alert.
The Implementation of the method
asyncConnectionRequst:
could work as follows: take the block and hold it in an ivar. When it is appropriate call it with the correct parameter. However, having blocks as ivars or properties will increase the risk to inadvertently introduce circular references.But, there is a better way: a wrapper block will be immediately dispatched to a suspended serial dispatch queue - which is hold as an ivar. Since the queue is suspended, they will not execute any blocks. Only until after the queue will be resumed, the block executes. You resume the queue in your
connectionDidFinish:
andconnectionDidFailWithError:
(see below):In your NSURLConnectionDelegate:
Then in the NSURLConnectionDelegate, dispatch a the handler and resume the handler queue:
Likewise when an error occurred:
There are even better ways, which however involve a few more basic helper classes which deal with asynchronous architectures which at the end of the day make your async code look like it were synchronous: