This is an old topic but agree with @possen ("This does not work so great if your parts consist of say UTF-8 and binary data because it tries to treat it as UTF-8, binary data will cause the initWithData function to fail. Instead, you need to stream the data in and handle each type separately for each encoding based upon the content-type.").
If this helps, this is a Swift implementation that handle binary image.
if let multiparts = responseData?.multipartArray(withBoundary: boundary) {
for part in multiparts {
if part.contentType == "application/json" {
let a = try? JSONDecoder().decode(YOUR_DECODABLE_STRUCT.self, from: part.body)
} else if part.contentType == "image/jpg" {
let imageData = part.body
}
}
}
extension Data {
func multipartArray(withBoundary boundary: String, key: String = "Content-Type:") -> [(contentType: String, body: Data)]? {
func extractBody(_ data: Data) -> Data? {
guard let startOfLine = key.data(using: .utf8) else { return nil }
guard let endOfLine = "\r\n".data(using: .utf8) else { return nil }
var result: Data? = nil
var pos = data.startIndex
while let r1 = data[pos...].range(of: startOfLine)
{
if let r2 = data[r1.upperBound...].range(of: endOfLine) {
pos = r2.upperBound
}
}
if pos < data.endIndex {
result = data[(pos+2)...]
}
return result
}
let multiparts = components(separatedBy: ("--" + boundary))
var result: [(String, Data)]? = nil
for part in multiparts
.enumerated()
.map({ index, data -> Data in
if index == multiparts.count-1 {
return data.dropLast(2)
} else {
return data
}
})
{
for contentTypeData in part.slices(between: key, and: "\r") {
if let contentType = String(data: contentTypeData, encoding: .utf8),
let body = extractBody(part)
{
if result == nil {
result = [(String, Data)]()
}
result?.append(
(contentType.trimmingCharacters(in: .whitespacesAndNewlines), body)
)
} else {
continue
}
}
}
return result
}
func slices(between from: String, and to: String) -> [Data] {
guard let from = from.data(using: .utf8) else { return [] }
guard let to = to.data(using: .utf8) else { return [] }
return slices(between: from, and: to)
}
func slices(between from: Data, and to: Data) -> [Data] {
var chunks: [Data] = []
var pos = startIndex
while let r1 = self[pos...].range(of: from),
let r2 = self[r1.upperBound...].range(of: to)
{
chunks.append(self[r1.upperBound..<r2.lowerBound])
pos = r1.upperBound
}
return chunks
}
func components(separatedBy separator: String) -> [Data] {
guard let separator = separator.data(using: .utf8) else { return [] }
return components(separatedBy: separator)
}
func components(separatedBy separator: Data) -> [Data] {
var chunks: [Data] = []
var pos = startIndex
while let r = self[pos...].range(of: separator) {
if r.lowerBound > pos {
chunks.append(self[pos..<r.lowerBound])
}
pos = r.upperBound
}
if pos < endIndex {
chunks.append(self[pos..<endIndex])
}
return chunks
}
}