Upload image with multipart form-data only in Swift 4.2

11,820

Please check my solution which is taken parameters and images.I have tested with PHP, .net, java.

class func request(withImages path:APIMethods, method:URLMethod, token : String?, headers:[String:String]?, parameters: [String:Any]?,imageNames : [String], images:[Data], completion: @escaping(Any?, Error?, Bool)->Void) {


    if !Reachability.isConnectedToNetwork() {
        DispatchQueue.main.async {
            showAlert(message: AppMessages.connectionFailed.rawValue)
        }
        dissmissHud()
        return
    }

    var stringUrl = "\(APIManager.url)\(APIMethods.preFix.rawValue)\(path.rawValue)"
    if method == .get, let lastPath = parameters?.values.first as? String {
        stringUrl += lastPath
    }else{
        stringUrl += token ?? ""
    }


    // generate boundary string using a unique per-app string
    let boundary = UUID().uuidString

    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    print("\n\ncomplete Url :-------------- ",stringUrl," \n\n-------------: complete Url")
    guard let url = URL(string: stringUrl) else { return }
    var request = URLRequest(url: url)
    request.httpMethod = method.rawValue

    if headers != nil{
        print("\n\nHeaders :-------------- ",headers as Any,"\n\n --------------: Headers")
        for (key, value) in headers! {
            request.setValue(value, forHTTPHeaderField: key)

        }
    }

    // Set Content-Type Header to multipart/form-data, this is equivalent to submitting form data with file upload in a web browser
    // And the boundary is also set here
    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

    var data = Data()
    if parameters != nil{
        for(key, value) in parameters!{
            // Add the reqtype field and its value to the raw http request data
            data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
            data.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
            data.append("\(value)".data(using: .utf8)!)
        }
    }
    for (index,imageData) in images.enumerated() {
        // Add the image data to the raw http request data
        data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
        data.append("Content-Disposition: form-data; name=\"\(imageNames[index])\"; filename=\"\(imageNames[index])\"\r\n".data(using: .utf8)!)
        data.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
        data.append(imageData)
    }

    // End the raw http request data, note that there is 2 extra dash ("-") at the end, this is to indicate the end of the data
    data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)

    // Send a POST request to the URL, with the data we created earlier
    session.uploadTask(with: request, from: data, completionHandler: { data, response, error in

        if let checkResponse = response as? HTTPURLResponse{
            if checkResponse.statusCode == 200{
                guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: [JSONSerialization.ReadingOptions.allowFragments]) else {
                    completion(nil, error, false)
                    return
                }
                let jsonString = String(data: data, encoding: .utf8)!
                print("\n\n---------------------------\n\n"+jsonString+"\n\n---------------------------\n\n")
                print(json)
                completion(json, nil, true)
            }else{
                guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) else {
                    completion(nil, error, false)
                    return
                }
                let jsonString = String(data: data, encoding: .utf8)!
                print("\n\n---------------------------\n\n"+jsonString+"\n\n---------------------------\n\n")
                print(json)
                completion(json, nil, false)
            }
        }else{
            guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) else {
                completion(nil, error, false)
                return
            }
            completion(json, nil, false)
        }

    }).resume()

}

extension Data {

    /// Append string to Data
    ///
    /// Rather than littering my code with calls to `data(using: .utf8)` to convert `String` values to `Data`, this wraps it in a nice convenient little extension to Data. This defaults to converting using UTF-8.
    ///
    /// - parameter string:       The string to be added to the `Data`.

    mutating func append(_ string: String, using encoding: String.Encoding = .utf8) {
        if let data = string.data(using: encoding) {
            append(data)
        }
    }
}
Share:
11,820

Related videos on Youtube

Nidhi Tiwari
Author by

Nidhi Tiwari

I help clients mobile applications. Results-oriented iOS developer with more than 7 years’ experience writing highly readable, clean, maintainable source code. Developed applications for iPhone and iPad that offer users an intuitive, friendly interface, and successfully executed on itunes.I have worked in multiple areas in mid size companies related to online products/services ranging from application development to user interface design, and project management. Results include robust downloads, activation, session lengths, retention rates, revenue increases and referrals.I am involved in product design and development for both MVPs and full featured apps. When needed I also help refine business models, explore revenue/monetization options, construct distribution strategies, and build customer acquisition plans. Technical Skills Summary :- • Good knowledge of Objective C & Swift programming language. • Very proficient in Web service integrations: SOAP, JSON, XML. • Good understanding of iOS Design Patterns. • Good knowledge of CoreData. • Experience in UI development. • Building interfaces via storyboard files, nibs, or programmatically. • Integrating third-party frameworks and libraries. • Memory management Process, ARC. • Familiar with iPad device orientations techniques and Localization. • Familiar with Apple’s provisioning profile and certificates. • Familiar with app store submission process. • Good problem solving skills and resourcefulness to find solutions through self-study and research. • Good experience working under pressure - individually and also as a team member.

Updated on May 23, 2022

Comments

  • Nidhi Tiwari
    Nidhi Tiwari about 2 years

    I tried lots of solution. I get some but they were using objective c code in somewhere. I need solution only in swift 4.2 and without any third party (like Alamofire).It is working fine using objective c classes .

    I have been able to make POST request with only, headers and other parameters and image as:

    Upload files using multipart request - Swift 4

    How to do multipart/form-data post request with Swift?

    Multipart-form (image,parameters,headers) Post request with Alamofire in swift

    Upload image with multipart form-data iOS in Swift

    How to upload images to a server in iOS with Swift?

    
    func sendFile(
        urlPath:String,
        fileName:String,
        data:NSData,
        completionHandler: (NSURLResponse!, NSData!, NSError!) -> Void){
    
            var url: NSURL = NSURL(string: urlPath)!
            var request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)
    
            request1.HTTPMethod = "POST"
    
            let boundary = generateBoundary()
            let fullData = photoDataToFormData(data,boundary:boundary,fileName:fileName)
    
            request1.setValue("multipart/form-data; boundary=" + boundary,
                forHTTPHeaderField: "Content-Type")
    
            // REQUIRED!
            request1.setValue(String(fullData.length), forHTTPHeaderField: "Content-Length")
    
            request1.HTTPBody = fullData
            request1.HTTPShouldHandleCookies = false
    
            let queue:NSOperationQueue = NSOperationQueue()
    
            NSURLConnection.sendAsynchronousRequest(
                request1,
                queue: queue,
                completionHandler:completionHandler)
    }
    
    // this is a very verbose version of that function
    // you can shorten it, but i left it as-is for clarity
    // and as an example
    func photoDataToFormData(data:NSData,boundary:String,fileName:String) -> NSData {
        var fullData = NSMutableData()
    
        // 1 - Boundary should start with --
        let lineOne = "--" + boundary + "\r\n"
        fullData.appendData(lineOne.dataUsingEncoding(
            NSUTF8StringEncoding,
            allowLossyConversion: false)!)
    
        // 2
        let lineTwo = "Content-Disposition: form-data; name=\"image\"; filename=\"" + fileName + "\"\r\n"
        NSLog(lineTwo)
        fullData.appendData(lineTwo.dataUsingEncoding(
            NSUTF8StringEncoding,
            allowLossyConversion: false)!)
    
        // 3
        let lineThree = "Content-Type: image/jpg\r\n\r\n"
        fullData.appendData(lineThree.dataUsingEncoding(
            NSUTF8StringEncoding,
            allowLossyConversion: false)!)
    
        // 4
        fullData.appendData(data)
    
        // 5
        let lineFive = "\r\n"
        fullData.appendData(lineFive.dataUsingEncoding(
            NSUTF8StringEncoding,
            allowLossyConversion: false)!)
    
        // 6 - The end. Notice -- at the start and at the end
        let lineSix = "--" + boundary + "--\r\n"
        fullData.appendData(lineSix.dataUsingEncoding(
            NSUTF8StringEncoding,
            allowLossyConversion: false)!)
    
        return fullData
    } 
    
    
  • Thet Htun
    Thet Htun over 3 years
    Thanks! Hope this help to anyone who might encounter similar silly mistakes 😄Forgot to close the request & had a few head scratch :) missed this part => data.append("\r\n--(boundary)--\r\n".data(using: .utf8)!).
  • Caterina Dmitrieva
    Caterina Dmitrieva over 2 years
    Thank you! worked with Django REST backend as well
  • Vinod Kumar
    Vinod Kumar over 2 years
    Welcome @CaterinaDmitrieva