views:

67

answers:

2

I am downloading XML to populate an array used to build UITableView. Until I'm informed otherwise, I believe I have to completely download the array before I can display it in the table (also it's text only and extremely small, so it downloads in a reasonable time on the slowest possible connection). It does take about 3-5 seconds at it's slowest, so it would be nice to display the activity indicator in the status bar while it downloads.

I make the call to...

[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

...before I do ANYTHING (then turn it off once I've done EVERYTHING), but it just pops and quits in about the minimum amount of milliseconds that make it visible to the human eye.

Any suggestions as to why I'm having this experience?

Thanks, Z@K!

+1  A: 

Synchronous downloads are performed on the calling thread and blocks the thread until it is complete, which is probably done on the same thread as your UI. As the download is blocking the thread till its complete you either won't see the activity indicator or it will display and not move till the download is complete.

You will have to put the synchronous download on a separate thread or use NSURLConnection:initWithRequest (which is multithreaded) to be able to have the App respond as expected.

Rudiger
If I put the download on a separate thread, how would I make my table view wait for the completed download, so it can/will display accurate information?
Zak
Whole heap of ways, if you are using synchronous downloads have a call back to your main thread once the download is complete to remove the activity indicator and either [tableview reloadData], or actually load the UITable at that instance using initWithFrame. The easiest way and I think the way most people do it using NSURLConnection:initWithRequest and then in the connectionDidFinishLoading:connection delegate method hide the activityIndicator and reload the table.
Rudiger
Can you expand on how would you choose to implement the callback? I like the idea of downloading the data asynchronously, I just don't know how to make the table behave "normally", without blocking the main thread.
Zak
The answer http://stackoverflow.com/questions/2124421/are-there-complete-examples-that-make-use-of-all-the-nsurlconnection-delegate-met will work for you. basically -(void)load: is where you initiate your activity indicator and connectionDidFinishLoading: is where you remove it and load / reload your table view. -(void)load is a custom one, perhaps place whats in it in your viewDidLoad method, all the rest including connectionDidFinishLoading are delegate methods and the names must remain the same.
Rudiger
My program is currently utilizing NSURLConnection, and I have it able to run a synchronous request or an asynchronous request based on an indicator.The example you linked only works when you have "self" as the delegate for the connection. I'm not set to run this way, I have a dedicated object that handles the download request for several tableviews, so basically connectionDidFinishLoading: has no reference to the calling table view, and cannot ask it to reload.I suppose the easiest thing to do is to pass it a reference, but that seems sloppy to me. Do you have any thoughts on this?
Zak
You can pass the delegate of the calling class to the object that calls the NSURLConnection. You can then have the delegate methods in the calling class rather than the other object or you can respond to two different objects depending on the response of NSURLConnection but this is getting complex. It would be easier just to create a separate thread and call the synchronous resource and then respond back to the main thread. Your code and the required answer is becoming a bit too complex for a simple question answer thread though. Either way your synchronous request is blocking your UI
Rudiger
A: 

The easy answer for me was GCD, Grand Central Dispatch. I barely had to modify my code at all...

My code started as this...

self.table_array = [self.webQuery downloadAndParseXMLForTable];
[(UITableView *)self.view reloadData];

*webQuery is a custom object that downloads and parses xml data from the web. **downloadAndParseXMLForTable is a custom method that synchronously downloads and parses an XML file, then returns an (NSArray *) object to support a table view.

The modified code below, shows the ONLY changes I had to do to adopt GCD, and keep my UI responsive.

dispatch_queue_t table_download_queue = dispatch_queue_create("com.yourcompany.queuename", NULL);

dispatch_async(table_download_queue, ^{
    self.table_array = [self.webQuery downloadAndParseXMLForTable];
    dispatch_async(dispatch_get_main_queue(), ^{
        [(UITableView *)self.view reloadData];
    });
});

dispatch_release(table_download_queue);

That's it! I hope this helps others in my predicament...

Cheers, Z@K!

WARNING: At the WWDC 2010, it was mentioned that GCD cannot currently support SECURE transmissions. I don't remember the details, but the speaker, Quinn, was very adamant about it. I believe the process he suggested required NSOperation...

Zak