The first time I worked with image uploads in iOS I was surprised by the lack of a default API.
Yes, you can create
NSURLRequest objects and configire them to POST paramenters to a remote endpoint, but there is no interface to directly attach an image object/file or even raw image data.
Indeed, the default network tools are quite low-level and cover just the most basic use cases, which means that developers are required to write their own code to handle more complex scenarios. Luckily there are a number Objective-C libraries that build upon the core framework and offer advanced network features. You should try them:
- AFNetworking is a great and well maintained library with a very large user base.
- ASIHTTPRequest is well known, but hasn’t been updated since May 2011 and its author said he’s no longer maintaining it. Still, it remains a valuable resource.
- MKNetworkKit is another library similar to AFNetworking.
However, uploading pictures is such a common scenario that it is worth to understand how it works, not to mention that it is actually quite simple.
We’ll need to send a HTTP POST request with its Content-type HTTP header set to multipart/form-data.
Long story short, that means that the body of the request will be composed of a number of different elements, separated by a fixed boundary: an arbitraty sequence of characters/bytes that sepratates the components of the form. You typically specify the boundary as a random string of characters that do not appear as part of other elements.
There is no builtin way to create a
multipart/form-data request body, so we will need to compose it manually by writing strings and objects to a
NSMutableData buffer. This buffer will be used as the request body.
Let’s have a look at the code:
First off, notice how we are mostly using mutable objects: we will need to compose the request and the request’s body one piece at a time.
As you would expect we start by creating a
NSURL object and use it to initialize a
We then proceed to set its HTTP method and the expected response format.
Next we need to define an arbitrary
NSString to use as boundary. When in the next step (lines 26-27) we set the
multipart/form-data, we also provide the boundary string: in this way we are telling the server how to interpret the request and how to separate the different elements composing its body.
Time to build the request body.
We create a
NSMutableData buffer and start pushing elements into it. It is important to serialize all elements using the same encoding:
NSUTF8StringEncoding. Also, all special characters, like
--\r\n, are required.
Each element must be accompained by information about the
name of the request parameter, and in some cases by the
The request body must start and end with the boundary, which will also be used to separate all the individual groups (actual data + accessory information) within the body.
In the code example we start by iterating through a dictionary of values (line 34). We write them in the body of the request using each key as the name of the parameter.
Writing the image
To add the image, we first convert it to a stream of raw data using the UIImageJPEGRepresentation function. The resulting
NSData object can be directly written in the main buffer, but we need to ensure that it will be properly interpreted as image data and not as a string parameter.
This is easy enough, and in fact this time we also provide a file name for the image and specify the Content-Type to
With all our parameters and data properly serialized, we can now close the buffer with one last occurrence of the boundary string and assign it to the request.
Sending the request
Sending the request is quite basic, although I strongly suggest to do it asynchronously: file uploads can take some time, and you don’t want to freeze your whole application in the meantime.