I need to send a server a set of requests with some data. The data in the subsequent requests will be determined based on the server response in the earlier requests. I do not want to use synchronous approach with NSURLConnection class, as it is very limiting (for one of the requests, for instance, i need to prevent redirects. According to Apple Dev documentation this can only be done with Delegate implementation).
Depending on which request in the chain it is, i need to be doing different things (send different messages to various classes).
The way i see it now, is that i have to either create n delegates for each of my requests, or create a delegate which would initWithFlag and then create the instances of that delegate with different flags, or i need to use something like a factory pattern which would be pretty similar solution to the second one.
I do not WANT to implement a delegate at all, i want to send requests with the least bit of coding possible.
I am also not looking at any frameworks (ASIHTTPRequest etc), i would like to do it with the native classes.
What would be the best approach?
I know you said you don't want to use third party frameworks, but I'd really suggest AFNetworking. With that said, you do not NEED AFNetworking or any third party library, it will just make your life easier IMHO.
So, what I have done in a similar scenario is essentially use the Command Pattern. When I want to send off one of these complicated "requests" I initialize a command object, set all of the necessary parameters and then call execute. My command object has completion and failure handlers/blocks and execute is an asynchronous call.
Within the command I have different 'steps' that are effectively synchronous and depend on each other. Let's say request A depends on B and B depends on C, the first step of the command is to execute A on it's own queue (I am using GCD with a private queue) and wait for it to finish. Once A finishes (successfully) I continue on to B and pass in any results I need from A into B. Likewise for B->C. If any of the intermediate requests fail throughout the process I can execute the failure block and handle it from where I executed the command (consumer end). If all finish successfully I execute the success block.
I prefer the encapsulation of this approach as it is very easy to re-use throughout the project, all of the intricacies are tucked away in the command's implementation.
Oh and the fact that I use callbacks/blocks I did not need to implement any delegates. In your case using the NSURL classes your command object would be the delegate of any of those instances.
I have settled on implementing the delegate after all.
The key things that tripped me were:
Do NOT declare the delegate methods in .h file. They won't work like that. Simply add them to implementation.
A delegate can be init'ed within the NSURLConnection initWithRequest method or it can be held as a property of the parent class, there is no difference.
The best way to handle multiple requests is the suggested initWithFlag. Therefore, it is best to create a delegate when initialising connection. The delegate lives long enough to perform full data transfer under ARC.
The most convenient way to cancel the redirect comes from Apple's Developer Library:
-(NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
{
NSURLRequest *newRequest = request;
if (redirectResponse)
{
newRequest = nil;
}
return newRequest;
}
Please note that this message is sent several times during the life of the connection for undisclosed reasons. However, if the response is not an actual redirect, the redirectResponse will be nil.
Setting the request to nil will cancel the redirect, but NSURLConnection will finish processing the original data (that is, connectionDidFinishLoading message will be sent).
You can cancel this behaviour by sending this message:
[connection cancel];
I found NSNotifications to be the best way to pass results to the parent class.
Related
HTTP requests made with NSURLConnection are event driven. This makes things a little weird when you need to issue say three requests one after another, where each request uses information returned by the previous one.
I'm used to doing it like this:
response1 = request1();
response2 = request2(response1);
response3 = request3(response2);
But the only way I could find how to do this with NSURLConnection is to have connectionDidFinishLoading: make the next request. But when the number of sequential requests grows, this can get messy.
What's the idiomatic way to handle sequential HTTP requests with cocoa?
You could wrap the requests in an NSOperation and then define operation dependencies, so that each request must wait on its dependent requests before executing.
From the Apple Docs:
Dependencies are a convenient way to execute operations in a specific order. You can add and remove dependencies for an operation using the addDependency: and removeDependency: methods. By default, an operation object that has dependencies is not considered ready until all of its dependent operation objects have finished executing. Once the last dependent operation finishes, however, the operation object becomes ready and able to execute.
I would advise you using a 3rd party library called MKNetworkKit. It can handle the hard work for you, so you can focus on the key aspects of your application. You can find it here.
You can and should use NSOperation and NSOperationQueues.
A good tutorial can be found here: How To Use NSOperations And NSOperationQueues
What would be the most appropriate way to chain asynchronous NSURLConnections? For example, I need to register a user with a web service. The workflow would be as follows:
Get Register Token => Use token to Register => Get Login Token => Use token to Login.
I know how to create an asynchronous NSURLConnection, but I'm unsure how to make sure the previous connection has finished before moving on. I know I can achieve this using NSURLConnection sendSynchronousRequest but I don't like my main thread being blocked while these chained requests happen.
We did EXACTLYA this when we built our first version of SignMeOut for iPhone. We created a subclass of NSUrlconnection and gave it an identifying tag do in the connectionDidFinish you would be able to use a simple switch/case with an enum. Works great - you can see the whole flow and example and code in our blog
http://www.isignmeout.com/multiple-nsurlconnections-viewcontroller/
UPDATE
I've modified the NSURLConnection subclass into a much simpler Category. Most of the usage is the same but cleaner using the same class
https://github.com/Shein/Categories
You can look at connectionDidFinishLoading to start another asynchronous connection. For the conditions as to which connection ended, you can keep references to the connections in case other connections are also expected to finish(probably not needed in your case). Make your class follow the NSURLConnectionDelegate and fire the appropriate connections in the connectionDidFinishLoading delegate method. You can also use the connectionDidReceiveData: method to check for credentials, etc received from the service. Go through the documentation too.
You have different options:
create a queue using a mutable array or dictionary
create an NSOperationQueue kind of easy if you use it in combination
with the new only ios5 API for NSUrlConnection
third parties lib such as AFNetworking
Block GCD grouping them (hard for NSRunLoop reasons, pay attention in wich thread the connection is running in)
In my iPhone app, I've been able to use NSURLConnection properly to download data from a URL. I simply set the delegate to my UIView, and make sure that I set up the UIView to answer for the proper delegate functions such as -connection:didReceiveResponse:. However, if I have a number of NSURLConnections (either for a similar type of request, or multiple kinds of requests), it gets messy because the delegate functions, such as didReceiveRequest, don't differentiate between the different requests. The advantage of asynchronous requests is that you are supposed to be able to multiple at once, without blocking the main thread. What's the best practice for how to use multiple NSURLConnection requests at the same time?
I prefer to wrap them in a higher-level object (like ImageDownloader or SomeWebServiceCall) that has all the per-connection/call state information. I usually create a delegate these objects so that the caller gets a more specific callback when the operation has succeeded or failed.
Perhaps look into ASIHTTPRequest, instead of NSURLConnection. ASIHTTPRequest makes a lot of this work trivially easy.
In this case, I'd say NSOperation is your best bet. ASIHTTPRequest is based on NSOperation and allows you to handle each request as an operation, which serves as the delegate for its own NSURLConnection.
You'll have to be careful here though, because by default NSOperations are run on separate threads, but some APIs, like this one, are required to be executed on the main thread. If you inspect the source code to ASIHTTPRequest you'll notice they've got some infrastructure to ensure delegate methods are called on the main thread.
Create an instance variable for each NSURLConnection and NSMutableData. All of your delegate methods have the NSURLConnection object as an argument, so you can match them as such:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == aConnection) {
// Do something with the data for aConnection
} else if (connection == otherConnection) {
// Do something with the data for otherConnection
}
}
This still limits you to one connection per instance variable, so it's not suitable for, for instance, a table view with images on each row that need to be loaded.
Not even sure if the title is correct, however, what I'm trying to do is use the standard NSURLConnection class to handle responses from calling my webservice. I am using the POST method to update a database and the GET method to retrieve rows from the database. The problem I have is that these 2 actions may occur simultaneously so that the methods to handle the request may step on each other. In other words in my "connection didReceiveData" method I have 2 paths through the code depending on whether I'm handling a response from a GET or POST request.
I keep track on which request in being processed by an instance variable called requestType. The problem is that since the requests are being executed simultaneously the instance variable is being changed from GET to POST before the GET completes (or vice-versa). My question is how do I maintain 2 separate requests in this scenario? Do I synchronize the requests? Is there a way for threads to work here? Do I create 2 separate objects for each of the requests and have the logic in "didRecieveData" query for which object is being processed? Any help would be much appreciated!!
Dealt with a similar issue in one of our apps. The solution involved creating a class that represents a webservice call, responsible for calling its own url, loading its own data, etc. The call class had a delegate that would handle parsing the responses (in our case, a web service controller). Wound up getting rather complicated, but prevented the issue of NSURLConnections stepping on each other.
Seems like you've created a messy problem by having a class that tries to do too many things. I would suggest taking one of the following three approaches:
1) Write two classes, one for updates and one for retrievals. Each class creates it's own private NSURLConnection object and acts as the delegate for the async notifications received from the NSURLConnection. The classes could possible share some utility parsing code or extend a base object that has that parsing code in it. But the key being that the code calling these classes would instantiate one of them, make the call, and then release it. This will keep your code cleaner and will insure that the event notifications don't get intermingled.
2) Create a single class that, depending on initialization, does either a post or a get with it's own private instance of NSURLConnection. When a call needs to be made, instantiate the class, get the results, and then release the class.
3) Write your connection handling classes so they use the synchronous NSURLConnection method and call that call that class in a background thread.
Either way, clean code and clear object orientation will prevent messy scenarios like the one you're describing.
Create separate objects that handle the calls. If you want to issue multiple requests at once I would strongly recommend looking at NSOperationQueue, and making these objects subclasses of NSOperation... much nicer way to deal with multiple background requests.
A good example is here:
http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/
The idea there is that you use the non-asyncronous web calls, in operations that are run on separate threads. You can still use asynch calls in NSOperation as well, but doing so is a little trickier and for simple calls you probably do not need to.
I'm in the design stage for an app which will utilize a REST web service and sort of have a dilemma in as far as using asynchronous vs synchronous vs threading. Here's the scenario.
Say you have three options to drill down into, each one having its own REST-based resource. I can either lazily load each one with a synchronous request, but that'll block the UI and prevent the user from hitting a back navigation button while data is retrieved. This case applies almost anywhere except for when your application requires a login screen. I can't see any reason to use synchronous HTTP requests vs asynchronous because of that reason alone. The only time it makes sense is to have a worker thread make your synchronous request, and notify the main thread when the request is done. This will prevent the block. The question then is bench marking your code and seeing which has more overhead, a threaded synchronous request or an asynchronous request.
The problem with asynchronous requests is you need to either setup a smart notification or delegate system as you can have multiple requests for multiple resources happening at any given time. The other problem with them is if I have a class, say a singleton which is handling all of my data, I can't use asynchronous requests in a getter method. Meaning the following won't go:
- (NSArray *)users {
if(users == nil)
users = do_async_request // NO GOOD
return users;
}
whereas the following:
- (NSArray *)users {
if(users == nil)
users == do_sync_request // OK.
return users;
}
You also might have priority. What I mean by priority is if you look at Apple's Mail application on the iPhone, you'll notice they first suck down your entire POP/IMAP tree before making a second request to retrieve the first 2 lines (the default) of your message.
I suppose my question to you experts is this. When are you using asynchronous, synchronous, threads -- and when are you using either async/sync in a thread? What kind of delegation system do you have setup to know what to do when a async request completes? Are you prioritizing your async requests?
There's a gamut of solutions to this all too common problem. It's simple to hack something out. The problem is, I don't want to hack and I want to have something that's simple and easy to maintain.
I'm not discounting asynchronous delegate calls, but I usually end up using a threaded worker class with synchronous requests. I find it's easier in the long run to have a well defined, threaded API, instead of filling up your controller with code managing the state between asynchronous methods. You could even make asynchronous in your worker thread, although usually it's easier to use the synchronous methods unless they don't support a feature you need to use. Of course, all of this depends on the circumstances, I can think of many situations where simply using the asynchronous methods would be the best route.
Definitely consider NSOperationQueue if you go this route; it greatly simplifies creating multiple worker threads, and it also supports priorities and dependancies between operations. Right now there are some problems with it on 10.5, but I haven't heard of any issues on the iPhone.
An official response is that you should almost always go asynchronous and that synchronous is bad. I found ASIHTTPRequest makes asynchronous requests easy-peasy.
I don't think that there's a "right" answer. It seems that you understand the compromises involved and you just need to make your design around those.
A few extra random points: sometimes your application forces a particular approach. For example, many of the convenience (i.e., synchronous) methods won't allow authentication. For me that meant that my decision was made.
For Yummy I ended up not using threads. I made all my network calls asynchronous and I used the default XML parser (which works using call backs). Since it's all event driven and each unit is small it allows the GUI to be pretty fluid without having the complexity of threading.
I use a state machine to figure out why I'm getting a particular response, and a queue so that I only need to have a single operation "in flight" at any given time. There's a distinct order to most requests so I have no need for a priority system.
The networking code is the most complex in my app and it took a long time to get working much less robust!
I personally look at what is being done, I will ususally use an asyc request to ensure that the UI doesn't block, however, I MAY during the course of that request disable the UI of my application.
A prime example of this is in an application that I built with a "search" button. Once the search was triggered as an async request I would disable the button until the response came back, effectivly limiting the ability for the user to spawn a second asyc request.
Doing this, at least I can prevent the need for priorites, granted this only works if you can in an easy to do way, limit your users to one action at a time.
I'd recommend the asychronous way, no question. Then, load the information only when needed, and use a delegate system to give that information to the correct object.
You don't want to block the UI. Ever. And loading information asynchronously allows you better control over what's happening so you can throw up an error message if needed.
Just a thought: If you want to use the Unit Testing framework for the iPhone, you may want to have synchronous functionality, as that could make writing the tests easier.
However, some of your APIs may not work synchronously, so you need to turn them into sync tasks. Provided the code that performs the unit testing runs in its own thread, you can write a wrapper that will wait until the async task has finished.
To accomplish that, you'd use a semaphore: Your wrapper function starts the async operation and then uses the semaphore to block itself (i.e. puts itself to sleep). The callback that signals the end of the async event releases the semaphore, so that the sleeping wrapper thread can continue and return.
I would use dispatch_async with synchronous. This way, you have the advantage of no delegates/NSNotifications and it is non-blocking
- (NSArray *)users {
if(users == nil) {
users = do_sync_request();
}
return users;
}
// now when calling the users method, do this
- (NSArray *)getUsers {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *users = [self users];
dispatch_sync(dispatch_get_main_queue(), ^{
return users;
}
}
}
Why can't you use an asynchronous request like so:
- (NSArray *)users {
if(users == nil && !didLaunchRequestAlready )
users = do_async_request // Looks good to me
return users;
}
Asynchronous is absolutely the only option - the only real question is if you want to start using separate threads, or if you want to just use the asynch calls. Start there and look at managing threads if you really need to.