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

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 {}

Related

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.

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.

how to apply the backgroud process in my iPad application without effecting the process running on foreground?

I'm Developing an iPad application where I need to download file from the Webservice and I don't want it affect any other process running on the foreground.
I am displaying the data from the local database in my app and also this data is coming from the web service.
Help Is Appreciated.
Thank You Very Much in advance.
NSURLConnection and its delegate method will allow an asynchronous(background thread) load of a URL request.
Refer the NSURLConnection Class Reference
After getting the data from the server you should parse it on another secondary thread. Then you can save it to the Database.
You can find a better demonstration in the Apple sample apps. Please check the TopPaid app .
This sample app don't have a Database management module. But will teach you to develop a Universal (iPad and iPhone compatible app).
Few thoughts:
you can run the download process on separate thread.
Write a class as below
#interface FileDownloader : NSOperation
//with following methods:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:fileRecord.fileURLString]] delegate:self startImmediately:YES];
you can use thread use below method to detach thread
[NSThread detachNewThreadSelector:#selector(yourMethod) toTarget:self withObject:nil];
now perform your task in method
-(void) yourMethod {
//ur work
}
good luck
When downloading from a service in the background, I prefer to use synchronous calls running on a separate thread. This is how I do it in most of my apps.
call my generic method that spins a new thread
[[MyServiceSingleton sharedInstance] doSomeWorkInBackground:param1];
within singleton - define private method - doSomeWorkBackgroundJob (I use the empty category approach) to call within doSomeWorkInBackground method
[self performSelectorInBackground:#selector(doSomeWorkBackgroundJob:) withObject:param1];
within background job - create pool, do work, drain pool
- (void)doSomeWorkBackgroundJob:(NSString *)param1 {
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
assert(pool != nil);
// you can call another method here or just create your synchronous request and handle the response data
[pool drain];
}

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. :)

UIWebView loading progress and adjust web page to fit the view page?

I am using UIWebView to load a web page.
There are 3 questions:
1.It it possible to track the percentage progress when UIWebView is loading the page?
2.I noticed that when Safari loading a web page, the URL textfield displays a blue background progress indicator to tell user the percentage of loading a web page. What is the technology for this?
3.I know there is property scalesPageToFit
scalesPageToFit
A Boolean value determining whether the webpage scales to fit the view and the user can change the scale.
I try to set it to YES, but it looks like that it is not in public API and my app stopped with black screen, I am not sure what is wrong?
To answer #1)
Instead of using a UIWebView, you can pull the webpage down as an NSData object using an NSURLConnection. When you get the initial response from your request from
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
the webserver should return a value of "expected content size" (which should be included in the response). Then you will keep getting the following method called each time you receive data:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
Keep appending the data to an existing NSMutableData object. Then you can check the size of your current data object (NSMutableData.length) against the expected response size.
percentage = (myData.length*100)/theResponse.expectedContentSize;
Then you can update a progress bar with that percentage! When
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
runs, use your data to call
[myWebView loadData:myData MIMEType:myMimeType textEncodingName:myEncoding baseURL:baseURL];
and it will load everything you pulled down into your web view.
Re #3:
You can try specifying scalePagesToFit as a YES in the viewDidLoad event of the UIView that contains your webview, e.g:
- (void)viewDidLoad {
self.webView.scalesPageToFit = YES;
//other code here...
}
For cases where this doesn't work, refer to the following StackOverflow question: UIWebView does not scale content to fit where the asker (and subsequently, answerer) gave this solution.
Apparently you can use javascript to resize your page to fit the browser:
NSString *jsCommand = [NSString stringWithFormat:#"document.body.style.zoom = 1.5;"];
[webLookupView stringByEvaluatingJavaScriptFromString:jsCommand];
You can try to use this subclass of UIWebView which uses private UIWebView methods - therefore, this solution is not 100% AppStore safe (though some apps do almost 100% use it: Facebook, Google app, ...).
https://github.com/petr-inmite/imtwebview
Re #2: I believe Safari uses a private API call on UITextField (so you can't do it if you want to submit to the app store), but you should be able to implement this yourself by putting a textfield with no border over the top of a progress bar.
To answer question #1, there is a solution that is App Store safe, and uses a similar method as Webkit to track progress.
https://github.com/otium/OTMWebView