Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private var urlSessionStore = SynchronizedDictionary<String, URLSession>()
private var observation: NSKeyValueObservation?

deinit {
observation?.invalidate()
observation?.invalidate()
}

// swiftlint:disable:next weak_delegate
Expand Down Expand Up @@ -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
Expand All @@ -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:))
Expand All @@ -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 {
Expand Down Expand Up @@ -170,7 +170,7 @@ private var urlSessionStore = SynchronizedDictionary<String, URLSession>()
}

dataTask.resume()

} catch {
apiResponseQueue.async {
cleanupRequest()
Expand Down Expand Up @@ -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 {

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
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)
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:

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")

if let mimeType = mimeType {
body.append("Content-Type: \(mimeType)\r\n\r\n")
}

body.append(data)
body.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n\r\n")

body.append("\r\n")
body.append("Content-Type: \(mimetype)\r\n\r\n")

body.append("--\(boundary)--\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\r\n")

urlRequest.httpBody = body

return urlRequest

}

func mimeType(for url: URL) -> String {
let pathExtension = url.pathExtension

Expand All @@ -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) {
Expand All @@ -527,4 +568,10 @@ fileprivate extension Data {
}
}

fileprivate extension Optional where Wrapped == Data {
var orEmpty: Data {
self ?? Data()
}
}

extension JSONDataEncoding: ParameterEncoding {}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 509bec696cc1d8641751b52e4fe4bef04ac4542c

COCOAPODS: 1.8.4
COCOAPODS: 1.9.0
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down Expand Up @@ -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>"; };
Expand Down Expand Up @@ -135,6 +139,8 @@
6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */,
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */,
1A501F47219C3DC600F372F6 /* DateFormatTests.swift */,
A5EA12522419387100E30FC3 /* FileUtils.swift */,
A5EA12532419387100E30FC3 /* UIImage+Extras.swift */,
);
path = SwaggerClientTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -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;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading