I'm encountering issues while attempting to upload audio files in my iOS Swift application using URLSession. The goal is to send an audio file along with some parameters to a server endpoint using multipart form data. However, despite seemingly correct implementation, I'm facing unexpected behavior.
Here's a brief overview of my code's functionality:
I have a function uploadFile(testID:) where I construct the URLRequest with necessary headers and parameters. I then attempt to read an audio file (sample-3s.mp3) from the main bundle and add it to the request as a part of multipart form data.
I'm utilizing a custom struct URLSession.File to represent each file to be uploaded, containing properties such as name, fileName, data, and contentType.
However, upon executing the request, I'm not receiving the 422 status code response from the server.
func uploadFile(testID:Int) {
var files: [URLSession.File] = []
var params: [String : Any] = ["test_id": testID]
guard let url = URL(string: "\(baseURL)/speaking-file") else {return}
var urlRequest = URLRequest(url: url)
let boundary = UUID().uuidString
urlRequest.httpMethod = "POST"
if let token = Auth.shared.oAuth?.accessToken, let type = Auth.shared.oAuth?.tokenType {
urlRequest.addValue("\(type) \(token)", forHTTPHeaderField: "Authorization")
}
urlRequest.addValue(Configuration.conf?.clientId ?? "", forHTTPHeaderField: "clientid")
urlRequest.addValue(Configuration.conf?.clientSecret ?? "", forHTTPHeaderField: "clientsecret")
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
if let audioUrl = Bundle.main.url(forResource: "sample-3s", withExtension: "mp3") {
do{
let audioData = try Data(contentsOf: audioUrl)
let fileName = audioUrl.lastPathComponent
let contentType = "audio/mp3" // Adjust content type as needed
let file = URLSession.File(name: "file", fileName: fileName, data: audioData, contentType: contentType)
files.append(file)
}catch {
print("Failed to convert to data")
}
}
let data = createBodyWithMultipleImages(parameters: params, files: files, boundary: boundary)
urlRequest.httpBody = data
Logger.log(request: urlRequest)
URLSession.shared.dataTask(with: urlRequest) { data, response, error in
if let error {
print("Error from file upload")
completion(.failure(error))
}
if let response {
print("response form file upload \(response)")
}
if let data {
do {
let json = try JSONSerialization.jsonObject(with: data,options: [])
print(json)
}catch {
print("Failed to seairlized json")
}
completion(.success(ResponseMessage(detail: "File Uploaded")))
}
}.resume()
}
private func createBodyWithMultipleImages(parameters: [String: Any]?, files: [URLSession.File], boundary: String) -> Data {
var body = Data()
if let parameters = parameters {
for (key, value) in parameters {
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
body.append("\(value)\r\n".data(using: .utf8)!)
}
}
files.forEach{
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"\($0.name)\"; filename=\"\($0.fileName)\"\r\n".data(using: .utf8)!)
body.append("Content-Type: \($0.contentType)\r\n\r\n".data(using: .utf8)!)
body.append($0.data)
body.append("\r\n".data(using: .utf8)!)
}
body.append("--\(boundary)--\r\n".data(using: .utf8)!)
return body
}
extension URLSession {
public struct File {
public let name: String
public let fileName: String
public let data: Data
public let contentType: String
public init(name: String, fileName: String, data: Data, contentType: String) {
self.name = name
self.fileName = fileName
self.data = data
self.contentType = contentType
}
}
}