Skip to content

Instantly share code, notes, and snippets.

@msanford1540
Last active January 28, 2025 10:10
Show Gist options
  • Save msanford1540/e8b6e5e85dd4a79c3f4867ec472fc1c9 to your computer and use it in GitHub Desktop.
Save msanford1540/e8b6e5e85dd4a79c3f4867ec472fc1c9 to your computer and use it in GitHub Desktop.
Swift 5/6 Multipart form-data Support
//
// MultiPartFormData.swift
//
import Foundation
/* Example Call site: */
func upload(imageData: Data) async throws {
guard let url = URL(string: "https://example.com/upload") else { return }
var multipartFormData = MultipartFormData()
multipartFormData.addField(named: "Content-Type", value: "image/png")
multipartFormData.addField(named: "file", filename: "profile.png", data: imageData)
let request = URLRequest(url: url, formData: multipartFormData)
_ = try await URLSession.shared.data(for: request)
}
/*----------------------------------*/
public struct MultipartFormData {
fileprivate let boundary = UUID().uuidString
private var formData = Data()
fileprivate var httpBody: Data {
var data = formData
data.append("--\(boundary)--")
return data
}
public mutating func addField(named name: String, value: String) {
formData.addField("--\(boundary)")
formData.addField("Content-Disposition: form-data; name=\"\(name)\"")
formData.addField()
formData.addField(value)
}
public mutating func addField(named name: String, filename: String, data: Data) {
formData.addField("--\(boundary)")
formData.addField("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(filename)\"")
formData.addField()
formData.addField(data)
}
}
public extension URLRequest {
init(url: URL, timeoutInterval: TimeInterval = 60, formData: MultipartFormData) {
self.init(url: url, timeoutInterval: timeoutInterval)
httpMethod = "POST"
setValue("multipart/form-data; boundary=\(formData.boundary)", forHTTPHeaderField: "Content-Type")
setValue("*/*", forHTTPHeaderField: "Accept")
httpBody = formData.httpBody
}
}
fileprivate extension Data {
mutating func append(_ string: String) {
append(Data(string.utf8))
}
mutating func addField() {
append(.httpFieldDelimiter)
}
mutating func addField(_ string: String) {
append(string)
append(.httpFieldDelimiter)
}
mutating func addField(_ data: Data) {
append(data)
append(.httpFieldDelimiter)
}
}
fileprivate extension String {
static let httpFieldDelimiter = "\r\n"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment