NSURLConnection with delegate vs initWithContentsOfURL: with Grand Central Dispatch - iphone

I am writing an application which downloads very much JSON data from the internet. My app needs to parse this JSON data and afterwards return the retrieved objects to a self-made delegate.
Now I was wondering if I could better use NSURLConnection or a combination of Grand Central Dispatch's dispatch_async with initWithContentsOfURL: (using GCD to prevent hanging).
Using Grand Central Dispatch is much easier to implement, especially because I don't need delegates nor NSMutableData nor all other variables that are used temporarily. But of course, everything with pros has its cons.
What are the advantages of NSURLConnection over initWithContentsOfURL with GCD?

You can target iOS 3.x.
You can use a customized NSURLRequest that, e.g., asks for the results of an HTTP POST—the NSData factory only does basic GET when passed an HTTP URL.
You can track the download's progress with your NSURLConnection delegate.
That's it off the top of my head. :)

Related

HTTP post with UIWebview?

I am quite new to Objective-C coding, learning at College at the moment so. Anyways, how do I perform a HTTP post with a UIWebview so that I can update the view when needed for a different flyer to put put there etc.
I may not even need to use HTTP post doing this, but my co-partner who is coding for Android insists I may need to!
Any help?
I'm not sure what you mean by flyer. But if you want a web page to appear in a UIWebView you don't need to use HTTP post, you just pass the url of a page to load to the UIWebView, or if you already have html content etc. you can load that content directly.
You can make HTTP requests directly but you probably don't need to. What exactly is it you want to display in the UIWebView?
To make an HTTP Connection start by learning about NSURLConnection at iOS Developer Library
The first thing you're going to want to start with is making your NSURL objects then make your NSURLRequest with the parameters as well as the URL of your request. Then make a NSURLConnection and initiate the connection either asynchronously (preferred) or synchronously. If you want a synchronous (blocking) connection initialize it like so
NSError *error;
NSURLResponse *responseHandler;
NSData *response = [myNSURLConnection sendSynchronousRequest:myNSURLRequest, returningResponse:&responseHandler,error:&error];

What major ASIHTTPRequest features is AFNetworking missing?

With work having recently stopped on ASIHTTPRequest, it seems like attention is shifting to AFNetworking.
However, I've not yet found a good comparison of the features of the two libraries, so I don't know what I might lose if/when I switch over.
Major differences I've spotted so far are:
AFNetworking has a much smaller code size (which is good)
AFNetworking is being rapidly improved (so it may not yet be mature, may not have a stable API yet?)
Both seem to have caching, though I've seen hints that because AFNetworking uses NSURLConnection it won't cache objects over 50K
ASIHTTPRequest has very good support for manual & automatic (PAC) http proxies; I can't find any information on what level of support AFNetworking has for proxies
AFNetworking requires iOS 4+, whereas ASIHTTPRequest works right back to iOS 2 (not really an issue for me, but it is an issue for some people)
AFNetworking doesn't (yet) have a built in persistent cache, but there's a persistent cache which has a pending pull request: https://github.com/gowalla/AFNetworking/pull/25
Has anyone seen any good comparisons of the two libraries or any documented experiences of switching from one to the other?
I loved ASIHTTPRequest and I was sad to see it go. However, the developer of ASI was right, ASIHTTPRequest has become so large and bloated that even he couldn't devote time to bring it to par with newest features of iOS and other frameworks. I moved on and now use AFNetworking.
That said, I must say that AFNetworking is much more unstable than ASIHTTP, and for the things I use it for, it needs refinement.
I often need to make HTTP requests to 100 HTTP sources before I display my results on screen, and I have put AFHTTPNetworkOperation into an operation queue. Before all results are downloaded, I want to be able to cancel all operations inside the operation queue and then dismiss the view controller that holds the results.
That doesn't always work.
I get crashes at random times with AFNetworking, while with ASIHTTPRequest, this operations were working flawlessly. I wish I could say which specific part of AFNetworking is crashing, as it keeps crashing at different points (however, most of these times the debugger points to the NSRunLoop that creates an NSURLConnection object). So, AFNetworking needs to mature in order to be considered as complete as ASIHTTPRequest was.
Also, ASIHTTPRequests supports client authentication, which AFNetworking lacks at the moment. The only way to implement it is to subclass AFHTTPRequestOperation and to override NSURLConnection's authentication methods. However, if you start getting involved with NSURLConnection, you will notice that putting NSURLConnection inside an NSOperation wrapper and writing completion blocks isn't so hard as it sounds and you will start thinking what keeps you from dumping 3rd party libraries.
ASI uses a whole different approach, since it uses CFNetworking (lower-level foundation frameworks based on C) to make downloading and file uploading possible, skipping NSURLConnection completely, and touching concepts most of us OS X and iOS developers are too afraid to. Because of this, you get better file uploading and downloading, even web page caches.
Which do i prefer? It's hard to say. If AFNetworking matures enough, I will like it more than ASI. Until then, I can't help but admire ASI, and the way it became one of the most used frameworks of all time for OS X and iOS.
EDIT:
I think it's time to update this answer, as things have changed a bit after this post.
This post was written some time ago, and AFNetworking has matured enough. 1-2 months ago AF posted a small update for POST operations that was my last complaint about the framework (a small line ending fault was the reason that echonest uploads failed with AF but were completed fine with ASI). Authentication is not an issue with AFnetworking, as for complex authentication methods you can subclass the operation and make your own calls and AFHTTPClient makes basic authentication a piece of cake. By subclassing AFHTTPClient you can make an entire service consumer in little time.
Not to mention the absolutely necessary UIImage additions that AFNetworking offers. With blocks and custom completion blocks and some clever algorithm, you can make table views with asynchronous image downloading and cell filling pretty easily, whereas in ASI you had to make operation queues for bandwidth throttling and mind yourself to cancel and resume the operation queue according to table view visibility, and stuff like that. Development time of such operations has been halved.
I also love the success and failure blocks. ASI has only a completion block (which is actually the completion block of NSOperation). You had to check whether you had an error on completion and act accordingly. For complex web services, you could get lost in all the "ifs" and "elses"; In AFNetworking, things are much more simple and intuitive.
ASI was great for its time, but with AF you can change the way you handle web services completely in a good way, and make scalable applications more easily. I really believe that there is no reason whatsoever to stick with ASI anymore, unless you want to target iOS 3 and below.
Just finishing up a project where I'm using AFNetworking instead of ASI. Have used ASI on previous projects; it's been a great help in the past.
Here's what AFNetworking is missing (as of today) that you should know about:
Nothing
ASI is going away. Use AF now. It's small, it works, and it's going to continue to be supported. It's also organized more logically, especially for API clients. It has a number of great classes for oft-used special cases like asynchronous loading of images in table views.
AFNetworking doesn't support clientCertificateIdentity and clientCertificates for TLS client authentication.
We can do that with the - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge method in a subclass of AFURLConnectionOperation but it's not as easy.
I've been using ASI* for a while now and I absolutely love the fileupload approach of ASI, and though I am excited to jump to AFNetworking, fileupload support in AfNetworking is not as easy to use compared to ASI*.
Until now I couldn't figure out how to set a timeout with AFNetworking when doing a synchronous POST request. UPDATE: I finally figured out: https://stackoverflow.com/a/8774125/601466
Now switching to AFNetworking : ]
==================
Apple overrides the timeout for a POST, setting it to 240 seconds (in case it was set shorter then the 240 seconds), and you can't change it. With ASIHTTP you just set a timeout and it works.
A code example with a synchronous POST request:
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"doSomething", #"task",
#"foo", #"bar",
nil];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:baseURL]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:requestURL parameters:params];
[httpClient release];
AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NDLog(#"fail! %#", [error localizedDescription]);
}];
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
[[AFNetworkActivityIndicatorManager sharedManager] incrementActivityCount];
[queue addOperation:operation];
[queue waitUntilAllOperationsAreFinished]; // Stuck here for at least 240 seconds!
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
if (![[operation responseString] isEqualToString:#""]) {
return [operation responseString];
}
return nil;
I tried to set a timeout here, but nothing worked. This issue keeps me from migrating to AFNetworking.
See also here: How to set a timeout with AFNetworking
AFNetwork lacks the ability to upload large files. It assumes file content is in RAM.
ASI was smart enough to simply stream file content from disk.
In ASIHTTP I loved that I could attach a userinfo dictionary to individual requests. As far as I see there is no direct support for this in AFHTTPRequestOperation. Has anyone come up with an elegant workaround yet? Apart from the trivial subclassing of course.
AFNetworking works with "blocks" which is more natural for me than working with delegates like ASIHTTPRequest does.
Working with blocks it's like working with Anonymous Function in javascript.

Parallel downloads in iOS

I am working on building a download manager functionality for the app I am working on. As a requirement we need to support maximum of three parallel downloads. I saw some code examples of this forum using same delegate object and create multiple instances of NSURLConnection objects. A drawback (that I think, and I may be wrong) to this approach is, all the callbacks to delegate object would happen on the same thread. This would result in packets being queued up on the thread. Am I missing something here.
Is there any other way of implementing this functionality such as do a NSInvocationQueue and start individual download on a different thread and thus get better efficiency. With this approach it adds a lot of complexity for tracking progress for each download, pause/resume downloads and thread management.
I am planning to create asynchronous requests on individual thread and not keep synchronous connections for obvious reasons. Also I am downloading large video files > 100 MB and storing it directly to a file. I am a little unclear as to how packets would be queued up and would I run out or memory or would it make main thread unresponsive.
Any pointers or help is greatly appreciated.
Thanks
As Tommy has pointed out, using separate threads just for downloading data is generally not very efficient. It also has a higher memory overhead than using the asynchronous interface of NSURLConnection on the main thread and you lose a lot of control (you can't cancel synchronous connections running on a background thread).
You're right, the delegate callbacks will all be queued on the main thread, but usually, the only thing you do there is to concatenate the data chunks until your download is finished – this is computationally very cheap.
If you intend to do computationally expensive things with the data after it's downloaded (like creating thumbnail images, parsing etc.), you could easily dispatch that work to a GCD queue after your connection has finished downloading. That way, you don't lose control over the download process, can easily display progress or cancel running downloads, but still don't block the main thread.
Apple's recommendation is that you use NSURLConnection asynchronously with a delegate rather than using threads and a blocking connection. If you use multiple NSURLConnections on the same thread then the data will all be returned on the same thread, but that isn't a problem. The data doesn't somehow become intertwined unless your code intertwines it, and there will be no effect on network performance.
Launching threads simply for the process of fetching data is less efficient, especially in battery utilisation, than simply using multiple NSURLConnections on the same thread.
It will help -
http://allseeing-i.com/ASIHTTPRequest/
For parallel download you can use ASINetworkQueue.
You shall look into HTTP Client library like ASIHTTPRequest.
ASIHTTPRequest handle concurrent requests using a queue, which you can limit the concurrency easily. Example code borrowed from ASIHttpRequest:
- (IBAction)grabURLInTheBackground:(id)sender
{
if (![self queue]) {
[self setQueue:[[[NSOperationQueue alloc] init] autorelease]];
}
NSURL *url = [NSURL URLWithString:#"http://allseeing-i.com"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestDone:)];
[request setDidFailSelector:#selector(requestWentWrong:)];
[[self queue] addOperation:request]; //queue is an NSOperationQueue
}
- (void)requestDone:(ASIHTTPRequest *)request
{
NSString *response = [request responseString];
}
- (void)requestWentWrong:(ASIHTTPRequest *)request
{
NSError *error = [request error];
}
Modify the [NSOperationQueue maxConcurrentOperationCount] to change concurrency.

Objective-C: server requests in a thread (like AsyncTask in Android)

I would like to start a server request, you can cancel.
My idea is to start the request in a thread so that the user interface does not freeze. So you can kill the whole thread including the request with a click on a "Cancel"-button.
With Android it works: the server request gets started in a "AsyncTask" and in the "onReturn()"-method I can react as soon as the server request finish.
How can I implement this using Objective-C on iOS?
My first attempt was a "NSInvocationOperation". You can cancel the operation, but it's difficult to handle when a request is completed and results are available. I think NSInvocationOperation is not the solution for my issue.
The would you recommend to me? Is NSThread the right choice for me?
Thank you very much!
Note!
This extremely old answer is now here only for historic purposes.
The wonderful ASIHttpRequest library no longer exists; technology is totally different now.
It is unbelievably simple to do this with ASIHttpRequest.
(Asynchronous is so simple, there is no reason you would ever do it not-asynchronously.)
Here are some rough extracts that might get you started.
...
ASIFormDataRequest *request;
...
NSURL *url = [NSURL URLWithString:#"https://blah.blah/blah.cgi?blah"];
request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:#"fred" forKey:#"username"];
[request setPostValue:#"flint" forKey:#"passie"];
[request setPostValue:#"stone" forKey:#"town"];
// send up data...
[request setData:[NSData dataWithBytes:blah length:blah] forKey:#"thefile"];
// or perhaps something like...
[request setData:imageData withFileName:#"blah.png"
andContentType:#"image/jpeg" forKey:#"photoimage"];
[request setDelegate:self];
[request setDidFinishSelector:#selector(postingDone:)];
[request setDidFailSelector:#selector(postingDoneProblem:)];
[request startAsynchronous];
...
-(void) postingDone:(ASIHTTPRequest *)request
{
// it worked
}
-(void) postingDoneProblem:(ASIHTTPRequest *)request
{
// failed
}
Couldn't really be any easier. You're basically just typing out the fields and values.
Per your question, here is how you cancel an "in-flight" request... just set the delegate to nil and then "cancel" it.
[myRequest setDelegate:nil];
[myRequest cancel];
[myRequest release];
ASIHttpRequest is the "miracle library". If you are new to iOS, ASIHttpRequest is simply THE most used 3rd party library. Essentially, every single iPhone app of the 300,000 iPhone apps uses it.
If at all possible BE SURE to donate a few bucks to the guy -- if he stops supporting that library, 100,000 iPhone programmers are buggered!
the documentation is trivial, a child can follow it:
http://allseeing-i.com/ASIHTTPRequest/How-to-use
"Creating an asynchronous request"
it is probably - almost certainly - the most amazingly simple networking library on any platform. It is trivial to do what you describe, happily. Enjoy.
NSURLConnection is async by default and supports cancelation as well as delegate methods when connection has been established, data has been received or whole request has been completed.
Also data transfer takes place in background so that UI stays responsive.
Cocoa's built-in async networking code is not thread-based but works with run loop events, but the result (asynchronous connections) is the same.
Create an NSURLConnection with +[NSURLConnection connectionWithRequest:delegate:]. The delegate you set will be informed about the progress of the connection and can cancel it anytime with -[NSURLConnection cancel].
Check out ASIHTTPRequest, specifically, the ASINetworkQueue subclass, which is described as:
ASINetworkQueue
A subclass of NSOperationQueue that
may be used to track progress across
multiple requests.
I've only used ASIHTTPRequest for a single synchronous request to download directly to disk, which was easy to implement, but I've heard good reports of using queues to manage multiple asynchronous server requests at once.
One thing to note on the recommendations to use +[NSURLConnection connectionWithRequest:delegate:] is that it should only be called from the main thread as of iOS 4.
see http://blog.mugunthkumar.com/coding/ios4-issue-nsurlconnection-and-nsoperation/ for an example of how to deal with this.

NSURLConnection sendSynchronousRequest - is it possible to implement without leak

Is there a way to implement NSURLConnection without it leaking? A number of Apps including NYTimes and others (including mine) suffer from this. Anyone have a working implementation?
It appears that best practice is to use NSURLConnection asynchronously.
According to the documentation, +[NSURLConnection sendSynchronousRequest:returningResponse:error:] is built on top of the asynchronous loading code made available by NSURLConnection. It would not be difficult to reimplement this by spawning and blocking on an NSThread, running the request asynchronously in the background on a run loop and ending the thread once either connectionDidFinishLoading: or connection:didFailWithError: is received.
Of course, you are better off using the asynchronous code in the first place; it makes for a much better user experience