I use the following code to execute a HTTP-Request:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
After that I want to use the received data. My problem is that i can't do this in connectionDidFinishLoading, but have to do this on another position in my code. As this is an asynchronous task, how can I verify that I don't start to use the received data before the task is completely done?
Thanks in advance,
Edit: My main problem is that the delegates are called after the code which uses the received data.
you just have to keep the received data in an ivar an use it whenever you want. if you really want to be sure, you can also use a BOOL set to YES in
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
don't forget that since iOS 5, the NSURLConnectionDelegate (and DownloadDelegate) have changed.
Download the AFNetworking library and use it for your connections. It has blocks methods, which (to me) are much 'cleaner' to use instead of delegate methods. AFNetworking is available on iOS 4 and higher.
https://github.com/AFNetworking/AFNetworking
Read this post on some basics of AFNetworking:
http://engineering.gowalla.com/2011/10/24/afnetworking/
Related
I am quite new to iOS, trying best way to implement HTTP post / Get communication.
Problem:
I want to make a multiple api calls and each calls will have its separate response. I am trying to write common network utils, Ideally it will take api url, make call and return data to caller. What is the right way to achive it?? I found moderate level of debate and fans for each approach.
Option 1:
dispatch_async(aQueue,^{
...[ make a sync network request get data back]
--- perform operation on data
--- then pass proceed data UI or set it in model.
dispatch_async(dispatch_get_main_queue()
}
Option 2:
-(NSString *) postData:(NSDictionary *)data serverUrl:(NSString *)targetUrl
-- call post data method with seperate delegate for each caller
-- start async request
-- on DidFinishedLaunching or OnError check delegate & then
return response back to callback
Thanks for your inputs.
I think your first option is not good. It is going to block the pooled thread for a long period of time which is not advisable. When implementing Multithreading in any environment pooled threads provided by the system should not be used for long running processes. Second synchronus network call are not really advised and it has its own pitfalls.
Your second option is more viable. An improvement that you may be able to do is to perform the work that happens in the did finish launching to a GCD thread and after the processing send the data on the main thread
There is a wonderful library available, called AFNetworking, which is very easy to implement.
It uses blocks, which greatly simply communication of data between classes (does away with delegates), and is asynchronous.
Example usage is below:
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:"www.yourwebsite.com/api"]];
NSDictionary *params = #{
#"position": [NSString stringWithFormat:#"%g", position]
};
[client postPath:#"/api" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
As simple as that! Result is available directly within the class that calls the HTTP Post or Get method.
It even includes image and JSON requests, JSON deserialization, file download with progress callback, and so much more.
I am creating an iOS app that consumes web services.
I have a class that makes the connections and stores the response in a variable. It also has a status variable where 1 indicates successful connection.
I have set up an NStimer and a function to check when the connection and download is done and if it was successful.
My question is:
Is this a proper way to manage the connection and its outcome?
any suggestions?
Here is the programming guide from Apple Developer website and it describes how to use NSURLConnection delegate. You can manage the received data in connectionDidFinishLoading: method. Notice that using these delegate methods will load data asynchronously. If you want to handle data synchronously, please try sendSynchronousRequest:returningResponse:error:, but this function should never be call in the main thread.
I am try to handle multiple NSURLConnection at the same time by using a different delegate for each connection, for each NSURLConnection I create, I create a new delegate object, but for some reason only one NSURLConnection works at a time, any NSURLConnection I try to start whilst one is already running simply do not start, my delegate does not receive any of the method calls connection:didReceiveResponse:, connection:didReceiveData:, connectionDidFinishLoading: or connection:didFailWithError:. Am I misunderstanding something about how NSURLConnection and its delegate works. Reading other posts most people seem to have a single delegate for all there connections and then use some kind of dictionary to get the right object to handle the right connection. Is that the way you have to do it.
What you are describing should work fine. You can have multiple NSURLConnections in flight at once, each with its own delegate.
If you want to know why your case isn't working, you'll probably need to show your code in your question.
I hope that at least answers the general question.
NSData has always had a very convenient method called +dataWithContentsOfURL:options:error:. While convenient, it also blocks execution of the current thread, which meant it was basically useless for production code (Ignoring NSOperation). I used this method so infrequently, I completely forgot that it existed. Until recently.
The way I've been grabbing data from the tubes is the standard NSURLConnectionDelegate approach: Write a download class that handles the various NSURLConnectionDelegate methods, gradually build up some data, handle errors, etc. I'll usually make this generic enough to be reused for as many requests as possible.
Say my typical downloader class runs somewhere in the ballpark of 100 lines. That's 100 lines to do asynchronously what NSData can do synchronously in one line. For more complexity, that downloader class needs a delegate protocol of its own to communicate completion and errors to its owner, and the owner needs to implement that protocol in some fashion.
Now, enter Grand Central Dispatch, and I can do something as fantastically simple as:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSData* data = [NSData dataWithContentsOfURL:someURL];
// Process data, also async...
dispatch_async(dispatch_get_main_queue(), ^(void) {
// Back to the main thread for UI updates, etc.
});
});
And I can throw that sucker in anywhere I want, right in-line. No need for a download class, no need to handle connection delegate methods: Easy async data in just a few lines. The disparity between this approach and my pre-GCD approach is of a magnitude great enough to trigger the Too Good to be True Alarm.
Thus, my question: Are there any caveats to using NSData + GCD for simple data download tasks instead of NSURLConnection (Assuming I don't care about things like download progress)?
You are losing a lot of functionality here:
Can't follow the download progression
Can't cancel the download
Can't manage the possible authentication process
You can't handle errors easily, which is really important especially in mobile development like on iPhone of course (because you often lose your network in real conditions, so it is very important to track such network error cases when developing for iOS)
and there's probably more I guess.
The right approach for that is to create a class than manages the download.
See my own OHURLLoader class for example, which is simple and I made the API to be easy to use with blocks:
NSURL* url = ...
NSURLRequest* req = [NSURLRequest requestWithURL:url];
OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
[loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
NSLog(#"Download of %# done (statusCode:%d)",url,httpStatusCode);
if (httpStatusCode == 200) {
NSLog(%#"Received string: %#", loader.receivedString); // receivedString is a commodity getter that interpret receivedData using the TextEncoding specified in the HTTP response
} else {
NSLog(#"HTTP Status code: %d",httpStatusCode); // Log unexpected status code
}
} errorHandler:^(NSError *error) {
NSLog(#"Error while downloading %#: %#",url,error);
}];
See the README file and sample project on github for more info.
This way:
you still rely on the asynchronous methods provided by NSURLConnection (and as the Apple's documentation says about Concurrency Programming if an API already exists to make asynchronous tasks, use it instead of relying on another threading technology if possible)
you keep advantages of NSURLConnection (error handlings, etc)
but you also have the advantages of the blocks syntax that makes your code more readable than when using delegate methods
WWDC 2010 Session Videos:
WWDC 2010 Session 207 - Network Apps for iPhone OS, Part 1
WWDC 2010 Session 208 - Network Apps for iPhone OS, Part 2
The lecturer said
"Threads Are Evilâ„¢".
For network programming, it is strongly recommended to use asynchronous API with RunLoop.
Because, if you use NSData + GCD like the following, it uses one thread per connection.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSData* data = [NSData dataWithContentsOfURL:someURL];
And it's likely to use many connections and many threads. It is too easy to use GCD :-)
Then, many threads eats huge amount of memory for its stack.
Thus, you'd better to use asynchronous API as AliSoftware said.
As of OS X v10.9 and iOS 7 the preferred way is to use NSURLSession. It gives you a nice, block-based interface and features like canceling, suspending and background downloading.
I had async NSURLConnections working just fine when I was using my viewController as the delegate, have all sorts of connections now and I want to do them all through a singleton Connection object. So I'm having Connection create a new delegate object for each connection it makes. Connection is instantiated in the app delegate, but the +(void)send:(Message *) function probably terminates.
My feeling about how this works is that the delegate listeners get put in the run loop (I'm not totally clear on this but I think they're not in separate threads. Shouldn't matter because the delegates allocate their own responseData memory.) and the connectionDidFinishLoading executes just fine, but with an empty responseData. By that I mean I find myself in connectionDidFinishLoading but responseData has zero bytes.
Code creating the app delegate (in the send method) is:
ConnectionDelegate *delegate = [[ConnectionDelegate alloc] init];
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:request delegate:delegate];
So my question is two folded:
Is the problem that the send method terminates? The delegate pointer has local scope.
If that's the problem, what can I do to keep the delegate alive?
You should double check your delegate's implementation of connection:didReceiveData:
Remember that you are responsible for collecting the incoming data segments there.
Please read Apple's doc(, especially URL loading system programming guide) and sample code again if any doubt.