Uploading an image to a server from your iOs device works by simulating an upload via a plain and simple web form – you know, those where you have a button that says “Browse” and another one that says “Upload” once you’ve selected an appropriate image file.

Today we’ll take a look at how the implementation on the iPhone/iPad can be done.

First of all, we need to set up the basics to get an asynchroneous connection to the server. We’ll set the HTTP method to ‘POST’ since we want to create/add something.

<code> 
#define DataDownloaderRunMode @"myapp.run_mode" 

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url 
                               cachePolicy:NSURLRequestUseProtocolCachePolicy 
                               timeoutInterval:60]; 

[request setHTTPMethod:@"POST"]; 

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request 
                                                       delegate:self 
                                                       startImmediately:NO]; 

[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:DataDownloaderRunMode]; 

[connection start]; </code>

 

The next thing we must do, is to add certain fields to the HTTP header on the one hand, the actual simulated web form with the image data to the body of the HTTP request on the other.

So, what are the header and the body supposed to look like?
The best way to find out is to go download a packet sniffer application, I highly recommend Wireshark. There probably also are all sorts of plugins for your browsers available that can do this.

Now start Wireshark, start a new capturing session and go to the web page that contains the form you use for the image submission. Go ahead and pick an image and hit the ‘upload’ button. You will see a list of packets in Wireshark. Now go to ‘File > Export > PLain Text File’. This will give you a .txt file with the traced packets. You’ll have to browse through it for a while, but at some point you will see many HEX numbers on the left (like a table) and the corresponding human readable content on the right. Just edit the text file so that at the end you only have the human readable content. The actual headers upload packets could be more than one, you might want to remove any information that’s in between. At the end you should have something like this (I’ve highlighted the important fields, you can ignore pretty much everything else like cookies or other fields):

So, what is this mysterious ‘boundary’? It’s a separator, that helps the receiver know when certain segments in the body of the HTTP request start and end, so it helps delimiting these segments of content. The RFC 2388 Specification explains it as follows:

4.1 Boundary

   As with other multipart types, a boundary is selected that does not
   occur in any of the data. Each field of the form is sent, in the
   order defined by the sending appliction and form, as a part of the
   multipart stream.  Each part identifies the INPUT name within the
   original form. Each part should be labelled with an appropriate
   content-type if the media type is known (e.g., inferred from the file
   extension or operating system typing information) or as
   "application/octet-stream".

 
 
What we need to do is to modify our HTTP request sent from the iPhone to look like what we’ve just traced. Note also that you have to set the appropriate url (as seen in the trace image) and make it a POST request.

#define DataDownloaderRunMode @"myapp.run_mode" 

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url 
                               cachePolicy:NSURLRequestUseProtocolCachePolicy 
                               timeoutInterval:60]; 

[request setHTTPMethod:@"POST"]; 

// We need to add a header field named Content-Type with a value that tells that it's a form and also add a boundary.
// I just picked a boundary by using one from a previous trace, you can just copy/paste from the traces.
NSString *boundary = @"----WebKitFormBoundarycC4YiaUFwM44F6rT";
    
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];

[request addValue:contentType forHTTPHeaderField: @"Content-Type"];
// end of what we've added to the header

// the body of the post
NSMutableData *body = [NSMutableData data];

// Now we need to append the different data 'segments'. We first start by adding the boundary.
[body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
	
// Now append the image
// Note that the name of the form field is exactly the same as in the trace ('attachment[file]' in my case)!
// You can choose whatever filename you want.
[body appendData:[[NSString stringWithString:@"Content-Disposition: form-data; name=\"attachment[file]\";
filename=\"picture.png\"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];

// We now need to tell the receiver what content type we have
// In my case it's a png image. If you have a jpg, set it to 'image/jpg'
[body appendData:[[NSString stringWithString:@"Content-Type: image/png\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];

// Now we append the actual image data
[body appendData:[NSData dataWithData:imageData]];

// and again the delimiting boundary    
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
	
// adding the body we've created to the request
[request setHTTPBody:body];

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request 
                                                       delegate:self 
                                                       startImmediately:NO]; 

[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:DataDownloaderRunMode]; 

[connection start];

 
 
This worked perfectly for me. It is possible that you’re going to face some trouble, since just one missing line or whitespace too much in the header or body might cause you problems. In this case, go back to Wireshark, start a new tracing session and start an image upload by running your app on the simulator from xCode. Again, export the trace to a text file, modify it to make it more readable and compare it to the trace from the web form. This will help you see if your HTTP headers and bodies have the same content – if not, modify the code accordingly.

Good luck! :-)


 
About iriphon

About iriphon

"Mobile Dev Blog" probably isn't the most accurate description of what iriphon.com is supposed to represent. I am still working on that, be patient with me :-) Basically anything that has to do with the mobile world can be a topic for iriphon, whether it refers to app store search optimization, design tips and tricks, reports on platform market shares, news from the mobile world or typical coding tutorial posts, I will try to cover everything. Even fun facts! The interesting part about all of this is that, as I am a female engineer, I'd like to support the initiative of more women channeling their studies towards a technical career. So, you might find posts on how it feels to be a woman in a male company, on figures showing how many girls are studying computer science in college and anything else you can think of. Stay tuned! :-)
About Irina

About Irina

Hi, my name's Irina and I'm happy to welcome you on my blog! I'm an enthusiast about the mobile world, working with native development for iOS and Android, web development as well as UI Design and UX. I currently live in the lovely city of Munich, enjoying the Bavarian lifestyle and working for Swedish IT consultancy Netlight. It's all about a variety of topics and for a wide range of well-known companies in the Munich area, doing frontend as well as backend development, mobile development, UI design, UX, using cutting-edge technologies and sketching, wireframing and prototyping. I also love graphic design and beautiful user interfaces. Before I used to work as a senior mobile developer for iOS and Android at NEXT Munich GmbH. The App Agency developing apps for a variety of customers. Already during my master-level studies in the field of Media Informatics at the LMU Munich I started specializing on mobile development but also on the "soft" side of an app: UI design, usability and information visualization. Furthermore I studied at the Università di Roma Tor Vergata, doing my bachelor thesis with JavaME on user-friendly coupling of mobile phones to public displays. At the CDTM I learned about business and product aspects during my degree in Technology Management and was able to create a great network of interesting and talented people. It was also through the CDTM that I got to design, implement and evaluate my very first iPhone app for the Munich-based fashion startup STYLIGHT. For a while I had the privilege of living in the sunshine state Queensland, more precisely in Brisbane, Australia, working on my master thesis with the Queensland University of Technology - an iPhone App for the Brisbane City Council, meant to encourage citizens to participate at shaping their city.