# How to upload files with a multipart request in swift.

Recently I had to make a multipart request from an iOS app written in Swift.

The thing is, that there is not a standard way to make it easily, at least not without any third-party library. So, making some research I found this [blog post](https://theswiftdev.com/easy-multipart-file-upload-for-swift/).

The author explains really well how to build a multipart request through a self-written struct called `MultipartRequest`.

But in my case, it didn't work out of the box, I'm pretty sure it was because I'm using a newer version of Swift.

So, in order to make it work, I made a few changes.

## Creating the MultipartRequest struct

The principal change I made was to replace the type of the `data` property from `Data` to `NSMutableData`.

```swift
private var data: NSMutableData
```

This is because the compiler marked an error telling me that Data was not mutable.

Then I had to add an extension to `NSMutableData` instead to `Data`.

```swift
extension NSMutableData {
    func appendString(_ string: String) {
        if let data = string.data(using: .utf8) {
            self.append(data)
        }
    }
}
```

And finally modified the calculated property `httpBody` to cast the `NSMutableData` to `Data`.

```swift
public var httpBody: Data {
    data.appendString("--\(boundary)--")
    return data as Data
}
```

Here you can see the full struct:

```swift
import Foundation

public struct MultipartRequest {
    
    public let boundary: String
    
    private let separator: String = "\r\n"
    private var data: NSMutableData

    public init(boundary: String = UUID().uuidString) {
        self.boundary = boundary
        self.data = .init()
    }
    
    private mutating func appendBoundarySeparator() {
        data.appendString("--\(boundary)\(separator)")
    }
    
    private mutating func appendSeparator() {
        data.appendString(separator)
    }

    private func disposition(_ key: String) -> String {
        "Content-Disposition: form-data; name=\"\(key)\""
    }

    public mutating func add(
        key: String,
        value: String
    ) {
        appendBoundarySeparator()
        data.appendString(disposition(key) + separator)
        appendSeparator()
        data.appendString(value + separator)
    }

    public mutating func add(
        key: String,
        fileName: String,
        fileMimeType: String,
        fileData: Data
    ) {
        appendBoundarySeparator()
        data.appendString(disposition(key) + "; filename=\"\(fileName)\"" + separator)
        data.appendString("Content-Type: \(fileMimeType)" + separator + separator)
        data.append(fileData)
        appendSeparator()
    }

    public var httpContentTypeHeadeValue: String {
        "multipart/form-data; boundary=\(boundary)"
    }

    public var httpBody: Data {
        data.appendString("--\(boundary)--")
        return data as Data
    }
}

extension NSMutableData {
    func appendString(_ string: String) {
        if let data = string.data(using: .utf8) {
            self.append(data)
        }
    }
}
```

## Using the MultipartRequest struct.

Using this struct is very easy:

First, instantiate an object of the `MultipartRequest` struct.

```swift
var multipart = MultipartRequest()
```

Now you can add parameters to your request:

```swift
multipart.add(key: "name", value: "John")
```

And of course, you can add your file data (in this example it's an image):

```swift
multipart.add(
    key: "file",
    fileName: "test.png",
    fileMimeType: "image/png",
    fileData: imageData
)
```

Build your request.

```swift
let url = URL(string: "https://myurl")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// Here is how to use your struct to set your content-type header
request.setValue(multipart.httpContentTypeHeadeValue, forHTTPHeaderField: "Content-Type")
// set the body request
request.httpBody = multipart.httpBody
```

Finally, execute the request.

```swift
do {
  let (responseData, response) = try await  URLSession.shared.data(for: request)
  print((response as! HTTPURLResponse).statusCode)
  print(String(data: responseData, encoding: .utf8)!)
} catch {
  print ("Error")
}
```

Now you can upload files from your iOS app.

## Conclusion.

I created this [Github repository](https://github.com/raag/IOS_MULTIPART_REQUEST) as an example using an image picker on SwiftUI.

Share in the comments if it is useful or if you know another way to do it.

Thanks for reading.
