Get rid of some pointless mutable state

This commit is contained in:
Sami Samhuri 2020-10-20 21:09:53 -07:00
parent 0bcf8fe801
commit 012ff20c96
4 changed files with 23 additions and 26 deletions

View file

@ -12,17 +12,19 @@ Copy the files you want to use into your project, and then customize them to sui
Create an encoder and then add parts to it as needed: Create an encoder and then add parts to it as needed:
```Swift ```Swift
let encoder = MultipartFormEncoder()
encoder.addPart(.text(name: "email", text: "somebody@example.com"))
encoder.addPart(.text(name: "password", text: "secret"))
let avatarData = UIImage(from: somewhere).jpegData(compressionQuality: 1) let avatarData = UIImage(from: somewhere).jpegData(compressionQuality: 1)
encoder.addPart(.binary(name: "avatar", type: "image/jpeg", data: avatarData, filename: "avatar.jpg")) let encoder = MultipartFormEncoder()
let body = encoder.encode(parts: [
.text(name: "email", text: "somebody@example.com"),
.text(name: "password", text: "secret"),
.binary(name: "avatar", type: "image/jpeg", data: avatarData, filename: "avatar.jpg"),
])
``` ```
The entire form is encoded as `Data` in memory so you may not want to use this for more than a few megabytes at a time: The entire form is encoded as `Data` in memory so you may not want to use this for more than a few megabytes at a time:
```Swift ```Swift
let body = encoder.encode() let body = encoder.encode(parts: [/* ... */])
var request = URLRequest(url: URL(string: "https://example.com/accounts")!) var request = URLRequest(url: URL(string: "https://example.com/accounts")!)
request.httpMethod = "POST" request.httpMethod = "POST"
request.httpBody = body.data request.httpBody = body.data

View file

@ -55,17 +55,11 @@ extension MultipartFormEncoder {
final class MultipartFormEncoder { final class MultipartFormEncoder {
let boundary: String let boundary: String
private var parts: [Part] = []
init(boundary: String? = nil) { init(boundary: String? = nil) {
self.boundary = boundary ?? "LifeIsMadeOfSeconds-\(UUID().uuidString)" self.boundary = boundary ?? "LifeIsMadeOfSeconds-\(UUID().uuidString)"
} }
func addPart(_ part: Part) { func encode(parts: [Part]) -> Body {
parts.append(part)
}
func encode() -> Body {
var bodyData = Data() var bodyData = Data()
for part in parts { for part in parts {
// Header // Header

View file

@ -37,10 +37,7 @@ final class RequestBuilder {
case .multipart: case .multipart:
let encoder = MultipartFormEncoder() let encoder = MultipartFormEncoder()
for part in request.parts { let body = encoder.encode(parts: request.parts)
encoder.addPart(part)
}
let body = encoder.encode()
result.addValue(body.contentType, forHTTPHeaderField: "Content-Type") result.addValue(body.contentType, forHTTPHeaderField: "Content-Type")
result.addValue("\(body.contentLength)", forHTTPHeaderField: "Content-Length") result.addValue("\(body.contentLength)", forHTTPHeaderField: "Content-Length")
result.httpBody = body.data result.httpBody = body.data

View file

@ -30,15 +30,14 @@ class MultipartFormEncoderTests: XCTestCase {
} }
func testEncodeNothing() throws { func testEncodeNothing() throws {
let body = subject.encode() let body = subject.encode(parts: [])
XCTAssertEqual(body.contentType, "multipart/form-data; boundary=\"SuperAwesomeBoundary\"") XCTAssertEqual(body.contentType, "multipart/form-data; boundary=\"SuperAwesomeBoundary\"")
AssertBodyEqual(body.data, "--SuperAwesomeBoundary--") AssertBodyEqual(body.data, "--SuperAwesomeBoundary--")
} }
func testEncodeText() throws { func testEncodeText() throws {
subject.addPart(.text(name: "name", value: "Tina"))
AssertBodyEqual( AssertBodyEqual(
subject.encode().data, subject.encode(parts: [.text(name: "name", value: "Tina")]).data,
[ [
"--SuperAwesomeBoundary", "--SuperAwesomeBoundary",
"Content-Disposition: form-data; name=\"name\"", "Content-Disposition: form-data; name=\"name\"",
@ -50,9 +49,11 @@ class MultipartFormEncoderTests: XCTestCase {
} }
func testEncodeData() throws { func testEncodeData() throws {
subject.addPart(.binary(name: "video", data: Data("phony video data".utf8), type: "video/mp4", filename: "LiesSex&VideoTape.mp4")) let data = Data("phony video data".utf8)
AssertBodyEqual( AssertBodyEqual(
subject.encode().data, subject.encode(parts: [
.binary(name: "video", data: data, type: "video/mp4", filename: "LiesSex&VideoTape.mp4"),
]).data,
[ [
"--SuperAwesomeBoundary", "--SuperAwesomeBoundary",
"Content-Disposition: form-data; name=\"video\"; filename=\"LiesSex&VideoTape.mp4\"", "Content-Disposition: form-data; name=\"video\"; filename=\"LiesSex&VideoTape.mp4\"",
@ -66,12 +67,15 @@ class MultipartFormEncoderTests: XCTestCase {
} }
func testEncodeEverything() throws { func testEncodeEverything() throws {
subject.addPart(.text(name: "name", value: "Queso")) let imageData = Data("phony image data".utf8)
subject.addPart(.binary(name: "image", data: Data("phony image data".utf8), type: "image/jpeg", filename: "feltcute.jpg")) let videoData = Data("phony video data".utf8)
subject.addPart(.text(name: "spot", value: "top of the bbq"))
subject.addPart(.binary(name: "video", data: Data("phony video data".utf8), type: "video/mp4", filename: "LiesSex&VideoTape.mp4"))
AssertBodyEqual( AssertBodyEqual(
subject.encode().data, subject.encode(parts: [
.text(name: "name", value: "Queso"),
.binary(name: "image", data: imageData, type: "image/jpeg", filename: "feltcute.jpg"),
.text(name: "spot", value: "top of the bbq"),
.binary(name: "video", data: videoData, type: "video/mp4", filename: "LiesSex&VideoTape.mp4"),
]).data,
[ [
"--SuperAwesomeBoundary", "--SuperAwesomeBoundary",
"Content-Disposition: form-data; name=\"name\"", "Content-Disposition: form-data; name=\"name\"",