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.
Related
I have an application in which I need to have a settings page,which has some credentials of the user then he can edit that.its a table view loading from an array taken from the httprequest.by clicking on each of this it will have the option to go to another view and update that value and come back. I have done the update call to the server on that update view like this..
dispatch_async(backgroundQueue_, ^{
[self performSelectorInBackground:#selector(load) withObject:nil];
dispatch_sync(dispatch_get_main_queue(), ^{
[self showHUD];
});
because in the mainqueue i am doing the popping back operation.so i need that update service to be called in the background.But the problem is when i coming back i am calling another service in the settings viewcontroller.to load the updated value.some times the delegates of the request is getting crashed.I am calling the service like this.
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:uidstr forKey:#"userId"];
request.userInfo=[NSDictionary dictionaryWithObject:#"update" forKey:#"type"];
[request setPostValue:self.string forKey:#"age"];
[request setDelegate:self];
[request setCachePolicy:ASIAskServerIfModifiedCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];
[self showHUD1];
[request startAsynchronous];
Can anybody point me how i can do this with out crashing my app.I think the problem is the delegate getting nil.
Decouple your network requests from your view controller code. That way if the views are unloaded, the network delegate will still exist.
For example: Make a NetworkRequest singleton class that does all the communication with the network, and then you could use a mechanism like NSNotifications, or an #protocol interface in the singleton class that view controllers could become delegates of, to pass results and status changes as needed.
For a good tutorial on singletons in Objective-C, see: http://www.galloway.me.uk/tutorials/singleton-classes/
Better drop usage of ASIHTTP since this framework is no longer supported by the developers. You won't get support for possible changes of future iOS versions.
I have been looking over the internet, but could not find the perfect tutorial for threading in iOS, currently i am working on iOS 5 , tutorial found on web were old, my app needs to fetch data from webservices, so I want to call network operation in a thread, I have a tabbar, in each tab it loads different type of data from web, as it start fetching data , I dont want my app to stuck on that tab, i want that user can navigate to other tab meanwhile its fetching data for that tab. how can it be possible
EDIT: I want to have a flow :
//in a thread
fetchDataFromWeb(){//during this call activity indicator
//fetch and make an array of ojbects
}
when data is loaded trigger the populate funciton
laoddata(){//remove activity indicator
//load tableview and other views
}
how will i know my thread finished its process
Take a look to NSOperation, NSThread or Grand Central Dispatch
Edit: NSThread example
//on main thread on some action
NSDictionary *myParameterDitionary = [..];
[NSThread detachNewThreadSelector:#selector(aBackThreadMethod:) toTarget:self withObject:myParameterDitionary];
//this is on back thread called
- (void)aBackThreadMethod:(NSDictionary *)variablesDictionary{
#autoreleasepool {
//presess data
NSDictionary *responseDictionary = [..];
[self performSelectorOnMainThread:#selector(didABackThreadMethod:) withObject:responseDictionary waitUntilDone:YES];
}
}
//call back on main thread
- (void)didFinishABackThreadMethod:(NSDictionary *)responseDictionary{
//do stuff ex update views
}
The #autoreleasepool is for ARC. If you use manual memory management you have to do:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool release];
I want to show activity indicator while sending and receiving synchronous asihttprequest from server. I have used activity indicator as the asihttprequest is send but it did not showing in iPhone due to synchronous request.Any suggestion how to show activity indicator during synchronous data transfer.Thanks
Synchronous request calls your activity indicator delegate method setProgress: on the main thread.
B/c you are using ASIHTTPRequest on the main thread you are blocking the UI, hence calls to setProgress: are queuing to be dispatched after the request is finished, but by that time the progress is already 100%
To solve this use either asynchronous request or call synchronous request on a background thread using
[self performSelectorInBackground:#selector(startSynchronous:) withObject:nil];
Edit
Remember to create your own autorelease pool to handle the memory inside your startSynchronous: method
-(void)startSynchronous:(BOOL)animate{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *autoreleasedString = #"xxx";
NSLog(#"%#",autoreleasedString);
[pool drain];
}
I was using NSURLConnection in a synchronous way before running on a background selector, so when I moved over to ASIHTTPRequest I did the same with this framework.
So, is it a bad idea to do something like the following?
// From another method
[self performSelectorInBackground:#selector(callDatasource) withObject:nil];
- (NSData *)callDatasource {
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:someURLthatIamusing];
[request setTimeOutSeconds:50.0];
[request startSynchronous];
NSError *error = [request error];
NSData *returnedData;
if (!error) {
returnedData = [request responseData];
} else {
// do something with error
}
[self performSelectorOnMainThread:#selector(done) withObject:nil waitUntilDone:NO];
[apool release];
return returnedData;
}//end
What would be the advantage to use the ASIHTTPRequest and asynchronous methods along with the delegate methods?
From experience, sometimes odd things can happen when using ASIHTTPRequest synchronous requests off a secondary thread: the download activity icon in the status bar not disappearing upon download completion is one issue I've noticed from time to time. I've had no major problems in the past, but I use the asynchronous methods now rather than your approach. The ASI asynchronous methods are by the nature of being a widely used library more highly tested than my own implementation could ever be.
There are a number of advantages with using the asynchronous methods - you mention the delegate methods, but the latest release of ASI actually also supports blocks, which is a great leap forward (dealing with multiple synchronous calls used to be a bit of a pain due to the shared delegate methods (or unique delegates for each asynchronous call). But with blocks you can now get rid of the delegates entirely. I've found them to be really useful. Plus if you use multiple contributors it can make readability a lot easier.
Also, by doing it Async, you can more easily track progress through the setProgressDelegate command.
I have an iPhone app, where I'm displaying a tableview, that's loaded from an RSS feed. When the view is loaded, I call this method to run in a new NSThread:
- (void)start:(NSURL*)url {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSXMLParser *XMLParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease];
[XMLParser setDelegate:self];
if (items) {
[items release];
}
items = [[NSMutableArray alloc] init];
[self startParsing:XMLParser];
[pool drain];
}
It's working fine, but if the user leaves the view while it's downloading or parsing the xml, I want the thread to stop running, but how would I stop it from running without leaking memory? Also, if it's running the -initWithContentsOfURL: method while I want it to stop, how would I stop that method?
If you anticipate needing to control connections (i.e. stopping a connection if the user cancels or navigates away) you should probably use the asynchronous NSURLConnection API to load your data before parsing the XML. In addition to giving you the ability to close connections as needed, you'll also be able to better respond to network errors.
As NSD pointed out, you should probably implement some sort of cancel method on the class that's driving your XML parsing thread - then just use performSelector:onThread:withObject:waitUntilDone: (or similar) from your main thread when the user cancels the download or navigates away.
These are your thread stopping options
http://developer.apple.com/mac/library/documentation/cocoa/reference/Foundation/Classes/NSThread_Class/Reference/Reference.html#//apple_ref/doc/uid/20000311-DontLinkElementID_12
And from elsewhere in the guide
"If you anticipate the need to terminate a thread in the middle of an operation, you should design your threads from the outset to respond to a cancel or exit message."
Perhaps you should look into the NSOperation and NSOperationQueue classes.
These classes give you a massive amount of control over concurrency and asynchronous execution.
The basic idea is to create a queue, and then subclass NSOperation. Inside your subclasses' main method, do the guts of your work, in this case, you could put your start method inside here.
Then you can control the operation easily, being able to set how many operations can run concurrently, set up any dependencies some operations may have on others. You can also easily cancel operations, which is what you want to do here.
Check out the documentation for NSOperation and NSOperationQueue.