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

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

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.

Wait for an action to be done before loading a detailsView

in my app, I'm loading a detailsView from a tableView. I need to get an XML document (with SOAP), parse it, and then display the informations I got in my detailsView. So, when loading my detailsView I want to wait for the parser to parse the entire document BEFORE displaying my detailsView.
In my parser I'm using a NSURLConnection, and methods of NSXMLParserDelegate like parserDidStartDocument, etc...
I've thought of using threads but I'm not getting anything conclusive.
I'm trying to be clear on what I want to do but it's pretty hard :s
I'll provide more infos if needed.
You can do it in either viewDidLoad or viewWillAppear Method but you have to make all your detailview's control hidden until it loads data from the server and then show it when you get response from the parser with the value you get in response. You can show loading indicator while you are parsing your response. You need to call your url Asynchronously.
Do something like this in your viewWillAppear Method :
NSURL *url = [NSURL URLWithString:#"www.yourwebserviceurlhere.com"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
Implement Connection delegate methods.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
After you FinishLoadingData and you got response you need to parse your data by implementing XMLParser delegate method and when you finished parsing you need to show your detailview.
That's it. Hope this help.

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];
}

saving image in UITableViewCell

I am loading an image from an API in my tableviewcell, it downloads the image everytime I scroll down the UITableView. So what is the best way to save this image and so it doesn't have to download the image again if it is already there?
Same thing for text, how can I save the text...
If it's not very big, you can download it once and save it into user preferences (NSUserDefaults) as NSData object. Works for me.
Alternatively, you can use asynchronous requests with NSUrlConnection and implement caching in any way you like. (For example, update image only once a week.)
Moreover, even default cache settings of NSUrlConnection might work good enough.
More on caching
edit
An example of asynchronous request.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString: url]];
URLConnectionDelegate *delegate = ...;
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
delegate:delegate];
if (!connection) {
// something went wrong
}
In delegate, you mainly need methods to handle received data and to finish connection.
Assume you have NSMutableData *receivedData object.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// success, do whatever you want with data
[receivedData release];
[connection release];
}
The link above and API documentation provide more details about delegate structure.
Look at the LazyTableImages sample application in the iOS site sample code section. It has a great example of how to download images for a table cell using NSUrlConnection asynchronous calls and storing the images (and text) in an NSMutableArray.
This sample demonstrates a multi-stage
approach to loading and displaying a
UITableView. It begins by loading the
relevant text from an RSS feed so the
table can load as quickly as possible,
and then downloads the images for each
row asynchronously so the UI is more
responsive.

Needing a design pattern/example link for iPhone network connections

I'm sorry if this is a basic question. I've been googling, searching StackOverflow, and looking through example code for hours and haven't found anything satisfactory for my skill level.
I'm wanting something like a design pattern for handling network functions on the iPhone SDK. I have heard of people using a singleton class but have heard there are better ways for asynchronous connections. Would NSOperation be useful? I am fairly new to object oriented programming but am needing to make occasional calls to my webserver through HTTP for my current app and hope to find a solution that is easily reusable.
I've looked through the NSURLConnection docs and can get the basic functionality but the programming structure is messy and I'm not sure how to better organize it. Is there sample code for this that separates out these functions into their own class? A link to an example that does this would be greatly appreciated!
thanks!
I've been dealing with this same question for a while now...
If you're effectively doing a GET on a simple resource, and you're confident that the resource will always be there & accessible, there's an easy way to do it:
NSURL *URL=[[NSURL alloc] initWithString:#"http://www.google.com/"l];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
//TODO This needs to have timeouts & such set up, maybe parallelism
NSString *results = [[NSString alloc] initWithContentsOfURL :URL];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
That's a REALLY simple way to do it, but as my comment says, not very robust or reliable.
A slightly more robust, yet still reasonably simple, is replacing the NSString line with:
results = [[NSString alloc] initWithContentsOfURL:URL encoding:NSASCIIStringEncoding error:&err]; // possibly NSUnicodeStringEncoding
if (err!=nil) NSLog(#"Error occurred: %#", [err localizedDescription]);
That will at least TELL you if there's an error...
ASIHTTPRequest provides a lot of neat & useful network functionality for dealing with resources over the internet. http://allseeing-i.com/ASIHTTPRequest/ - the developer has been VERY responsive on his Google Group. I really wanted to use that, and may get back to it when/if it supports SSL Client Certificate authentication (which is what my project requires).
NSURLConnection, as described above - that's what I'm using now in my project. I would imagine that this will satisfy almost all needs, but it's (in my opinion) more tricky to use. And to be honest, I'm still having a little trouble wrapping my mind around how to integrate asynchronous data loading into my application. But if it will work for you - and it probably will, Apple is using it all over the OS and its apps - that's your best bet!
One possible approach is to use the NSURLConnection (as you mentioned).
Inside your .h file:
NSMutableData *connectionData;
Also add a property for connectionData...
Inside your .m file:
- (void)updateFromServer {
// You might want to display a loading indication here...
NSMutableData *connectionDataTemp = [[NSMutableData alloc] init];
self.connectionData = connectionDataTemp;
[connectionDataTemp release];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL: your_url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection release];
[request release];
}
#pragma mark -
#pragma mark NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Add the received bulk of data to your mutable data object
[self.connectionData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Use your data
// If there is a loading indication displayed then this is a good place to hide it...
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// Handle error
// If there is a loading indication displayed then this is a good place to hide it...
}