Detecting when Asynchronous Connection has completed - iphone

I have a class with a method (getAndPlaySong) that gets an text file from a server, gets a specific string from the text, and uses the string (a URL) to get a music file from the server. The first server call to get the text file is quick so I implemented it synchronously. The second takes longer and I want to update the display while it is occurring.
I want the method to wait for the asynchronous request to finish before it proceeds. I have tried putting a BOOL in the connectionDidFinishLoading method:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[_receivedData length]);
[AppDelegate playAudioWithData: _receivedData];
_dataIsReceived = YES; //BOOL
// release the connection, and the data object
[connection release];
[_receivedData release];
}
And then putting a while loop in the getAndPlaySong method:
[self startRequest: correctSongURL]; starts the request asynchronously
while (!_dataIsReceived) {
//do stuff
}
The result is that the app hangs when it reaches the loop. The loop never terminates and I never get the "Succeeded..." output. I am a little confused and would be grateful for some input.

I don't wish to sound rude, but what you have done is the worst possible thing. Of course the app hangs because it has an infinite loop. Even if the loop terminates eventually, while its in the loop your app is stuck - what if the user tries to do something with your app while its in the middle of the loop?
You need to change your mindset and learn how to use event driven programming with a state machine. That's the paradigm of iOS, you have to knuckle down and do some reading and learning and change the way you think about and construct your programs.
After you make the request you do nothing, i.e. there is no code coming next in your code snippet. Instead your app sits there doing nothing (but still responsive) waiting for the connection to finish (while your app is doing nothing, the OS has created a thread on your behalf and the connection code is executing inside it, when its finished your connection code needs to inform the rest of your code it has finished, you could do this via a delegate for example, or via a notification).
And that is not the only thing it needs to sit and wait for - what if the user taps on the screen? What if the user exits your app? What if ...
Your app needs to be able to responde to all possible events and respond quickly, the connection is just one more event it must listen for and respond to. It can't do that if its in a loop.
Never use loops anywhere like this.
"I want the method to wait for the asynchronous request to finish before it proceeds." No you don't - you can never do anything like this in an application with a GUI. The function must finish executing. You need to implement a state machine, every iOS is a state machine, the application delegate is the main thing driving the state machine, then you add more states and transitions between the state as more events get added to the program.
Look at the app delegate and its methods that tell your code the app entered the foreground, or the background, or there is a low memory situation etc. That's event driven programming, and you need to extend and build on that principle to take into consideration your connection events and any other events.
Sorry if I sound a bit harsh.

Your app is hanging on the Loop because it is infinite.
On iOS, NSURLConnectionDelegate is the way to get a call back when data is received on the connection and to be notified when it is done. And EVERYTHING should be done asynchronously and via the Delegate. That is THE way it is done on iOS.
The results as it streams in should be stored in an NSData object in the didRecieveData method.
Look at the docs on NSURLConnectionDelegate, but here is an outline example:
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
int statusCode = [(NSHTTPURLResponse *)response statusCode];
if(statusCode == 404) {
// Post notification and let concerned parties know something went wrong.
[self cleanupConnection];
}
else if( statusCode == 200) {
// Success do something with the data.
requestData = [[NSMutableData alloc] init];
}
else {
[self cleanupConnection];
}
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[requestData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self finishedLoadingAppData];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Connection Attempt Failed with Error:%#", [error localizedDescription]);
}

Two quick options come to mind:
Create a method to call to handle the completed connection
Use the NSNotification API to send a notification when the load is complete
Additionally, you can use the method for NSURLConnection didReceiveData to handle data coming incrementally from the server.

Related

viewDidAppear being called before WebService connection methods

ViewDidAppear method I put breakpoint in the last step of the operation, but the breakpoint viewDidAppear method of putting at first, trying to run it directly.
Are emerging in connection using WebService. Be the first breakpoint while running webservice connections, calling the latest viewDidAppear. However, prior to providing breakpoint viewDidAppear when calling WebService connection, and this causes the value NULL to return.
In short, I would like to be called viewDidAppear method, after obtaining all the webservice connections. Breakpoint when it's like this, but when I want to work in the same way.
- (void)viewDidAppear:(BOOL)animated
{
[self LabelYukle];
[super viewDidAppear:animated];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if(theConnection)
[webData setLength:0];
}
viewDidAppear is called by iOS system on its own just before view is going to be appeared.
If you want to perform some functions after didReceiveResponse method, there is a method in NSURLConnectionDelegate that can help you.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
This method is used when a connection has finished loading successfully and you can write the functions you want to perform after didRecieveRespose here in this method.
You can read more about NSURLConnectionDelegateProtocol methods here.

Moving stuff out of appdelegate

I have a tricky situation in my app . i am making the server call in application did finish launching method based on the server response i need to load different views .my app is working fine in simulator but in device the app getting crashed because of the time taking to get the server response .i tried NSThread to run the server call but screen is becoming wait until it gets the response from the server.how can i move the stuff out the app deleagte method
You need to call
[[NSURLConnection alloc]initWithRequest:request delegate:self];
This is asynchronous request, you can implement delegate methods of NSURLConnection in appdelegate.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
A better approach would be to display some temporary view and make the network request asynchronous or do it on a background thread. iOS will kill your app if you block the main thread for too long (this may be what's happening in your case), and users aren't likely to want to use an app that spends ten or twenty seconds doing what looks like nothing before putting up a useful display. When you get a response from the server, you can then reconfigure your display appropriately.
BTW, it'd be a good idea to state your question explicitly next time. It's a bit hard to know what you're asking for as the question stands now.
If you're targeting iOS 4.0 and greater, you can use Grand Central Dispatch to make your network call on a background thread. Here's a simple example.
// Create a dispatch queue
dispatch_queue_t networkQueue = dispatch_queue_create("NetworkQueue", 0);
dispatch_async(networkQueue, ^ {
// Start your network task here.
// Now when it completes we will dispatch back to the main queue
// and you can perform UI updates
dispatch_async(dispatch_get_main_queue(), ^ {
// Perform UI updates here on the main thread
};
// Release your dispatch queue
dispatch_release(networkQueue);
};
Now while this is going on be sure you've already put some preliminary unpopulated UI on screen. You should also show and hide UIApplication's networkActivityIndicator appropriately, and possibly some "Loading..." UI.
NSURLConnection is your friend. The best way to solve it is to do the server call asynchronous using NSURLConnection.
There is Sample Code on the Apple Developer Connection that downloads the images for each row in a UITableView asynchronously so the UI is more responsive. So if you take a look at that code you should be able to refactor your code.
Loading the server response asynchronously prevents your app from being shut down by the iOS runtime because you are blocking the main thread for more than 20 seconds.

Cocoa Touch - Display an Activity Indicator while loading a UITabBar View

I have a UITabBar Application with two views that load large amounts of data from the web in their "viewWillAppear" methods. I want to show a progress bar or an activity indicator while this data is being retrieved, to make sure the user knows the app isn't frozen.
I am aware that this has been asked before. I simply need some clarification on what seems to be a rather good solution.
I have implimented the code in the example. The question's original asker later solved their problem, by putting the retrieval of data into another "thread". I understand the concept of threads, but I do not know how I would impliment this.
With research, I have found that I need to move all of my heavy data retrieval into a background thread, as all of the UI updating occurs in the main thread.
If one would be so kind as to provide an example for me, I would be very appreciative. I can provide parts of my existing code as necessary.
If you use NSURLConnection it runs on another thread automatically.
in your viewDidLoad:
NSURLRequest *req = [NSURLRequest requestWithURL:theURL];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
then you need some custom methods. If you type in -connection and press Esc you'll see all the different methods you can use. There are three you will need with this:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// this is called when there is a response
// if you're collecting data init your NSMutableData here
}
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// each time the connection downloads a
// packet of data it gets send here
// so you can do [myData appendData:data];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
// the connection has finished so you can
// do what you want with the data here
}
That is basically all there is to it. NSURLConnection handles all the multithreading itself and you don't have to worry. Now you can create an activity indicator and display it and it will work because the main thread is empty. :)

Making multiple service calls on iPhone app initialization

I need to make multiple asynchronous service calls in the application:didFinishLaunchingWithOptions: method from my application delegate in order to retrieve some data from a service to be used across various controllers in my app. I have control over the service, and I've designed the API to be as RESTful as possible, so I need to make multiple calls during app initialization.
What I want to do is to show a loading view with a progress indicator - similar to the default splash screen from Default.png - and remove that view once the service calls have completed and I have the initial values I need. This is pretty easy to do if there's only one service call, since I can simply hook that logic into the connectionDidFinishLoading: delegate method of NSURLConnection by hiding the loading view and displaying the root controller.
However, with multiple service calls, it becomes tricky. I can "chain" everything together and fire off one request, wait for it to finish/fail, then fire off the second request, and so on until I get to the last request. In the last request, I then hide the loading view and display the normal view. However, this can get unwieldy with multiple service calls, and the code becomes hard to understand and follow.
Any suggestions on the best approach for this?
I'm thinking one solution is to have a singleton class responsible for making service calls and app initialization. The singleton object will fire off all necessary requests in parallel on start, and each fail/finish callback will check if every request has finished. If all requests have finished, then it can call some method in the application delegate and tell it to hide the loading view, show the root controller, etc.
Thoughts?
Another possibility is to have each service completion callback notify (NSNotification) the controller of the progress indicator that progress has been made. You could also tell the controller of the progress indicator of how many request you were planning to make, and let it keep score, and itself do a callback when it thinks everything is done.
I am doing something similar with an NSOperationQueue that is configured to just run 1 operation at a time. See for example WeaveEngine.m and it's synchronizewithServer:credentials: method. I queue up all the separate operations, which are mostly async network calls.
you could use NSThreading and make synchronous calls in separate threads for each thing you need to get like
[NSThread detachNewThreadSelector:#selector(getDataRequest1:) toTarget:self withObject:urlRequest];
[NSThread detachNewThreadSelector:#selector(getDataRequest2:) toTarget:self withObject:urlRequest];
[NSThread detachNewThreadSelector:#selector(getDataRequest3:) toTarget:self withObject:urlRequest];
then in the selector for each thread do something like
- (void) getDataRequest1:(NSURLRequest*)urlRequest {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSHTTPURLResponse *urlResponse;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&urlResponse error:&error];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
if ([urlResponse statusCode] < 200 || [urlResponse statusCode] > 299) {
//request probably failed
}else{
[self performSelectorOnMainThread:#selector(completeRequest1:) withObject:responseData waitUntilDone:NO];
}
[pool drain];
[responseString release];
[urlRequest release];
}
of course it really depends on how many requests/threads you are wanting to spawn.
and you will need to keep track of how many you spawn vs how many finish so you can properly stop your spinner.

My delegate's methods never get called when I use connectionWithRequest:delegate

I'm making an asynchronus POST request in an iPhone app with this call:
[NSURLConnection connectionWithRequest:req delegate:self];
The request seems to get to the server just fine, but none of the delegate methods get hit. I've implemented:
- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- (void) connectionDidFinishLoading:(NSURLConnection *)connection
and
- (void) connection:didFailWithError:(NSURLConnection *)connection
None of the log messages for these methods ever appear. The documentation for connectionWithRequest:delegate: says:
delegate
The delegate object for the connection. The delegate will receive delegate messages as the load progresses. Messages to the delegate will be sent on the thread that calls this method. For the connection to work correctly the calling thread’s run loop must be operating in the default run loop mode.
I think the call is being made off of the main thread (is the assumption that, if I didn't start a new thread explicitly, everything runs in one thread correct?), and I checked the thread with this:
CFRunLoopRef loop = CFRunLoopGetMain();
CFStringRef modeString = CFRunLoopCopyCurrentMode(loop);
NSLog(#"The main loop is running in mode: %#", modeString);
Which creates this console output:
2009-09-13 13:32:05.611 Stock Footage[686:20b] The main loop is running in mode: kCFRunLoopDefaultMode
So, that sounds like it is in the default run loop mode. Is there anything else I should look at that could tell me why my delegate methods aren't hit? The delegate is definitely not dying before the request is complete.
Update: CFRunLoopGetCurrent has the mode kCFRunLoopDefaultMode.
Update (5:40 PM Eastern): Well, I've switched to using initWithRequest:delegate: startImmediately in order to get the callbacks going, but I'd still love to see an example of connectionWithRequest:delegate that actually hits the delegate up.
I had a similar problem. The reason my delegates were not being called is that after I called connectionWithRequest, I started a loop that was checking if the data from the connection had been sent. Since I never returned the control to the RunLoop code, the messages left in the queue were never processed, and therefore the delegate's methods were not called either...
Just make sure that you do not do anything heavy after calling connectionWithRequest and everything should work nicely.
Two things jump out at me:
You have the wrong prototype for your error method (should still get called, however):
connection: (NSURLConnection *) connection didFailWithError: (NSError *) error
You should check to make sure you're getting a valid connection back from + connectionWithRequest:delegate:. It's possible it's not even starting the request.