Coming from the world of web programming, I'm pretty much comfortable with working with multipart form requests to upload files. However, in iOS, we have a thing called NSURLSession
with the method uploadTaskWithRequest
, which seems to be the method to call to do image uploads and the likes.
Can you explain the difference between the two approach, multipart form upload vs uploadTaskWithRequest
? If I already have a backend that handle multipart form uploads, what kind of adjustments that I might need so that it support uploadTaskWithRequest
as well?
Fortunately, it is quite easy to do a multipart/form-data POST request in the background, for example if you want to upload an image along with some other information.
First, create the NSMutableURLRequest in the same way like you would for a synchronous request (see for example POST multipart/form-data with Objective-C).
Then, write the body of the request to a file and supply it to the uploadTaskWithRequest method of the NSURLSession that you created with a backgroundSessionConfiguration:
If you have multiple tasks and want to be able to distinguish them in the delegate callback, you can set a parameter (after you have created the request) with the NSURLProtocol class:
and get it back in the callback like this:
File Upload with
multipart/form-data
The first approach using a
multipart/form-data
Content-type was originally defined in RFC 1867, then moved to the World Wide Web Consortium, which included it in the specification for HTML 4.0, where forms are expressed in HTML and where form values are sent via HTTP and electronic mail. When the form has been filled out by a user the form was sent to the server. This technique is widely supported and used by browsers and web servers.However
multipart/form-data
can also be used to define form data which are presented in other representations than HTML. That is, you don't necessarily need a web browser or web server. The current specification which can be used by a wide variety of applications and transported by a wide variety of protocols is RFC 7578 (form IETF).It must be mentioned, though, that the
multipart/form-data
content type was not always/is not without issues. It is quite complex by itself. Additionally, it uses/refers to a lot of other RFCs and - as a result of clearing things up - it and those which it depends on have been changed, obsoleted and updated quite frequently. Due to its complexity, serialisers and parsers are getting quite complicated, too and there's a lot of room for bugs and other issues.NSURLSession uploadTaskWithRequest
How
NSURLSession
composes a request is not precisely documented. It certainly does not use amultipart/form-data
content type, though.For upload tasks,
NSURLSession
uses a POST request with aNSURLRequest
as parameter which you can setup yourself. That is, you optionally can set the content type (for exampletext/plain; charset=utf-8)
, and other headers.NSURLSession
can also derive an appropriate content type itself from the given content (file, stream or NSData). That is, we may say, it becomes a "simple" POST request. Due to less complexity, the request is less troublesome.So, in order for your server to support an
uploadTaskWithRequest
where a file should be uploaded, it should simply support a POST request with some "simple" content type. That is, as opposed to a "file upload" with amultipart/form-data
content type which contains the file name in a disposition header, the server would need to return the URL of the location where the resource (the file) has been written to.The
uploadTaskWithRequest
simply sends theNSData
, file, or stream as the body of the request. It doesn't do anything beyond that. It simply has the benefit that it can be used with background sessions.So, if you have web service that is expecting
multipart/form-data
request, you have to build that request yourself (unless you use something like AFNetworking or Alamofire to do this for you). Once you've built that request, you can either usedataTaskWithRequest
(having set theHTTPBody
of theNSMutableURLRequest
) oruploadTaskWithRequest
(in which case you don't setHTTPBody
, but rather provide it as a parameter touploadTaskWithRequest
).By the way, a tool like Charles is very useful in these cases, letting you observe what's going on behind the scenes.