Last active
October 5, 2023 14:31
-
-
Save nolanw/dff7cc5d5570b030d6ba385698348b7c to your computer and use it in GitHub Desktop.
Swift multipart/form-data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Public domain - https://gist.github.com/nolanw/dff7cc5d5570b030d6ba385698348b7c | |
import Foundation | |
extension URLRequest { | |
/** | |
Configures the URL request for `multipart/form-data`. The request's `httpBody` is set, and a value is set for the HTTP header field `Content-Type`. | |
- Parameter parameters: The form data to set. | |
- Parameter encoding: The encoding to use for the keys and values. | |
- Throws: `MultipartFormDataEncodingError` if any keys or values in `parameters` are not entirely in `encoding`. | |
- Note: The default `httpMethod` is `GET`, and `GET` requests do not typically have a response body. Remember to set the `httpMethod` to e.g. `POST` before sending the request. | |
- Seealso: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data | |
*/ | |
public mutating func setMultipartFormData(_ parameters: [String: String], encoding: String.Encoding) throws { | |
let makeRandom = { UInt32.random(in: (.min)...(.max)) } | |
let boundary = String(format: "------------------------%08X%08X", makeRandom(), makeRandom()) | |
let contentType: String = try { | |
guard let charset = CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(encoding.rawValue)) else { | |
throw MultipartFormDataEncodingError.characterSetName | |
} | |
return "multipart/form-data; charset=\(charset); boundary=\(boundary)" | |
}() | |
addValue(contentType, forHTTPHeaderField: "Content-Type") | |
httpBody = try { | |
var body = Data() | |
for (rawName, rawValue) in parameters { | |
if !body.isEmpty { | |
body.append("\r\n".data(using: .utf8)!) | |
} | |
body.append("--\(boundary)\r\n".data(using: .utf8)!) | |
guard | |
rawName.canBeConverted(to: encoding), | |
let disposition = "Content-Disposition: form-data; name=\"\(rawName)\"\r\n".data(using: encoding) else { | |
throw MultipartFormDataEncodingError.name(rawName) | |
} | |
body.append(disposition) | |
body.append("\r\n".data(using: .utf8)!) | |
guard let value = rawValue.data(using: encoding) else { | |
throw MultipartFormDataEncodingError.value(rawValue, name: rawName) | |
} | |
body.append(value) | |
} | |
body.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!) | |
return body | |
}() | |
} | |
} | |
public enum MultipartFormDataEncodingError: Error { | |
case characterSetName | |
case name(String) | |
case value(String, name: String) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
👏