On several occasions I’ve worked with server devs who build endpoints for image uploading (eg user profile pictures, image posting, etc.) that expect the data in multipart format. As someone who mostly uses a thin wrapper over URLSession for my networking, this can be… frustrating. There’s no simple way to just tell URLSession that the data should be multipart encoded without pulling in something like Alamofire to my dependencies, which, when it’s just for this purpose is super annoying.
So, in collaboration with the excellent Soroush Khanlou, my mobile app development company created an encoder (like the JSONEncoder
many of you will be familiar with) that will convert a Swift object to a multipart data stream that you can attach directly to a URLRequest. Suppose we wanted to upload a model like this with a multipart request:
struct TitledImageUpload: Codable {
var title: String?
var file: PngImage?
}
PngImage
refers to an empty subclass of UIImage
that exists solely so you can be explicit about how you want to encode the data – but to be clear, any UIImage
can be cast to a PngImage
, and if you want your data encoded as a jpg instead you can just call our extension function on UIImage called jpgImage(ofQuality:)
, which will return a jpg of the requested quality. And just like for a JSONEncoder
, we make the model Codable
.
With that definition out of the way, here’s how we use it:
let image = UIImage(named: "...")
let imageUpload = TitledImageUpload(title: "Awesome Img", file: image as PngImage)
let encoder = FormDataEncoder()
let stream = try? encoder.encode(imageUpload, boundary: "--boundary-\(Date().timeIntervalSince1970)file-image-boundary--")
let request = URLRequest(url: URL(string: "https://lithobyte.co/api/v1")!)
request.httpMethod = "POST"
request.httpBodyStream = stream?.makeInputStream()
// add headers, etc
We take the UIImage
called image
and create our struct from it, casting the UIImage
as a PngImage
, then we create a FormDataEncoder
, and encode the struct with a multipart boundary. This gives us a stream that we can assign to a URLRequest
‘s httpBodyStream
.
This is available as a subspec in our networking library, which I introduced when talking about what I want in an ideal networking layer. To use just the multipart stuff though, add it to your Podfile like this:
pod 'FunNet/Multipart', git: 'https://github.com/LithoByte/funnet'