[Swift] fix URLSession file upload (#5546)

* [swift5] - fix URLSession file upload

* [swift5] - fix URLSession file upload

* [swift5] fix file upload

* [swift5] - fix URLSession file upload

* [swift] add unit tests for file upload

* [swift] update samples copyright
This commit is contained in:
Bruno Coelho
2020-03-13 14:04:57 +00:00
committed by GitHub
parent f22efe3c62
commit 86159cba49
40 changed files with 1410 additions and 306 deletions

View File

@@ -29,7 +29,7 @@ private var urlSessionStore = SynchronizedDictionary<String, URLSession>()
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
observation?.invalidate()
}
// swiftlint:disable:next weak_delegate
@@ -84,7 +84,7 @@ private var urlSessionStore = SynchronizedDictionary<String, URLSession>()
guard let url = URL(string: URLString) else {
throw DownloadException.requestMissingURL
}
var originalRequest = URLRequest(url: url)
originalRequest.httpMethod = method.rawValue
@@ -110,9 +110,9 @@ private var urlSessionStore = SynchronizedDictionary<String, URLSession>()
let parameters: [String: Any] = self.parameters ?? [:]
let fileKeys = parameters.filter { $1 is NSURL }
let fileKeys = parameters.filter { $1 is URL }
.map { $0.0 }
let encoding: ParameterEncoding
if fileKeys.count > 0 {
encoding = FileUploadEncoding(contentTypeForFormPart: contentTypeForFormPart(fileURL:))
@@ -135,13 +135,13 @@ private var urlSessionStore = SynchronizedDictionary<String, URLSession>()
let request = try createURLRequest(urlSession: urlSession, method: xMethod, encoding: encoding, headers: headers)
let dataTask = urlSession.dataTask(with: request) { [weak self] data, response, error in
guard let self = self else { return }
if let taskCompletionShouldRetry = self.taskCompletionShouldRetry {
taskCompletionShouldRetry(data, response, error) { [weak self] shouldRetry in
guard let self = self else { return }
if shouldRetry {
@@ -170,7 +170,7 @@ private var urlSessionStore = SynchronizedDictionary<String, URLSession>()
}
dataTask.resume()
} catch {
apiResponseQueue.async {
cleanupRequest()
@@ -266,7 +266,7 @@ private var urlSessionStore = SynchronizedDictionary<String, URLSession>()
let items = contentDisposition.components(separatedBy: ";")
var filename : String? = nil
var filename: String?
for contentItem in items {
@@ -366,7 +366,7 @@ private var urlSessionStore = SynchronizedDictionary<String, URLSession>()
fileprivate class SessionDelegate: NSObject, URLSessionDelegate, URLSessionDataDelegate {
var credential: URLCredential?
var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
@@ -430,76 +430,117 @@ fileprivate class URLEncoding: ParameterEncoding {
}
fileprivate class FileUploadEncoding: ParameterEncoding {
let contentTypeForFormPart: (_ fileURL: URL) -> String?
init(contentTypeForFormPart: @escaping (_ fileURL: URL) -> String?) {
self.contentTypeForFormPart = contentTypeForFormPart
}
func encode(_ urlRequest: URLRequest, with parameters: [String : Any]?) throws -> URLRequest {
func encode(_ urlRequest: URLRequest, with parameters: [String: Any]?) throws -> URLRequest {
var urlRequest = urlRequest
for (k, v) in parameters ?? [:] {
switch v {
guard let parameters = parameters, !parameters.isEmpty else {
return urlRequest
}
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
for (key, value) in parameters {
switch value {
case let fileURL as URL:
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: fileURL.lastPathComponent, data: fileData, mimeType: mimetype)
urlRequest = try configureFileUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
fileURL: fileURL
)
case let string as String:
if let data = string.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
case let number as NSNumber:
if let data = number.stringValue.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
default:
fatalError("Unprocessable value \(v) with key \(k)")
fatalError("Unprocessable value \(value) with key \(key)")
}
}
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)--")
urlRequest.httpBody = body
return urlRequest
}
private func configureFileUploadRequest(urlRequest: URLRequest, name: String, data: Data, mimeType: String?) -> URLRequest {
private func configureFileUploadRequest(urlRequest: URLRequest, boundary: String, name: String, fileURL: URL) throws -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody ?? Data()
var body = urlRequest.httpBody.orEmpty
// https://stackoverflow.com/a/26163136/976628
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
let fileName = fileURL.lastPathComponent
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(fileData)
body.append("\r\n\r\n")
if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
urlRequest.httpBody = body
return urlRequest
}
private func configureDataUploadRequest(urlRequest: URLRequest, boundary: String, name: String, data: Data) -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
body.append(data)
body.append("\r\n\r\n")
urlRequest.httpBody = body
return urlRequest
}
func mimeType(for url: URL) -> String {
let pathExtension = url.pathExtension
@@ -510,15 +551,15 @@ fileprivate class FileUploadEncoding: ParameterEncoding {
}
return "application/octet-stream"
}
}
fileprivate extension Data {
/// Append string to NSMutableData
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to Data, and then add that data to the Data, this wraps it in a nice convenient little extension to Data. This converts using UTF-8.
///
/// - parameter string: The string to be added to the `NSMutableData`.
/// - parameter string: The string to be added to the `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
@@ -527,4 +568,10 @@ fileprivate extension Data {
}
}
fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}
extension JSONDataEncoding: ParameterEncoding {}

View File

@@ -20,4 +20,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 509bec696cc1d8641751b52e4fe4bef04ac4542c
COCOAPODS: 1.8.4
COCOAPODS: 1.9.0

View File

@@ -17,6 +17,8 @@
6D4EFBB51C693BE200B96B06 /* PetAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB41C693BE200B96B06 /* PetAPITests.swift */; };
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */; };
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */; };
A5EA12542419387200E30FC3 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA12522419387100E30FC3 /* FileUtils.swift */; };
A5EA12552419387200E30FC3 /* UIImage+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA12532419387100E30FC3 /* UIImage+Extras.swift */; };
FB5CCC7EFA680BB2746B695B /* Pods_SwaggerClientTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83FDC034BBA2A07AE9975250 /* Pods_SwaggerClientTests.framework */; };
/* End PBXBuildFile section */
@@ -47,6 +49,8 @@
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserAPITests.swift; sourceTree = "<group>"; };
7F98CC8B18E5FA9213F6A68D /* Pods_SwaggerClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwaggerClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
83FDC034BBA2A07AE9975250 /* Pods_SwaggerClientTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwaggerClientTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A5EA12522419387100E30FC3 /* FileUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
A5EA12532419387100E30FC3 /* UIImage+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extras.swift"; sourceTree = "<group>"; };
ACB80AC61FA8D8916D4559AA /* Pods-SwaggerClient.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClient.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClient/Pods-SwaggerClient.release.xcconfig"; sourceTree = "<group>"; };
C07EC0A94AA0F86D60668B32 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E43FC34A9681D65ED44EE914 /* Pods-SwaggerClientTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClientTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClientTests/Pods-SwaggerClientTests.debug.xcconfig"; sourceTree = "<group>"; };
@@ -135,6 +139,8 @@
6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */,
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */,
1A501F47219C3DC600F372F6 /* DateFormatTests.swift */,
A5EA12522419387100E30FC3 /* FileUtils.swift */,
A5EA12532419387100E30FC3 /* UIImage+Extras.swift */,
);
path = SwaggerClientTests;
sourceTree = "<group>";
@@ -323,8 +329,10 @@
files = (
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */,
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */,
A5EA12552419387200E30FC3 /* UIImage+Extras.swift in Sources */,
1A501F48219C3DC600F372F6 /* DateFormatTests.swift in Sources */,
6D4EFBB51C693BE200B96B06 /* PetAPITests.swift in Sources */,
A5EA12542419387200E30FC3 /* FileUtils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -0,0 +1,49 @@
//
// FileUtils.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
class FileUtils {
static func saveImage(imageName: String, image: UIImage) -> URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
//Checks if file exists, removes it if so.
deleteFile(fileURL: fileURL)
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
return nil
}
return fileURL
}
@discardableResult
static func deleteFile(fileURL: URL) -> Bool {
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
return true
} catch let removeError {
print("couldn't remove file at path", removeError)
return false
}
}
return false
}
}

View File

@@ -61,8 +61,35 @@ class PetAPITests: XCTestCase {
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test3UploadFile() {
let expectation = self.expectation(description: "testUploadFile")
func test3DeletePet() {
let imageName = UUID().uuidString + ".png"
guard
let image = UIImage(color: .red, size: CGSize(width: 10, height: 10)),
let imageURL = FileUtils.saveImage(imageName: imageName, image: image)
else {
fatalError()
}
PetAPI.uploadFile(petId: 1000, additionalMetadata: "additionalMetadata", file: imageURL) { (_, error) in
guard error == nil else {
FileUtils.deleteFile(fileURL: imageURL)
XCTFail("error uploading file")
return
}
FileUtils.deleteFile(fileURL: imageURL)
expectation.fulfill()
}
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test4DeletePet() {
let expectation = self.expectation(description: "testDeletePet")
PetAPI.deletePet(petId: 1000) { (_, error) in

View File

@@ -0,0 +1,23 @@
//
// UIImage+Extras.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
extension UIImage {
convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
}

View File

@@ -29,7 +29,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
observation?.invalidate()
}
// swiftlint:disable:next weak_delegate
@@ -110,7 +110,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
let parameters: [String: Any] = self.parameters ?? [:]
let fileKeys = parameters.filter { $1 is NSURL }
let fileKeys = parameters.filter { $1 is URL }
.map { $0.0 }
let encoding: ParameterEncoding
@@ -441,58 +441,99 @@ private class FileUploadEncoding: ParameterEncoding {
var urlRequest = urlRequest
for (k, v) in parameters ?? [:] {
switch v {
guard let parameters = parameters, !parameters.isEmpty else {
return urlRequest
}
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
for (key, value) in parameters {
switch value {
case let fileURL as URL:
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: fileURL.lastPathComponent, data: fileData, mimeType: mimetype)
urlRequest = try configureFileUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
fileURL: fileURL
)
case let string as String:
if let data = string.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
case let number as NSNumber:
if let data = number.stringValue.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
default:
fatalError("Unprocessable value \(v) with key \(k)")
fatalError("Unprocessable value \(value) with key \(key)")
}
}
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)--")
urlRequest.httpBody = body
return urlRequest
}
private func configureFileUploadRequest(urlRequest: URLRequest, name: String, data: Data, mimeType: String?) -> URLRequest {
private func configureFileUploadRequest(urlRequest: URLRequest, boundary: String, name: String, fileURL: URL) throws -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody ?? Data()
var body = urlRequest.httpBody.orEmpty
// https://stackoverflow.com/a/26163136/976628
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
let fileName = fileURL.lastPathComponent
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")
if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(fileData)
body.append("\r\n\r\n")
urlRequest.httpBody = body
return urlRequest
}
private func configureDataUploadRequest(urlRequest: URLRequest, boundary: String, name: String, data: Data) -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
body.append("\r\n\r\n")
urlRequest.httpBody = body
@@ -514,11 +555,11 @@ private class FileUploadEncoding: ParameterEncoding {
}
fileprivate extension Data {
/// Append string to NSMutableData
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to Data, and then add that data to the Data, this wraps it in a nice convenient little extension to Data. This converts using UTF-8.
///
/// - parameter string: The string to be added to the `NSMutableData`.
/// - parameter string: The string to be added to the `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
@@ -527,4 +568,10 @@ fileprivate extension Data {
}
}
fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}
extension JSONDataEncoding: ParameterEncoding {}

View File

@@ -13,4 +13,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 509bec696cc1d8641751b52e4fe4bef04ac4542c
COCOAPODS: 1.8.4
COCOAPODS: 1.9.0

View File

@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
A5EA12582419390400E30FC3 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA12562419390400E30FC3 /* FileUtils.swift */; };
A5EA12592419390400E30FC3 /* UIImage+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA12572419390400E30FC3 /* UIImage+Extras.swift */; };
B024164FBFF71BF644D4419A /* Pods_SwaggerClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 177A58DD5CF63F2989335DCC /* Pods_SwaggerClient.framework */; };
B1D0246C8960F47A60098F37 /* Pods_SwaggerClientTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F96A0131101344CC5406CB3 /* Pods_SwaggerClientTests.framework */; };
B596E4BD205657A500B46F03 /* APIHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B596E4BC205657A500B46F03 /* APIHelperTests.swift */; };
@@ -36,6 +38,8 @@
4EF2021609D112A6F5AE0F55 /* Pods-SwaggerClientTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClientTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClientTests/Pods-SwaggerClientTests.debug.xcconfig"; sourceTree = "<group>"; };
6F96A0131101344CC5406CB3 /* Pods_SwaggerClientTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwaggerClientTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8D99518E8E05FD856A952698 /* Pods-SwaggerClient.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClient.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClient/Pods-SwaggerClient.debug.xcconfig"; sourceTree = "<group>"; };
A5EA12562419390400E30FC3 /* FileUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
A5EA12572419390400E30FC3 /* UIImage+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extras.swift"; sourceTree = "<group>"; };
B596E4BC205657A500B46F03 /* APIHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIHelperTests.swift; sourceTree = "<group>"; };
EAEC0BBE1D4E30CE00C908A3 /* SwaggerClient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwaggerClient.app; sourceTree = BUILT_PRODUCTS_DIR; };
EAEC0BC11D4E30CE00C908A3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -133,6 +137,8 @@
EAEC0BE51D4E379000C908A3 /* StoreAPITests.swift */,
EAEC0BE71D4E38CB00C908A3 /* UserAPITests.swift */,
B596E4BC205657A500B46F03 /* APIHelperTests.swift */,
A5EA12562419390400E30FC3 /* FileUtils.swift */,
A5EA12572419390400E30FC3 /* UIImage+Extras.swift */,
);
path = SwaggerClientTests;
sourceTree = "<group>";
@@ -311,8 +317,10 @@
files = (
B596E4BD205657A500B46F03 /* APIHelperTests.swift in Sources */,
EAEC0BE81D4E38CB00C908A3 /* UserAPITests.swift in Sources */,
A5EA12592419390400E30FC3 /* UIImage+Extras.swift in Sources */,
EAEC0BE61D4E379000C908A3 /* StoreAPITests.swift in Sources */,
EAEC0BE41D4E330700C908A3 /* PetAPITests.swift in Sources */,
A5EA12582419390400E30FC3 /* FileUtils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -0,0 +1,49 @@
//
// FileUtils.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
class FileUtils {
static func saveImage(imageName: String, image: UIImage) -> URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
//Checks if file exists, removes it if so.
deleteFile(fileURL: fileURL)
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
return nil
}
return fileURL
}
@discardableResult
static func deleteFile(fileURL: URL) -> Bool {
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
return true
} catch let removeError {
print("couldn't remove file at path", removeError)
return false
}
}
return false
}
}

View File

@@ -76,7 +76,35 @@ class PetAPITests: XCTestCase {
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test3DeletePet() {
func test3UploadFile() {
let expectation = self.expectation(description: "testUploadFile")
let imageName = UUID().uuidString + ".png"
guard
let image = UIImage(color: .red, size: CGSize(width: 10, height: 10)),
let imageURL = FileUtils.saveImage(imageName: imageName, image: image)
else {
fatalError()
}
PetAPI.uploadFile(petId: 1000, additionalMetadata: "additionalMetadata", file: imageURL).sink(receiveCompletion: { (completion) in
switch completion {
case .failure:
FileUtils.deleteFile(fileURL: imageURL)
XCTFail("error uploading file")
case .finished:()
}
}, receiveValue: { _ in
FileUtils.deleteFile(fileURL: imageURL)
expectation.fulfill()
}).store(in: &anyCancellables)
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test4DeletePet() {
let expectation = self.expectation(description: "testDeletePet")
PetAPI.deletePet(petId: 1000).sink(receiveCompletion: { (completion) in
switch completion {

View File

@@ -0,0 +1,23 @@
//
// UIImage+Extras.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
extension UIImage {
convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
}

View File

@@ -29,7 +29,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
observation?.invalidate()
}
// swiftlint:disable:next weak_delegate
@@ -110,7 +110,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
let parameters: [String: Any] = self.parameters ?? [:]
let fileKeys = parameters.filter { $1 is NSURL }
let fileKeys = parameters.filter { $1 is URL }
.map { $0.0 }
let encoding: ParameterEncoding
@@ -441,58 +441,99 @@ private class FileUploadEncoding: ParameterEncoding {
var urlRequest = urlRequest
for (k, v) in parameters ?? [:] {
switch v {
guard let parameters = parameters, !parameters.isEmpty else {
return urlRequest
}
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
for (key, value) in parameters {
switch value {
case let fileURL as URL:
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: fileURL.lastPathComponent, data: fileData, mimeType: mimetype)
urlRequest = try configureFileUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
fileURL: fileURL
)
case let string as String:
if let data = string.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
case let number as NSNumber:
if let data = number.stringValue.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
default:
fatalError("Unprocessable value \(v) with key \(k)")
fatalError("Unprocessable value \(value) with key \(key)")
}
}
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)--")
urlRequest.httpBody = body
return urlRequest
}
private func configureFileUploadRequest(urlRequest: URLRequest, name: String, data: Data, mimeType: String?) -> URLRequest {
private func configureFileUploadRequest(urlRequest: URLRequest, boundary: String, name: String, fileURL: URL) throws -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody ?? Data()
var body = urlRequest.httpBody.orEmpty
// https://stackoverflow.com/a/26163136/976628
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
let fileName = fileURL.lastPathComponent
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")
if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(fileData)
body.append("\r\n\r\n")
urlRequest.httpBody = body
return urlRequest
}
private func configureDataUploadRequest(urlRequest: URLRequest, boundary: String, name: String, data: Data) -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
body.append("\r\n\r\n")
urlRequest.httpBody = body
@@ -514,11 +555,11 @@ private class FileUploadEncoding: ParameterEncoding {
}
fileprivate extension Data {
/// Append string to NSMutableData
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to Data, and then add that data to the Data, this wraps it in a nice convenient little extension to Data. This converts using UTF-8.
///
/// - parameter string: The string to be added to the `NSMutableData`.
/// - parameter string: The string to be added to the `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
@@ -527,4 +568,10 @@ fileprivate extension Data {
}
}
fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}
extension JSONDataEncoding: ParameterEncoding {}

View File

@@ -13,4 +13,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 509bec696cc1d8641751b52e4fe4bef04ac4542c
COCOAPODS: 1.8.4
COCOAPODS: 1.9.0

View File

@@ -17,6 +17,8 @@
6D4EFBB51C693BE200B96B06 /* PetAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB41C693BE200B96B06 /* PetAPITests.swift */; };
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */; };
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */; };
A5EA124F241901AA00E30FC3 /* UIImage+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA124E241901AA00E30FC3 /* UIImage+Extras.swift */; };
A5EA1251241905F000E30FC3 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA1250241905F000E30FC3 /* FileUtils.swift */; };
FB5CCC7EFA680BB2746B695B /* Pods_SwaggerClientTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83FDC034BBA2A07AE9975250 /* Pods_SwaggerClientTests.framework */; };
/* End PBXBuildFile section */
@@ -47,6 +49,8 @@
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserAPITests.swift; sourceTree = "<group>"; };
7F98CC8B18E5FA9213F6A68D /* Pods_SwaggerClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwaggerClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
83FDC034BBA2A07AE9975250 /* Pods_SwaggerClientTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwaggerClientTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A5EA124E241901AA00E30FC3 /* UIImage+Extras.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extras.swift"; sourceTree = "<group>"; };
A5EA1250241905F000E30FC3 /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
ACB80AC61FA8D8916D4559AA /* Pods-SwaggerClient.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClient.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClient/Pods-SwaggerClient.release.xcconfig"; sourceTree = "<group>"; };
C07EC0A94AA0F86D60668B32 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E43FC34A9681D65ED44EE914 /* Pods-SwaggerClientTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClientTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClientTests/Pods-SwaggerClientTests.debug.xcconfig"; sourceTree = "<group>"; };
@@ -135,6 +139,8 @@
6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */,
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */,
1A501F47219C3DC600F372F6 /* DateFormatTests.swift */,
A5EA124E241901AA00E30FC3 /* UIImage+Extras.swift */,
A5EA1250241905F000E30FC3 /* FileUtils.swift */,
);
path = SwaggerClientTests;
sourceTree = "<group>";
@@ -322,7 +328,9 @@
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */,
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */,
1A501F48219C3DC600F372F6 /* DateFormatTests.swift in Sources */,
A5EA1251241905F000E30FC3 /* FileUtils.swift in Sources */,
6D4EFBB51C693BE200B96B06 /* PetAPITests.swift in Sources */,
A5EA124F241901AA00E30FC3 /* UIImage+Extras.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -0,0 +1,49 @@
//
// FileUtils.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
class FileUtils {
static func saveImage(imageName: String, image: UIImage) -> URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
//Checks if file exists, removes it if so.
deleteFile(fileURL: fileURL)
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
return nil
}
return fileURL
}
@discardableResult
static func deleteFile(fileURL: URL) -> Bool {
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
return true
} catch let removeError {
print("couldn't remove file at path", removeError)
return false
}
}
return false
}
}

View File

@@ -62,7 +62,33 @@ class PetAPITests: XCTestCase {
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test3DeletePet() {
func test3UploadFile() {
let expectation = self.expectation(description: "testUploadFile")
let imageName = UUID().uuidString + ".png"
guard
let image = UIImage(color: .red, size: CGSize(width: 10, height: 10)),
let imageURL = FileUtils.saveImage(imageName: imageName, image: image)
else {
fatalError()
}
PetAPI.uploadFile(petId: 1000, additionalMetadata: "additionalMetadata", file: imageURL) { (_, error) in
guard error == nil else {
FileUtils.deleteFile(fileURL: imageURL)
XCTFail("error uploading file")
return
}
FileUtils.deleteFile(fileURL: imageURL)
expectation.fulfill()
}
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test4DeletePet() {
let expectation = self.expectation(description: "testDeletePet")
PetAPI.deletePet(petId: 1000) { (_, error) in

View File

@@ -0,0 +1,23 @@
//
// UIImage+Extras.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
extension UIImage {
convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
}

View File

@@ -29,7 +29,7 @@ internal class URLSessionRequestBuilder<T>: RequestBuilder<T> {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
observation?.invalidate()
}
// swiftlint:disable:next weak_delegate
@@ -110,7 +110,7 @@ internal class URLSessionRequestBuilder<T>: RequestBuilder<T> {
let parameters: [String: Any] = self.parameters ?? [:]
let fileKeys = parameters.filter { $1 is NSURL }
let fileKeys = parameters.filter { $1 is URL }
.map { $0.0 }
let encoding: ParameterEncoding
@@ -441,58 +441,99 @@ private class FileUploadEncoding: ParameterEncoding {
var urlRequest = urlRequest
for (k, v) in parameters ?? [:] {
switch v {
guard let parameters = parameters, !parameters.isEmpty else {
return urlRequest
}
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
for (key, value) in parameters {
switch value {
case let fileURL as URL:
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: fileURL.lastPathComponent, data: fileData, mimeType: mimetype)
urlRequest = try configureFileUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
fileURL: fileURL
)
case let string as String:
if let data = string.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
case let number as NSNumber:
if let data = number.stringValue.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
default:
fatalError("Unprocessable value \(v) with key \(k)")
fatalError("Unprocessable value \(value) with key \(key)")
}
}
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)--")
urlRequest.httpBody = body
return urlRequest
}
private func configureFileUploadRequest(urlRequest: URLRequest, name: String, data: Data, mimeType: String?) -> URLRequest {
private func configureFileUploadRequest(urlRequest: URLRequest, boundary: String, name: String, fileURL: URL) throws -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody ?? Data()
var body = urlRequest.httpBody.orEmpty
// https://stackoverflow.com/a/26163136/976628
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
let fileName = fileURL.lastPathComponent
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")
if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(fileData)
body.append("\r\n\r\n")
urlRequest.httpBody = body
return urlRequest
}
private func configureDataUploadRequest(urlRequest: URLRequest, boundary: String, name: String, data: Data) -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
body.append("\r\n\r\n")
urlRequest.httpBody = body
@@ -514,11 +555,11 @@ private class FileUploadEncoding: ParameterEncoding {
}
fileprivate extension Data {
/// Append string to NSMutableData
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to Data, and then add that data to the Data, this wraps it in a nice convenient little extension to Data. This converts using UTF-8.
///
/// - parameter string: The string to be added to the `NSMutableData`.
/// - parameter string: The string to be added to the `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
@@ -527,4 +568,10 @@ fileprivate extension Data {
}
}
fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}
extension JSONDataEncoding: ParameterEncoding {}

View File

@@ -29,7 +29,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
observation?.invalidate()
}
// swiftlint:disable:next weak_delegate
@@ -110,7 +110,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
let parameters: [String: Any] = self.parameters ?? [:]
let fileKeys = parameters.filter { $1 is NSURL }
let fileKeys = parameters.filter { $1 is URL }
.map { $0.0 }
let encoding: ParameterEncoding
@@ -441,58 +441,99 @@ private class FileUploadEncoding: ParameterEncoding {
var urlRequest = urlRequest
for (k, v) in parameters ?? [:] {
switch v {
guard let parameters = parameters, !parameters.isEmpty else {
return urlRequest
}
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
for (key, value) in parameters {
switch value {
case let fileURL as URL:
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: fileURL.lastPathComponent, data: fileData, mimeType: mimetype)
urlRequest = try configureFileUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
fileURL: fileURL
)
case let string as String:
if let data = string.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
case let number as NSNumber:
if let data = number.stringValue.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
default:
fatalError("Unprocessable value \(v) with key \(k)")
fatalError("Unprocessable value \(value) with key \(key)")
}
}
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)--")
urlRequest.httpBody = body
return urlRequest
}
private func configureFileUploadRequest(urlRequest: URLRequest, name: String, data: Data, mimeType: String?) -> URLRequest {
private func configureFileUploadRequest(urlRequest: URLRequest, boundary: String, name: String, fileURL: URL) throws -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody ?? Data()
var body = urlRequest.httpBody.orEmpty
// https://stackoverflow.com/a/26163136/976628
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
let fileName = fileURL.lastPathComponent
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")
if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(fileData)
body.append("\r\n\r\n")
urlRequest.httpBody = body
return urlRequest
}
private func configureDataUploadRequest(urlRequest: URLRequest, boundary: String, name: String, data: Data) -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
body.append("\r\n\r\n")
urlRequest.httpBody = body
@@ -514,11 +555,11 @@ private class FileUploadEncoding: ParameterEncoding {
}
fileprivate extension Data {
/// Append string to NSMutableData
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to Data, and then add that data to the Data, this wraps it in a nice convenient little extension to Data. This converts using UTF-8.
///
/// - parameter string: The string to be added to the `NSMutableData`.
/// - parameter string: The string to be added to the `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
@@ -527,4 +568,10 @@ fileprivate extension Data {
}
}
fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}
extension JSONDataEncoding: ParameterEncoding {}

View File

@@ -29,7 +29,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
observation?.invalidate()
}
// swiftlint:disable:next weak_delegate
@@ -110,7 +110,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
let parameters: [String: Any] = self.parameters ?? [:]
let fileKeys = parameters.filter { $1 is NSURL }
let fileKeys = parameters.filter { $1 is URL }
.map { $0.0 }
let encoding: ParameterEncoding
@@ -441,58 +441,99 @@ private class FileUploadEncoding: ParameterEncoding {
var urlRequest = urlRequest
for (k, v) in parameters ?? [:] {
switch v {
guard let parameters = parameters, !parameters.isEmpty else {
return urlRequest
}
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
for (key, value) in parameters {
switch value {
case let fileURL as URL:
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: fileURL.lastPathComponent, data: fileData, mimeType: mimetype)
urlRequest = try configureFileUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
fileURL: fileURL
)
case let string as String:
if let data = string.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
case let number as NSNumber:
if let data = number.stringValue.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
default:
fatalError("Unprocessable value \(v) with key \(k)")
fatalError("Unprocessable value \(value) with key \(key)")
}
}
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)--")
urlRequest.httpBody = body
return urlRequest
}
private func configureFileUploadRequest(urlRequest: URLRequest, name: String, data: Data, mimeType: String?) -> URLRequest {
private func configureFileUploadRequest(urlRequest: URLRequest, boundary: String, name: String, fileURL: URL) throws -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody ?? Data()
var body = urlRequest.httpBody.orEmpty
// https://stackoverflow.com/a/26163136/976628
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
let fileName = fileURL.lastPathComponent
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")
if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(fileData)
body.append("\r\n\r\n")
urlRequest.httpBody = body
return urlRequest
}
private func configureDataUploadRequest(urlRequest: URLRequest, boundary: String, name: String, data: Data) -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
body.append("\r\n\r\n")
urlRequest.httpBody = body
@@ -514,11 +555,11 @@ private class FileUploadEncoding: ParameterEncoding {
}
fileprivate extension Data {
/// Append string to NSMutableData
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to Data, and then add that data to the Data, this wraps it in a nice convenient little extension to Data. This converts using UTF-8.
///
/// - parameter string: The string to be added to the `NSMutableData`.
/// - parameter string: The string to be added to the `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
@@ -527,4 +568,10 @@ fileprivate extension Data {
}
}
fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}
extension JSONDataEncoding: ParameterEncoding {}

View File

@@ -20,4 +20,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 509bec696cc1d8641751b52e4fe4bef04ac4542c
COCOAPODS: 1.8.4
COCOAPODS: 1.9.0

View File

@@ -17,6 +17,8 @@
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */; };
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */; };
751C65B82F596107A3DC8ED9 /* Pods_SwaggerClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F60AECFF321A25553B6A5B0 /* Pods_SwaggerClient.framework */; };
A5EA125C2419398500E30FC3 /* UIImage+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA125A2419398500E30FC3 /* UIImage+Extras.swift */; };
A5EA125D2419398500E30FC3 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA125B2419398500E30FC3 /* FileUtils.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -44,6 +46,8 @@
6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreAPITests.swift; sourceTree = "<group>"; };
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserAPITests.swift; sourceTree = "<group>"; };
8F60AECFF321A25553B6A5B0 /* Pods_SwaggerClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwaggerClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A5EA125A2419398500E30FC3 /* UIImage+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extras.swift"; sourceTree = "<group>"; };
A5EA125B2419398500E30FC3 /* FileUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
A638467ACFB30852DEA51F7A /* Pods-SwaggerClient.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClient.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClient/Pods-SwaggerClient.debug.xcconfig"; sourceTree = "<group>"; };
B4B2BEC2ECA535C616F2F3FE /* Pods-SwaggerClientTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClientTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClientTests/Pods-SwaggerClientTests.release.xcconfig"; sourceTree = "<group>"; };
C07EC0A94AA0F86D60668B32 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -132,6 +136,8 @@
6D4EFBB41C693BE200B96B06 /* PetAPITests.swift */,
6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */,
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */,
A5EA125B2419398500E30FC3 /* FileUtils.swift */,
A5EA125A2419398500E30FC3 /* UIImage+Extras.swift */,
);
path = SwaggerClientTests;
sourceTree = "<group>";
@@ -312,7 +318,9 @@
files = (
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */,
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */,
A5EA125C2419398500E30FC3 /* UIImage+Extras.swift in Sources */,
6D4EFBB51C693BE200B96B06 /* PetAPITests.swift in Sources */,
A5EA125D2419398500E30FC3 /* FileUtils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -0,0 +1,49 @@
//
// FileUtils.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
class FileUtils {
static func saveImage(imageName: String, image: UIImage) -> URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
//Checks if file exists, removes it if so.
deleteFile(fileURL: fileURL)
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
return nil
}
return fileURL
}
@discardableResult
static func deleteFile(fileURL: URL) -> Bool {
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
return true
} catch let removeError {
print("couldn't remove file at path", removeError)
return false
}
}
return false
}
}

View File

@@ -50,8 +50,32 @@ class PetAPITests: XCTestCase {
}
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test3UploadFile() {
let expectation = self.expectation(description: "testUploadFile")
func test3DeletePet() {
let imageName = UUID().uuidString + ".png"
guard
let image = UIImage(color: .red, size: CGSize(width: 10, height: 10)),
let imageURL = FileUtils.saveImage(imageName: imageName, image: image)
else {
fatalError()
}
PetAPI.uploadFile(petId: 1000, additionalMetadata: "additionalMetadata", file: imageURL).done { _ in
FileUtils.deleteFile(fileURL: imageURL)
expectation.fulfill()
}.catch { _ in
FileUtils.deleteFile(fileURL: imageURL)
XCTFail("error uploading file")
}
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test4DeletePet() {
let expectation = self.expectation(description: "testDeletePet")
PetAPI.deletePet(petId: 1000).done {
expectation.fulfill()

View File

@@ -0,0 +1,23 @@
//
// UIImage+Extras.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
extension UIImage {
convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
}

View File

@@ -29,7 +29,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
observation?.invalidate()
}
// swiftlint:disable:next weak_delegate
@@ -110,7 +110,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
let parameters: [String: Any] = self.parameters ?? [:]
let fileKeys = parameters.filter { $1 is NSURL }
let fileKeys = parameters.filter { $1 is URL }
.map { $0.0 }
let encoding: ParameterEncoding
@@ -441,58 +441,99 @@ private class FileUploadEncoding: ParameterEncoding {
var urlRequest = urlRequest
for (k, v) in parameters ?? [:] {
switch v {
guard let parameters = parameters, !parameters.isEmpty else {
return urlRequest
}
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
for (key, value) in parameters {
switch value {
case let fileURL as URL:
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: fileURL.lastPathComponent, data: fileData, mimeType: mimetype)
urlRequest = try configureFileUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
fileURL: fileURL
)
case let string as String:
if let data = string.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
case let number as NSNumber:
if let data = number.stringValue.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
default:
fatalError("Unprocessable value \(v) with key \(k)")
fatalError("Unprocessable value \(value) with key \(key)")
}
}
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)--")
urlRequest.httpBody = body
return urlRequest
}
private func configureFileUploadRequest(urlRequest: URLRequest, name: String, data: Data, mimeType: String?) -> URLRequest {
private func configureFileUploadRequest(urlRequest: URLRequest, boundary: String, name: String, fileURL: URL) throws -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody ?? Data()
var body = urlRequest.httpBody.orEmpty
// https://stackoverflow.com/a/26163136/976628
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
let fileName = fileURL.lastPathComponent
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")
if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(fileData)
body.append("\r\n\r\n")
urlRequest.httpBody = body
return urlRequest
}
private func configureDataUploadRequest(urlRequest: URLRequest, boundary: String, name: String, data: Data) -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
body.append("\r\n\r\n")
urlRequest.httpBody = body
@@ -514,11 +555,11 @@ private class FileUploadEncoding: ParameterEncoding {
}
fileprivate extension Data {
/// Append string to NSMutableData
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to Data, and then add that data to the Data, this wraps it in a nice convenient little extension to Data. This converts using UTF-8.
///
/// - parameter string: The string to be added to the `NSMutableData`.
/// - parameter string: The string to be added to the `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
@@ -527,4 +568,10 @@ fileprivate extension Data {
}
}
fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}
extension JSONDataEncoding: ParameterEncoding {}

View File

@@ -29,7 +29,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
observation?.invalidate()
}
// swiftlint:disable:next weak_delegate
@@ -110,7 +110,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
let parameters: [String: Any] = self.parameters ?? [:]
let fileKeys = parameters.filter { $1 is NSURL }
let fileKeys = parameters.filter { $1 is URL }
.map { $0.0 }
let encoding: ParameterEncoding
@@ -441,58 +441,99 @@ private class FileUploadEncoding: ParameterEncoding {
var urlRequest = urlRequest
for (k, v) in parameters ?? [:] {
switch v {
guard let parameters = parameters, !parameters.isEmpty else {
return urlRequest
}
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
for (key, value) in parameters {
switch value {
case let fileURL as URL:
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: fileURL.lastPathComponent, data: fileData, mimeType: mimetype)
urlRequest = try configureFileUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
fileURL: fileURL
)
case let string as String:
if let data = string.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
case let number as NSNumber:
if let data = number.stringValue.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
default:
fatalError("Unprocessable value \(v) with key \(k)")
fatalError("Unprocessable value \(value) with key \(key)")
}
}
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)--")
urlRequest.httpBody = body
return urlRequest
}
private func configureFileUploadRequest(urlRequest: URLRequest, name: String, data: Data, mimeType: String?) -> URLRequest {
private func configureFileUploadRequest(urlRequest: URLRequest, boundary: String, name: String, fileURL: URL) throws -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody ?? Data()
var body = urlRequest.httpBody.orEmpty
// https://stackoverflow.com/a/26163136/976628
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
let fileName = fileURL.lastPathComponent
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")
if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(fileData)
body.append("\r\n\r\n")
urlRequest.httpBody = body
return urlRequest
}
private func configureDataUploadRequest(urlRequest: URLRequest, boundary: String, name: String, data: Data) -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
body.append("\r\n\r\n")
urlRequest.httpBody = body
@@ -514,11 +555,11 @@ private class FileUploadEncoding: ParameterEncoding {
}
fileprivate extension Data {
/// Append string to NSMutableData
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to Data, and then add that data to the Data, this wraps it in a nice convenient little extension to Data. This converts using UTF-8.
///
/// - parameter string: The string to be added to the `NSMutableData`.
/// - parameter string: The string to be added to the `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
@@ -527,4 +568,10 @@ fileprivate extension Data {
}
}
fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}
extension JSONDataEncoding: ParameterEncoding {}

View File

@@ -20,4 +20,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 509bec696cc1d8641751b52e4fe4bef04ac4542c
COCOAPODS: 1.8.4
COCOAPODS: 1.9.0

View File

@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
A5EA1260241941BE00E30FC3 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA125E241941BE00E30FC3 /* FileUtils.swift */; };
A5EA1261241941BE00E30FC3 /* UIImage+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA125F241941BE00E30FC3 /* UIImage+Extras.swift */; };
B024164FBFF71BF644D4419A /* Pods_SwaggerClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 177A58DD5CF63F2989335DCC /* Pods_SwaggerClient.framework */; };
B1D0246C8960F47A60098F37 /* Pods_SwaggerClientTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F96A0131101344CC5406CB3 /* Pods_SwaggerClientTests.framework */; };
B596E4BD205657A500B46F03 /* APIHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B596E4BC205657A500B46F03 /* APIHelperTests.swift */; };
@@ -36,6 +38,8 @@
4EF2021609D112A6F5AE0F55 /* Pods-SwaggerClientTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClientTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClientTests/Pods-SwaggerClientTests.debug.xcconfig"; sourceTree = "<group>"; };
6F96A0131101344CC5406CB3 /* Pods_SwaggerClientTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwaggerClientTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8D99518E8E05FD856A952698 /* Pods-SwaggerClient.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClient.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClient/Pods-SwaggerClient.debug.xcconfig"; sourceTree = "<group>"; };
A5EA125E241941BE00E30FC3 /* FileUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
A5EA125F241941BE00E30FC3 /* UIImage+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extras.swift"; sourceTree = "<group>"; };
B596E4BC205657A500B46F03 /* APIHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIHelperTests.swift; sourceTree = "<group>"; };
EAEC0BBE1D4E30CE00C908A3 /* SwaggerClient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwaggerClient.app; sourceTree = BUILT_PRODUCTS_DIR; };
EAEC0BC11D4E30CE00C908A3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -133,6 +137,8 @@
EAEC0BE51D4E379000C908A3 /* StoreAPITests.swift */,
EAEC0BE71D4E38CB00C908A3 /* UserAPITests.swift */,
B596E4BC205657A500B46F03 /* APIHelperTests.swift */,
A5EA125E241941BE00E30FC3 /* FileUtils.swift */,
A5EA125F241941BE00E30FC3 /* UIImage+Extras.swift */,
);
path = SwaggerClientTests;
sourceTree = "<group>";
@@ -313,8 +319,10 @@
files = (
B596E4BD205657A500B46F03 /* APIHelperTests.swift in Sources */,
EAEC0BE81D4E38CB00C908A3 /* UserAPITests.swift in Sources */,
A5EA1261241941BE00E30FC3 /* UIImage+Extras.swift in Sources */,
EAEC0BE61D4E379000C908A3 /* StoreAPITests.swift in Sources */,
EAEC0BE41D4E330700C908A3 /* PetAPITests.swift in Sources */,
A5EA1260241941BE00E30FC3 /* FileUtils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -0,0 +1,49 @@
//
// FileUtils.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
class FileUtils {
static func saveImage(imageName: String, image: UIImage) -> URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
//Checks if file exists, removes it if so.
deleteFile(fileURL: fileURL)
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
return nil
}
return fileURL
}
@discardableResult
static func deleteFile(fileURL: URL) -> Bool {
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
return true
} catch let removeError {
print("couldn't remove file at path", removeError)
return false
}
}
return false
}
}

View File

@@ -66,7 +66,30 @@ class PetAPITests: XCTestCase {
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test3DeletePet() {
func test3UploadFile() {
let expectation = self.expectation(description: "testUploadFile")
let imageName = UUID().uuidString + ".png"
guard
let image = UIImage(color: .red, size: CGSize(width: 10, height: 10)),
let imageURL = FileUtils.saveImage(imageName: imageName, image: image)
else {
fatalError()
}
PetAPI.uploadFile(petId: 1000, additionalMetadata: "additionalMetadata", file: imageURL).subscribe(onNext: { pet in
FileUtils.deleteFile(fileURL: imageURL)
expectation.fulfill()
}, onError: { _ in
FileUtils.deleteFile(fileURL: imageURL)
XCTFail("error uploading file")
}).disposed(by: disposeBag)
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test4DeletePet() {
let expectation = self.expectation(description: "testDeletePet")
PetAPI.deletePet(petId: 1000).subscribe(onNext: {
expectation.fulfill()

View File

@@ -0,0 +1,23 @@
//
// UIImage+Extras.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
extension UIImage {
convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
}

View File

@@ -29,7 +29,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
observation?.invalidate()
}
// swiftlint:disable:next weak_delegate
@@ -110,7 +110,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
let parameters: [String: Any] = self.parameters ?? [:]
let fileKeys = parameters.filter { $1 is NSURL }
let fileKeys = parameters.filter { $1 is URL }
.map { $0.0 }
let encoding: ParameterEncoding
@@ -441,58 +441,99 @@ private class FileUploadEncoding: ParameterEncoding {
var urlRequest = urlRequest
for (k, v) in parameters ?? [:] {
switch v {
guard let parameters = parameters, !parameters.isEmpty else {
return urlRequest
}
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
for (key, value) in parameters {
switch value {
case let fileURL as URL:
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: fileURL.lastPathComponent, data: fileData, mimeType: mimetype)
urlRequest = try configureFileUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
fileURL: fileURL
)
case let string as String:
if let data = string.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
case let number as NSNumber:
if let data = number.stringValue.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
default:
fatalError("Unprocessable value \(v) with key \(k)")
fatalError("Unprocessable value \(value) with key \(key)")
}
}
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)--")
urlRequest.httpBody = body
return urlRequest
}
private func configureFileUploadRequest(urlRequest: URLRequest, name: String, data: Data, mimeType: String?) -> URLRequest {
private func configureFileUploadRequest(urlRequest: URLRequest, boundary: String, name: String, fileURL: URL) throws -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody ?? Data()
var body = urlRequest.httpBody.orEmpty
// https://stackoverflow.com/a/26163136/976628
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
let fileName = fileURL.lastPathComponent
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")
if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(fileData)
body.append("\r\n\r\n")
urlRequest.httpBody = body
return urlRequest
}
private func configureDataUploadRequest(urlRequest: URLRequest, boundary: String, name: String, data: Data) -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
body.append("\r\n\r\n")
urlRequest.httpBody = body
@@ -514,11 +555,11 @@ private class FileUploadEncoding: ParameterEncoding {
}
fileprivate extension Data {
/// Append string to NSMutableData
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to Data, and then add that data to the Data, this wraps it in a nice convenient little extension to Data. This converts using UTF-8.
///
/// - parameter string: The string to be added to the `NSMutableData`.
/// - parameter string: The string to be added to the `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
@@ -527,4 +568,10 @@ fileprivate extension Data {
}
}
fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}
extension JSONDataEncoding: ParameterEncoding {}

View File

@@ -13,4 +13,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 509bec696cc1d8641751b52e4fe4bef04ac4542c
COCOAPODS: 1.8.4
COCOAPODS: 1.9.0

View File

@@ -17,6 +17,8 @@
6D4EFBB51C693BE200B96B06 /* PetAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB41C693BE200B96B06 /* PetAPITests.swift */; };
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */; };
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */; };
A5EA12642419439700E30FC3 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA12622419439700E30FC3 /* FileUtils.swift */; };
A5EA12652419439700E30FC3 /* UIImage+Extras.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EA12632419439700E30FC3 /* UIImage+Extras.swift */; };
FB5CCC7EFA680BB2746B695B /* Pods_SwaggerClientTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83FDC034BBA2A07AE9975250 /* Pods_SwaggerClientTests.framework */; };
/* End PBXBuildFile section */
@@ -47,6 +49,8 @@
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserAPITests.swift; sourceTree = "<group>"; };
7F98CC8B18E5FA9213F6A68D /* Pods_SwaggerClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwaggerClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
83FDC034BBA2A07AE9975250 /* Pods_SwaggerClientTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwaggerClientTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A5EA12622419439700E30FC3 /* FileUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
A5EA12632419439700E30FC3 /* UIImage+Extras.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extras.swift"; sourceTree = "<group>"; };
ACB80AC61FA8D8916D4559AA /* Pods-SwaggerClient.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClient.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClient/Pods-SwaggerClient.release.xcconfig"; sourceTree = "<group>"; };
C07EC0A94AA0F86D60668B32 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E43FC34A9681D65ED44EE914 /* Pods-SwaggerClientTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClientTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClientTests/Pods-SwaggerClientTests.debug.xcconfig"; sourceTree = "<group>"; };
@@ -135,6 +139,8 @@
6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */,
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */,
1A501F47219C3DC600F372F6 /* DateFormatTests.swift */,
A5EA12622419439700E30FC3 /* FileUtils.swift */,
A5EA12632419439700E30FC3 /* UIImage+Extras.swift */,
);
path = SwaggerClientTests;
sourceTree = "<group>";
@@ -321,8 +327,10 @@
files = (
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */,
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */,
A5EA12652419439700E30FC3 /* UIImage+Extras.swift in Sources */,
1A501F48219C3DC600F372F6 /* DateFormatTests.swift in Sources */,
6D4EFBB51C693BE200B96B06 /* PetAPITests.swift in Sources */,
A5EA12642419439700E30FC3 /* FileUtils.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -0,0 +1,49 @@
//
// FileUtils.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
class FileUtils {
static func saveImage(imageName: String, image: UIImage) -> URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
}
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
//Checks if file exists, removes it if so.
deleteFile(fileURL: fileURL)
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
return nil
}
return fileURL
}
@discardableResult
static func deleteFile(fileURL: URL) -> Bool {
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
return true
} catch let removeError {
print("couldn't remove file at path", removeError)
return false
}
}
return false
}
}

View File

@@ -62,7 +62,33 @@ class PetAPITests: XCTestCase {
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test3DeletePet() {
func test3UploadFile() {
let expectation = self.expectation(description: "testUploadFile")
let imageName = UUID().uuidString + ".png"
guard
let image = UIImage(color: .red, size: CGSize(width: 10, height: 10)),
let imageURL = FileUtils.saveImage(imageName: imageName, image: image)
else {
fatalError()
}
PetAPI.uploadFile(petId: 1000, additionalMetadata: "additionalMetadata", file: imageURL) { (_, error) in
guard error == nil else {
FileUtils.deleteFile(fileURL: imageURL)
XCTFail("error uploading file")
return
}
FileUtils.deleteFile(fileURL: imageURL)
expectation.fulfill()
}
self.waitForExpectations(timeout: testTimeout, handler: nil)
}
func test4DeletePet() {
let expectation = self.expectation(description: "testDeletePet")
PetAPI.deletePet(petId: 1000) { (_, error) in

View File

@@ -0,0 +1,23 @@
//
// UIImage+Extras.swift
// SwaggerClientTests
//
// Created by Bruno Coelho on 11/03/2020.
// Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
//
import UIKit
extension UIImage {
convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
color.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
guard let cgImage = image?.cgImage else { return nil }
self.init(cgImage: cgImage)
}
}

View File

@@ -29,7 +29,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
private var observation: NSKeyValueObservation?
deinit {
observation?.invalidate()
observation?.invalidate()
}
// swiftlint:disable:next weak_delegate
@@ -110,7 +110,7 @@ open class URLSessionRequestBuilder<T>: RequestBuilder<T> {
let parameters: [String: Any] = self.parameters ?? [:]
let fileKeys = parameters.filter { $1 is NSURL }
let fileKeys = parameters.filter { $1 is URL }
.map { $0.0 }
let encoding: ParameterEncoding
@@ -441,58 +441,99 @@ private class FileUploadEncoding: ParameterEncoding {
var urlRequest = urlRequest
for (k, v) in parameters ?? [:] {
switch v {
guard let parameters = parameters, !parameters.isEmpty else {
return urlRequest
}
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
for (key, value) in parameters {
switch value {
case let fileURL as URL:
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: fileURL.lastPathComponent, data: fileData, mimeType: mimetype)
urlRequest = try configureFileUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
fileURL: fileURL
)
case let string as String:
if let data = string.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
case let number as NSNumber:
if let data = number.stringValue.data(using: .utf8) {
urlRequest = configureFileUploadRequest(urlRequest: urlRequest, name: k, data: data, mimeType: nil)
urlRequest = configureDataUploadRequest(
urlRequest: urlRequest,
boundary: boundary,
name: key,
data: data
)
}
default:
fatalError("Unprocessable value \(v) with key \(k)")
fatalError("Unprocessable value \(value) with key \(key)")
}
}
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)--")
urlRequest.httpBody = body
return urlRequest
}
private func configureFileUploadRequest(urlRequest: URLRequest, name: String, data: Data, mimeType: String?) -> URLRequest {
private func configureFileUploadRequest(urlRequest: URLRequest, boundary: String, name: String, fileURL: URL) throws -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody ?? Data()
var body = urlRequest.httpBody.orEmpty
// https://stackoverflow.com/a/26163136/976628
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let fileData = try Data(contentsOf: fileURL)
let mimetype = self.contentTypeForFormPart(fileURL) ?? mimeType(for: fileURL)
let fileName = fileURL.lastPathComponent
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")
if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}
body.append("Content-Type: \(mimetype)\r\n\r\n")
body.append(fileData)
body.append("\r\n\r\n")
urlRequest.httpBody = body
return urlRequest
}
private func configureDataUploadRequest(urlRequest: URLRequest, boundary: String, name: String, data: Data) -> URLRequest {
var urlRequest = urlRequest
var body = urlRequest.httpBody.orEmpty
body.append("--\(boundary)\r\n")
body.append("Content-Disposition: form-data; name=\"\(name)\"\r\n\r\n")
body.append(data)
body.append("\r\n")
body.append("--\(boundary)--\r\n")
body.append("\r\n\r\n")
urlRequest.httpBody = body
@@ -514,11 +555,11 @@ private class FileUploadEncoding: ParameterEncoding {
}
fileprivate extension Data {
/// Append string to NSMutableData
/// Append string to Data
///
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to NSData, and then add that data to the NSMutableData, this wraps it in a nice convenient little extension to NSMutableData. This converts using UTF-8.
/// Rather than littering my code with calls to `dataUsingEncoding` to convert strings to Data, and then add that data to the Data, this wraps it in a nice convenient little extension to Data. This converts using UTF-8.
///
/// - parameter string: The string to be added to the `NSMutableData`.
/// - parameter string: The string to be added to the `Data`.
mutating func append(_ string: String) {
if let data = string.data(using: .utf8) {
@@ -527,4 +568,10 @@ fileprivate extension Data {
}
}
fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}
extension JSONDataEncoding: ParameterEncoding {}