Let’s see how to upload any file to a web server, using only Apple’s built-in URLSession. Most of the examples in the Internet utilize dataTask and not the uploadTask. Also the examples I found demonstrate only the uploading of the image files. That’s why I decided to share a working code for building up a multipart/form-data request, that can be used in combination with uploadTask.
// generate boundary string using a unique string let boundary = UUID().uuidString // Set the URLRequest to POST and to the specified URL var request = URLRequest(url: url) request.httpMethod = "POST" request.addValue("Bearer \(yourAuthorizationToken)", forHTTPHeaderField: "Authorization") // Content-Type is multipart/form-data, this is the same as submitting form data with file upload // in a web browser request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") let fileName = fileURL.lastPathComponent let mimetype = mimeType(for: fileName) let paramName = "file" let fileData = try? Data(contentsOf: fileURL) var data = Data() // Add the file data to the raw http request data data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!) data.append("Content-Disposition: form-data; name=\"\(paramName)\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!) data.append("Content-Type: \(mimetype)\r\n\r\n".data(using: .utf8)!) data.append(fileData!) data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!) // do not forget to set the content-length! request.setValue(String(data.count), forHTTPHeaderField: "Content-Length")
As you can see, it’s pretty straightforward. First we create a request, setting Content-Type to “multipart/form-data” and Authorization token if required. You can set any other values if you web service requires. The second is to create the data that confirms to the multipart/form-data protocol. For we want to upload any kind of files, the mime type is recognized automatically:
private func mimeType(for path: String) -> String { let pathExtension = URL(fileURLWithPath: path).pathExtension as NSString guard let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, nil)?.takeRetainedValue(), let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() else { return "application/octet-stream" } return mimetype as String }
So now we have two peaces of puzzle. To bring them together just use the uploadTask(with:data:) function:
session.uploadTask(with: request, from: data)
The webservice should be happy now with your upload file call.