Trouble uploading audio files in iOS Swift using URLSession: Unexpected Behavior with Multipart Form Data

39 Views Asked by At

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
        }
        
    }
    
}
0

There are 0 best solutions below