Moving stuff out of appdelegate - iphone

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.

Related

Aborting, montitoring a download after navigating away, thread issue?

I am trying to solve my last problem in my app which is the biggest for me.
I have this downloadView shown in the screenshot where I download a file/document from a web server through NSURLConnection asynchronously.
All of the view components work perfectly (download button, progressBarView, abortButton) and so on.
When the download starts, the progress bar increments and I can perfectly abort the download by setting the connection to nil and setting the data length to zero.
:: My challenging problem is that ::
when the download is processed in the background and I click on the "BACK" button and navigate back to the firstView and then navigate back to this downloadView, I lose the access to the download. I can no longer abort it or monitor it. The progress bar resets to zero. However, I can still see the download is going and running through NSLog for progressBar.progress.
I think this problem has something to do with retaining views and accessing threads and keeping a downladView alive when pressing on the "back" button.
Sorry for writing too much but I am trying to clarify this issue well.
here is a basic code to show how I am downloading the file.
-(IBAction)downloadButton:(id)sender{
urlLink= #"http://www.example.com/text.pdf";
NSURLRequest *request= [NSURLRequest requestWithURL:[NSURL URLWithString:urlLink] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
connectionbook = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
// using the regular delegate connectino methods
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{}
The core problem is that when you hit the Back button, the view controller object gets dealloc'd (or it should), and when you revisit that view, you get another view created from scratch.
So you have a couple of options. Have the view which you go back to keep a strong reference, so your view shown here is never really dealloced. Thus you always push the same object. You will need to keeps some state around to deal with the viewWillAppear etc getting called all the time.
The other solution is to have some other persistent object keep the connection and have some way for your view controller showing progress access the information.
Two other comments. You sadi "I can perfectly abort the download by setting the connection to nil and setting the data length to zero", which is not the right way to do it. When you want to stop a connection, you send it [conn cancel], then conn.delegate - nil, then you can release it.

Run a background thread when application enter background

I need to schedule a task in background when the application enter background state.
I have to do this in order to call a remote service each x time and then show a local notification when some event happend with the remote service.
(I know it's looks like RPN, yes it is, but for some reason I am not able to use PRM)
I tried this code :
- (void)applicationDidEnterBackground:(UIApplication *)application{
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void){
remoteServiceCallThread = [[NSThread alloc] initWithTarget:self selector:#selector(doRemoteCall:) object:nil];
[remoteServiceCallThread start];
}];
}
- (void)applicationWillEnterForeground:(UIApplication *)application{
[remoteServiceCallThread cancel];
}
I put breakpoint in the doRemoteCall selector, put is not working.
Maybe my approach is not the best one. If you have any other hack to doing this operation like I describe it I'll take it.
Thank you.
You are not starting the thread, it's initialization code is at the expiration handler block which will be called right before the app is shut down with a timeout:
A handler to be called shortly before the application’s remaining
background time reaches 0. You should use this handler to clean up and
mark the end of the background task. Failure to end the task
explicitly will result in the termination of the application. The
handler is called synchronously on the main thread, thus blocking the
application’s suspension momentarily while the application is
notified.
The task should be active for 10 minutes only (that is driven by iOS) if your app is not supporting one of the background modes (gps, audio, voip).
You also need to keep the returned UIBackgroundTaskIdentifier reference to be able to mark it as ended if the user brings the app to foreground or when task time is going to the end (that's when the handler block is called).

Detecting when Asynchronous Connection has completed

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.

iPhone: How to Determine When a Download from a URL has Completed

I need to populate an array using data from a NSURL. How do I determine when the download from the URL has completed?
You usually use NSURLConnection and then set its delegate to whatever object controls the download. When NSURLConnection finishes it sends connectionDidFinishLoading: to the delegate to signal completion.
See the URL System Programming Guide.
there is delegate method will be called when all the data from url is download.- (void)connectionDidFinishLoading:(NSURLConnection *)connection {}

IPhone SendDelegateMessage failed to return after waiting 10 Secs

I keep getting the following message from my iPhone 3.0 when trying to convert a large NSData object into base64Encoding for http transmission :
void SendDelegateMessage(NSInvocation*): delegate failed to return after waiting 10 seconds. main run loop mode: kCFRunLoopDefaultMode
If you were not using the touch screen for this entire interval (which can prolong this wait), please file a bug.
I am using synchronous request and touch screen will be frozen with only UIProgressView displaying status while uploading data.
Anyone have any good idea how to resolve this problem ?
As it says: you take too long ;D
web view to english: " i called a delegate and it takes too long and I can't continue displaying HTML or running JS"
don't block the web view or it will complain after a while...
so doing a synchronous request? on the main thread? never do that
Better way:
- webView:... {
dispatch_async(dispatch_get_global_queue(0,0), ^{
//DO LONG RUNNING IN BG
dispatch_sync(dispatch_get_main_queue(), ^{
//update UI
}
}
}
If you are using the UIWebView then we need to do this first before moving to the next view
webView.delegate = nil;