iOS Programming - Converting from ASIHTTPRequest to AFNetworking
12/19/2011 By Will Larche In Technology

There are times in everyone's life when we know we must sit down and do something unpleasant for our own good: prepare a tax return, make an OkCupid profile, delete an OkCupid profile, shave our toes, etc. Forcing yourself to learn a new API can seem just as un-fun but often it must be done.

ASIHTTPRequest. What can I say? I don't have a problem with it. I love it. It's simple and allows me to quickly whip up code that sends HTTP requests in only a few lines. Here's an example where I send a Post and a File:

ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:remoteUrl];
[request setPostValue:self.fileName.text forKey:@"name"];
[request setFile:filePath forKey:@"filename"];
[request setDelegate:self];
[request startAsynchronous];

I love this: set the Post value and its key, I set the File and its key, I make myself a delegate and start the upload! The protocol notifies me when it's done and I can move on.

Sadly, ASIHTTPRequest was written by one man who got tired of dealing with his screaming fans and critics. So he decided to abandon the project. Such is the blessing/problem with Open-Source projects: they are free/tenuous. While the code still exists (and still works) he will not be updating it for any future iOS version. He even said in his blog that it was time for us all to move on. Enter AFNetworking.

The good people at Gowalla (formerly Alamofire) have created an HTTP request API that many people are beginning to use. But I mean beginning. It's just really new. I even peaked into someone else's code that I saw implemented the library from AFNetworking to see how they were using it; they aren't tho. They included it and abandoned it to keep using ASIHTTPRequest. :)

So as far as documentation goes there is still not a lot out there. This leaves me in the position of having to teach myself.

 

First task:  Install the AFNetworking documentation into Xcode

One of the best tools in Xcode is its ability to show you the documentation for an element in two clicks. To my great surprise I found that you can install documentation for 3rd party APIs too if they supply it which Gowalla does. But you must acquire a tool called appledoc first and install it. It comes as an Xcode project AND includes an installer shell script. So if you use that you'll be on easy street. Then follow Gowalla's instructions for installing their documentation from GitHub. Now when you need documentation for an AFNetworking class, method, etc, it's available in the same place as the Apple Foundation documentation!

 AFHHPRequestOperation

Second task:  Get the file upload going


Now this was tough. The code necessary to do the same thing as above is much longer (I don't see how this is a plus. I like things succinct.)

AFHTTPClient *httpClient = 
[[AFHTTPClient alloc] initWithBaseURL:remoteUrl];
NSMutableURLRequest *afRequest = [httpClient
multipartFormRequestWithMethod:@"POST"
path:@"/photos"
parameters:nil
constructingBodyWithBlock:
^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:photoImageData
name:self.fileName.text
fileName:[NSString
stringWithFormat:@"%@.jpg", self.fileName.text]
mimeType:@"image/jpeg"];
}
];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
[operation setUploadProgressBlock:
^(NSInteger bytesWritten, NSInteger totalBytesWritten,
NSInteger totalBytesExpectedToWrite) {
NSLog(@"Sent %d of %d bytes", totalBytesWritten, totalBytesExpectedToWrite);
}
];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
[self takeADeepBreath];

Ok. That code is crazy difficult to read at a glance. Let's break it down.

AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:remoteUrl];

This inits a new instance of the AFHTTPClient object which is where you will often do the heavy lifting in this API. Note the convenience method for putting in the URL in its initialization. But also note that this is the base URL. This allows us to make easy calls to other relative URLs we need just by adding the /[directory].

NSMutableURLRequest *afRequest = [httpClient

Makes sense. That's what we're really doing: Making URL requests. Here we use our instance of AFHTTPClient to begin pulling the information we wanna put into our request.

     multipartFormRequestWithMethod:@"POST"
path:@"/photos"

We're gonna Post to the web directory for photos which is one off of our base URL. We could also Put or Delete.

parameters:nil

This I still don't know. Someone email me and tell me. :)

constructingBodyWithBlock:
^(id <AFMultipartFormData>formData) {

“BLOCKS!!!!!?????” Yes. Blocks. It's time to become a man and use a block. And now that I have, I ain't scared any more. It seems to me that blocks work like little functions within methods. I guess this saves us a little space by not having to declare them, etc. But they are very intimidating. Especially when you aren't taught them in your intro to iPhone courses.

Picture a block thusly: it's a function that can be called as an argument. When I say it that way it's REALLY useful sounding!

After the colon you put a carrot (^) to define the beginning of the block. That's followed by the return type (in this case an instance of AFMultipartFormData object called formData. Open a curly brace and start the function!

[formData appendPartWithFileData:photoImageData                                

name:self.fileName.text fileName:[NSString

stringWithFormat:@"%@.jpg", self.fileName.text]

mimeType:@"image/jpeg"];

One message and we're done with this one. But hold up. How is it that we are using the return in the function? Well, maybe 'return' is the wrong word. What it really is an argument? An argument that's populated by the method calling it? Oy. ...

formData is an instance of AFMultipartFormData that the method gives us to play with in the block (glibly.) And in this case we are taking that instance and populating it with the data we want sent in our HTTP body:

the file (in this case a photo),

the value of the 'name' key (which I'm pulling from a UITextField) and

the 'fileName' key (which here is just the 'name' with an extension.



];

Now close that block AND the original message!

When you look at it that way, we now have our HTTP Post request ready to go. In ASIHTTP you would just tell it to startSynchronous or startAsynchronous to get it on its way. Here we must accept that good apps make use of Grand Central Dispatch.

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
AFHTTPRequestOperation inherits from AFURLConnectionOperation which inherits from NSOperation. NSOperation is the class that allows us to encapsulate individual tasks and pass them to the processors with more control. We can take tasks and queue them up by threads and the compiler or the OS or something actually knows which processor core to put them on so that they can run concurrently!

 

The benefits of this are obvious: pass this on to one core so that other tasks are held up waiting for your broken-url to timeout or your huge file to upload over Edge. When used properly, you can avoid HTTP related locks to your app.

[operation setUploadProgressBlock:   
^(NSInteger bytesWritten, NSInteger totalBytesWritten,
NSInteger totalBytesExpectedToWrite) {
NSLog(@"Sent %d of %d bytes", totalBytesWritten,
totalBytesExpectedToWrite);
}
];

BLOCK ALERT! But it's helpful again. This block analyses our upload progress for us and gives us the data to do with as we please. Break it down: it's giving us 3 ints to play with:

            bytesWritten,
totalBytesWritten, and
totalBytesExpectedToWrite

Wanna make a progress bar? Here's its model! This block runs EVERY TIME A PACKET IS SENT giving us useful variables constantly updated. (I'm really starting to love this stuff!)

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

Turns out you can't put these NSOperations right to work. You have to put them in an NSOperationQueue. Wish you didn't have to. Wish you could just send them a start message and they'd go. Maybe some day.

[queue addOperation:operation];

Compile and go! Watch your app post an image.

Comments

That part you asked about (the parameters:nil) allows you to pass an NSDictionary of extra parameters to include in the POST. For example, when our app uploads a file, the server needs to know what the purpose of the file is, so it can move it to the right destination directory.

So one of our parameters is "type", which might contain the value "thumbnail".

Great tutorial, thanks! Now I only need to find out how to deal with timeout exceptions and stuff.

Wow!! What a relief .. great work

Wow! you just saved my arse: however, all should consider change the final code section to------>

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {

NSLog(@"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);

Yes i have been looking for the same configuration. I just tried in my file and it is working for me and executed without any issue.
http://www.slotmachinesaustralia.net

Leave a Comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
To prevent automated spam submissions leave this field empty.
By submitting this form, you accept the Mollom privacy policy.