Simple multipart file add for Swift


I consider that you have already heard in regards to the well-known multipart-data add method that everybody likes to add recordsdata and submit kind information, but when not, hopefully this text will provide help to somewhat bit to grasp this stuff higher.

Let’s begin with some principle. Don’t fret, it is only one hyperlink, in regards to the multipart/form-data content material sort specification. To shortly summarize it first I would wish to let you know just a few phrases about how the HTTP layer works. In a nutshell, you ship some information with some headers (give it some thought as a key-value consumer information object) to a given URL utilizing a technique and as a response you will get again a standing code, some headers and possibly some type of response information too. 🥜

  • HTTP request = Technique + URL + Headers + Physique (request information)
  • HTTP response = Standing code + Headers + Physique (response information)

The request methodology & URL is fairly easy, the fascinating half is whenever you specify the Content material-Kind HTTP header, in our case the multipart/form-data;boundary="xxx" worth means, that we’ll ship a request physique utilizing a number of elements and we’ll use the “xxx” boundary string as a separator between the elements. Oh, by the best way every half can have it is personal sort and identify, we’ll use the Content material-Disposition: form-data; identify="field1" line to let the server find out about these fields, earlier than we truly ship the precise content material worth.

That is greater than sufficient principle for now, let me snow you the way we are able to implement all of this utilizing Swift 5. Initially, we want to have the ability to append string values to a Knowledge object, so we’ll prolong Knowledge sort with an ‘append string utilizing encoding’ methodology:

import Basis

public extension Knowledge {

    mutating func append(
        _ string: String,
        encoding: String.Encoding = .utf8
    ) {
        guard let information = string.information(utilizing: encoding) else {
            return
        }
        append(information)
    }
}

Subsequent, we’d like one thing that may assemble the HTTP multipart physique information, for this objective we’ll construct a MultipartRequest object. We will set the boundary after we init this object and we’ll append the elements wanted to assemble the HTTP physique information.

The personal strategies will assist to assemble every part, we merely append string values to the personal information object that holds our information construction. The general public API solely consists of two add capabilities that you need to use to append a key-value based mostly kind subject or a complete file utilizing its information. 👍

public struct MultipartRequest {
    
    public let boundary: String
    
    personal let separator: String = "rn"
    personal var information: Knowledge

    public init(boundary: String = UUID().uuidString) {
        self.boundary = boundary
        self.information = .init()
    }
    
    personal mutating func appendBoundarySeparator() {
        information.append("--(boundary)(separator)")
    }
    
    personal mutating func appendSeparator() {
        information.append(separator)
    }

    personal func disposition(_ key: String) -> String {
        "Content material-Disposition: form-data; identify="(key)""
    }

    public mutating func add(
        key: String,
        worth: String
    ) {
        appendBoundarySeparator()
        information.append(disposition(key) + separator)
        appendSeparator()
        information.append(worth + separator)
    }

    public mutating func add(
        key: String,
        fileName: String,
        fileMimeType: String,
        fileData: Knowledge
    ) {
        appendBoundarySeparator()
        information.append(disposition(key) + "; filename="(fileName)"" + separator)
        information.append("Content material-Kind: (fileMimeType)" + separator + separator)
        information.append(fileData)
        appendSeparator()
    }

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

    public var httpBody: Knowledge {
        var bodyData = information
        bodyData.append("--(boundary)--")
        return bodyData
    }
}

The final remaining two public variables are helpers to simply get again the HTTP associated content material sort header worth utilizing the right boundary and the whole information object that you need to to ship to the server. Here is how one can assemble the HTTP URLRequest utilizing the multipart struct.

var multipart = MultipartRequest()
for subject in [
    "firstName": "John",
    "lastName": "Doe"
] {
    multipart.add(key: subject.key, worth: subject.worth)
}

multipart.add(
    key: "file",
    fileName: "pic.jpg",
    fileMimeType: "picture/png",
    fileData: "fake-image-data".information(utilizing: .utf8)!
)


let url = URL(string: "https://httpbin.org/publish")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue(multipart.httpContentTypeHeadeValue, forHTTPHeaderField: "Content material-Kind")
request.httpBody = multipart.httpBody


let (information, response) = strive await URLSession.shared.information(for: request)

print((response as! HTTPURLResponse).statusCode)
print(String(information: information, encoding: .utf8)!)

As you possibly can see it is comparatively easy, you simply add the shape fields and the recordsdata that you simply wish to add, and get again the HTTP associated values utilizing the helper API. I hope this text will provide help to to simulate kind submissions utilizing multipart requests with out trouble. 😊

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles