Итак, я нашел проблему и решение.
Ошибка была в этой строке:
body += "\(file.data)\r\n"
Как говорится @ Larme , мы не можем поместить данные и строка в одну строку.
Лучше Решение будет добавлять данные и строку отдельно, например:
// old body += "\(file.data)\r\n"
// new:
var body: Data = Data()
body.append(file.data)
body.append("\r\n".data(using: .utf8)!)
В моем случае MultiPartFormDataParameterEncoder будет выглядеть так:
func encode(urlRequest: inout URLRequest, with parameters: Parameters?, files: Files?) throws {
do {
let boundary = "Boundary-\(UUID().uuidString)"
var body: Data = Data()
if let parameters = parameters {
for (key, value) in parameters {
try body.appendString("--\(boundary)\r\n")
try body.appendString("Content-Disposition:form-data; name=\"\(String(describing: key))\"\r\n\r\n")
try body.appendString("\(String(describing: value))\r\n")
}
}
if let files = files {
for file in files {
try body.appendString("--\(boundary)\r\n")
try body.appendString("Content-Disposition: form-data; name=\"\(file.key)\"; filename=\"\(file.name)\"\r\n")
try body.appendString("Content-Type: \"\(file.type)\"\r\n\r\n")
body.append(file.data)
try body.appendString("\r\n")
}
}
try body.appendString("--".appending(boundary.appending("--")))
urlRequest.httpBody = body
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
}
} catch {
throw NetworkError.encodingMultiPartFormDataFailed
}
}
public enum NetworkError: String, Error {
case encodingMultiPartFormDataFailed = "Parameter encoding in MultiPartFormData failed ..."
}
Также необходимо добавить файлы и параметры моделей:
public typealias Parameters = [String:Any]
public typealias Files = [FileModel]
public struct FileModel {
let name: String
let key: String
let data: Data
let type: String
init(name: String, type: FileType, data: Data) {
self.name = name
self.key = type.key // will be image or audio
self.data = data
self.type = type.rawValue // will be "audio/mp3" or "image/jpeg"
}
}
extension FileModel {
enum FileType: String {
case image = "image/jpeg"
case audio = "audio/mp3"
var key: String {
return String(describing: self)
}
}
}
// USAGE EXAMPLE
FileModel(name: name, type: .image, data: imageJpegData)
Также не забыли расширение данных для большей безопасности:
extension Data {
private enum DataError: String, Error {
case encodingStringToDataFailed = "Encoding String to Data failed ..."
}
mutating func appendString(_ string: String) throws {
guard let data: Data = string.data(using: String.Encoding.utf8, allowLossyConversion: false) else {
throw DataError.encodingStringToDataFailed
}
append(data)
}
}
Спасибо ^ _ ^