Last active
January 28, 2025 10:10
-
-
Save msanford1540/e8b6e5e85dd4a79c3f4867ec472fc1c9 to your computer and use it in GitHub Desktop.
Swift 5/6 Multipart form-data Support
This file contains 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
// | |
// 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