diff --git a/Specs/BinaryUpload/generated/Swift/BinaryUpload.podspec b/Specs/BinaryUpload/generated/Swift/BinaryUpload.podspec
new file mode 100644
index 000000000..180cab20d
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/BinaryUpload.podspec
@@ -0,0 +1,14 @@
+Pod::Spec.new do |s|
+ s.source_files = '*.swift'
+ s.name = 'BinaryUpload'
+ s.authors = 'Yonas Kolb'
+ s.summary = 'A generated API'
+ s.version = '1.0'
+ s.homepage = 'https://github.com/yonaskolb/SwagGen'
+ s.source = { :git => 'git@github.com:https://github.com/yonaskolb/SwagGen.git' }
+ s.ios.deployment_target = '10.0'
+ s.tvos.deployment_target = '10.0'
+ s.osx.deployment_target = '10.12'
+ s.source_files = 'Sources/**/*.swift'
+ s.dependency 'Alamofire', '~> 5.4.4'
+end
diff --git a/Specs/BinaryUpload/generated/Swift/Cartfile b/Specs/BinaryUpload/generated/Swift/Cartfile
new file mode 100644
index 000000000..e60e02fd8
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Cartfile
@@ -0,0 +1,2 @@
+
+github "Alamofire/Alamofire" ~> 5.4.4
diff --git a/Specs/BinaryUpload/generated/Swift/Info.plist b/Specs/BinaryUpload/generated/Swift/Info.plist
new file mode 100644
index 000000000..1007fd9dd
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Info.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ NSPrincipalClass
+
+
+
diff --git a/Specs/BinaryUpload/generated/Swift/Package.swift b/Specs/BinaryUpload/generated/Swift/Package.swift
new file mode 100644
index 000000000..1ee04ac95
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Package.swift
@@ -0,0 +1,24 @@
+// swift-tools-version:5.2
+
+import PackageDescription
+
+let package = Package(
+ name: "BinaryUpload",
+ platforms: [
+ .macOS(.v10_12),
+ .iOS(.v10),
+ .tvOS(.v10),
+ .watchOS(.v3)
+ ],
+ products: [
+ .library(name: "BinaryUpload", targets: ["BinaryUpload"])
+ ],
+ dependencies: [
+ .package(url: "https://github.com/Alamofire/Alamofire.git", .exact("5.4.4")),
+ ],
+ targets: [
+ .target(name: "BinaryUpload", dependencies: [
+ "Alamofire",
+ ], path: "Sources")
+ ]
+)
diff --git a/Specs/BinaryUpload/generated/Swift/README.md b/Specs/BinaryUpload/generated/Swift/README.md
new file mode 100644
index 000000000..cde823532
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/README.md
@@ -0,0 +1,158 @@
+# BinaryUpload
+
+This is an api generated from a OpenAPI 3.0 spec with [SwagGen](https://github.com/yonaskolb/SwagGen)
+
+## Operation
+
+Each operation lives under the `BinaryUpload` namespace and within an optional tag: `BinaryUpload(.tagName).operationId`. If an operation doesn't have an operationId one will be generated from the path and method.
+
+Each operation has a nested `Request` and a `Response`, as well as a static `service` property
+
+#### Service
+
+This is the struct that contains the static information about an operation including it's id, tag, method, pre-modified path, and authorization requirements. It has a generic `ResponseType` type which maps to the `Response` type.
+You shouldn't really need to interact with this service type.
+
+#### Request
+
+Each request is a subclass of `APIRequest` and has an `init` with a body param if it has a body, and a `options` struct for other url and path parameters. There is also a convenience init for passing parameters directly.
+The `options` and `body` structs are both mutable so they can be modified before actually sending the request.
+
+#### Response
+
+The response is an enum of all the possible responses the request can return. it also contains getters for the `statusCode`, whether it was `successful`, and the actual decoded optional `success` response. If the operation only has one type of failure type there is also an optional `failure` type.
+
+## Model
+Models that are sent and returned from the API are mutable classes. Each model is `Equatable` and `Codable`.
+
+`Required` properties are non optional and non-required are optional
+
+All properties can be passed into the initializer, with `required` properties being mandatory.
+
+If a model has `additionalProperties` it will have a subscript to access these by string
+
+## APIClient
+The `APIClient` is used to encode, authorize, send, monitor, and decode the requests. There is a `APIClient.default` that uses the default `baseURL` otherwise a custom one can be initialized:
+
+```swift
+public init(baseURL: String, sessionManager: SessionManager = .default, defaultHeaders: [String: String] = [:], behaviours: [RequestBehaviour] = [])
+```
+
+#### APIClient properties
+
+- `baseURL`: The base url that every request `path` will be appended to
+- `behaviours`: A list of [Request Behaviours](#requestbehaviour) to add to every request
+- `sessionManager`: An `Alamofire.SessionManager` that can be customized
+- `defaultHeaders`: Headers that will be applied to every request
+- `decodingQueue`: The `DispatchQueue` to decode responses on
+
+#### Making a request
+To make a request first initialize a [Request](#request) and then pass it to `makeRequest`. The `complete` closure will be called with an `APIResponse`
+
+```swift
+func makeRequest(_ request: APIRequest, behaviours: [RequestBehaviour] = [], queue: DispatchQueue = DispatchQueue.main, complete: @escaping (APIResponse) -> Void) -> Request? {
+```
+
+Example request (that is not neccessarily in this api):
+
+```swift
+
+let getUserRequest = BinaryUpload.User.GetUser.Request(id: 123)
+let apiClient = APIClient.default
+
+apiClient.makeRequest(getUserRequest) { apiResponse in
+ switch apiResponse {
+ case .result(let apiResponseValue):
+ if let user = apiResponseValue.success {
+ print("GetUser returned user \(user)")
+ } else {
+ print("GetUser returned \(apiResponseValue)")
+ }
+ case .error(let apiError):
+ print("GetUser failed with \(apiError)")
+ }
+}
+```
+
+Each [Request](#request) also has a `makeRequest` convenience function that uses `BinaryUpload.shared`.
+
+#### APIResponse
+The `APIResponse` that gets passed to the completion closure contains the following properties:
+
+- `request`: The original request
+- `result`: A `Result` type either containing an `APIClientError` or the [Response](#response) of the request
+- `urlRequest`: The `URLRequest` used to send the request
+- `urlResponse`: The `HTTPURLResponse` that was returned by the request
+- `data`: The `Data` returned by the request.
+- `timeline`: The `Alamofire.Timeline` of the request which contains timing information.
+
+#### Encoding and Decoding
+Only JSON requests and responses are supported. These are encoded and decoded by `JSONEncoder` and `JSONDecoder` respectively, using Swift's `Codable` apis.
+There are some options to control how invalid JSON is handled when decoding and these are available as static properties on `BinaryUpload`:
+
+- `safeOptionalDecoding`: Whether to discard any errors when decoding optional properties. Defaults to `true`.
+- `safeArrayDecoding`: Whether to remove invalid elements instead of throwing when decoding arrays. Defaults to `true`.
+
+Dates are encoded and decoded differently according to the swagger date format. They use different `DateFormatter`'s that you can set.
+- `date-time`
+ - `DateTime.dateEncodingFormatter`: defaults to `yyyy-MM-dd'T'HH:mm:ss.Z`
+ - `DateTime.dateDecodingFormatters`: an array of date formatters. The first one to decode successfully will be used
+- `date`
+ - `DateDay.dateFormatter`: defaults to `yyyy-MM-dd`
+
+#### APIClientError
+This is error enum that `APIResponse.result` may contain:
+
+```swift
+public enum APIClientError: Error {
+ case unexpectedStatusCode(statusCode: Int, data: Data)
+ case decodingError(DecodingError)
+ case requestEncodingError(String)
+ case validationError(String)
+ case networkError(Error)
+ case unknownError(Error)
+}
+```
+
+#### RequestBehaviour
+Request behaviours are used to modify, authorize, monitor or respond to requests. They can be added to the `APIClient.behaviours` for all requests, or they can passed into `makeRequest` for just that single request.
+
+`RequestBehaviour` is a protocol you can conform to with each function being optional. As the behaviours must work across multiple different request types, they only have access to a typed erased `AnyRequest`.
+
+```swift
+public protocol RequestBehaviour {
+
+ /// runs first and allows the requests to be modified. If modifying asynchronously use validate
+ func modifyRequest(request: AnyRequest, urlRequest: URLRequest) -> URLRequest
+
+ /// validates and modifies the request. complete must be called with either .success or .fail
+ func validate(request: AnyRequest, urlRequest: URLRequest, complete: @escaping (RequestValidationResult) -> Void)
+
+ /// called before request is sent
+ func beforeSend(request: AnyRequest)
+
+ /// called when request successfuly returns a 200 range response
+ func onSuccess(request: AnyRequest, result: Any)
+
+ /// called when request fails with an error. This will not be called if the request returns a known response even if the a status code is out of the 200 range
+ func onFailure(request: AnyRequest, error: APIClientError)
+
+ /// called if the request recieves a network response. This is not called if request fails validation or encoding
+ func onResponse(request: AnyRequest, response: AnyResponse)
+}
+```
+
+### Authorization
+Each request has an optional `securityRequirement`. You can create a `RequestBehaviour` that checks this requirement and adds some form of authorization (usually via headers) in `validate` or `modifyRequest`. An alternative way is to set the `APIClient.defaultHeaders` which applies to all requests.
+
+#### Reactive and Promises
+To add support for a specific asynchronous library, just add an extension on `APIClient` and add a function that wraps the `makeRequest` function and converts from a closure based syntax to returning the object of choice (stream, future...ect)
+
+## Models
+
+- **UploadModel**
+
+## Requests
+
+- **BinaryUpload.PostUser**: POST `/withoutmodel`
+- **BinaryUpload.PostWithType**: POST `/withmodel`
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/API.swift b/Specs/BinaryUpload/generated/Swift/Sources/API.swift
new file mode 100644
index 000000000..fd532317a
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/API.swift
@@ -0,0 +1,28 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+import Foundation
+
+public struct BinaryUpload {
+
+ /// Whether to discard any errors when decoding optional properties
+ public static var safeOptionalDecoding = false
+
+ /// Whether to remove invalid elements instead of throwing when decoding arrays
+ public static var safeArrayDecoding = false
+
+ /// Used to encode Dates when uses as string params
+ public static var dateEncodingFormatter = DateFormatter(formatString: "yyyy-MM-dd'T'HH:mm:ssZZZZZ",
+ locale: Locale(identifier: "en_US_POSIX"),
+ calendar: Calendar(identifier: .gregorian))
+
+ public static let version = "1.0"
+
+
+ public enum Server {
+
+ public static let main = "http://localhost:3000"
+ }
+}
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/APIClient.swift b/Specs/BinaryUpload/generated/Swift/Sources/APIClient.swift
new file mode 100644
index 000000000..b2135c1b5
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/APIClient.swift
@@ -0,0 +1,253 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+import Foundation
+import Alamofire
+
+/// Manages and sends APIRequests
+public class APIClient {
+
+ public static var `default` = APIClient(baseURL: BinaryUpload.Server.main)
+
+ /// A list of RequestBehaviours that can be used to monitor and alter all requests
+ public var behaviours: [RequestBehaviour] = []
+
+ /// The base url prepended before every request path
+ public var baseURL: String
+
+ /// The Alamofire SessionManager used for each request
+ public var sessionManager: Session
+
+ /// These headers will get added to every request
+ public var defaultHeaders: [String: String]
+
+ public var jsonDecoder = JSONDecoder()
+ public var jsonEncoder = JSONEncoder()
+
+ public var decodingQueue = DispatchQueue(label: "apiClient", qos: .utility, attributes: .concurrent)
+
+ public init(baseURL: String, sessionManager: Session = .default, defaultHeaders: [String: String] = [:], behaviours: [RequestBehaviour] = []) {
+ self.baseURL = baseURL
+ self.sessionManager = sessionManager
+ self.behaviours = behaviours
+ self.defaultHeaders = defaultHeaders
+ jsonDecoder.dateDecodingStrategy = .custom(dateDecoder)
+ jsonEncoder.dateEncodingStrategy = .formatted(BinaryUpload.dateEncodingFormatter)
+ }
+
+ /// Makes a network request
+ ///
+ /// - Parameters:
+ /// - request: The API request to make
+ /// - behaviours: A list of behaviours that will be run for this request. Merged with APIClient.behaviours
+ /// - completionQueue: The queue that complete will be called on
+ /// - complete: A closure that gets passed the APIResponse
+ /// - Returns: A cancellable request. Not that cancellation will only work after any validation RequestBehaviours have run
+ @discardableResult
+ public func makeRequest(_ request: APIRequest, behaviours: [RequestBehaviour] = [], completionQueue: DispatchQueue = DispatchQueue.main, complete: @escaping (APIResponse) -> Void) -> CancellableRequest? {
+ // create composite behaviour to make it easy to call functions on array of behaviours
+ let requestBehaviour = RequestBehaviourGroup(request: request, behaviours: self.behaviours + behaviours)
+
+ // create the url request from the request
+ var urlRequest: URLRequest
+ do {
+ guard let safeURL = URL(string: baseURL) else {
+ throw InternalError.malformedURL
+ }
+
+ urlRequest = try request.createURLRequest(baseURL: safeURL, encoder: jsonEncoder)
+ } catch {
+ let error = APIClientError.requestEncodingError(error)
+ requestBehaviour.onFailure(error: error)
+ let response = APIResponse(request: request, result: .failure(error))
+ complete(response)
+ return nil
+ }
+
+ // add the default headers
+ if urlRequest.allHTTPHeaderFields == nil {
+ urlRequest.allHTTPHeaderFields = [:]
+ }
+ for (key, value) in defaultHeaders {
+ urlRequest.allHTTPHeaderFields?[key] = value
+ }
+
+ urlRequest = requestBehaviour.modifyRequest(urlRequest)
+
+ let cancellableRequest = CancellableRequest(request: request.asAny())
+
+ requestBehaviour.validate(urlRequest) { result in
+ switch result {
+ case .success(let urlRequest):
+ let networkRequest = self.makeNetworkRequest(request: request, urlRequest: urlRequest, requestBehaviour: requestBehaviour, completionQueue: completionQueue, complete: complete)
+ cancellableRequest.networkRequest = networkRequest
+ case .failure(let error):
+ let error = APIClientError.validationError(error)
+ let response = APIResponse(request: request, result: .failure(error), urlRequest: urlRequest)
+ requestBehaviour.onFailure(error: error)
+ complete(response)
+ }
+ }
+ return cancellableRequest
+ }
+
+ private func makeNetworkRequest(request: APIRequest, urlRequest: URLRequest, requestBehaviour: RequestBehaviourGroup, completionQueue: DispatchQueue, complete: @escaping (APIResponse) -> Void) -> Request {
+ requestBehaviour.beforeSend()
+
+ if request.service.isUpload {
+ return sessionManager.upload(
+ multipartFormData: { multipartFormData in
+ for (name, value) in request.formParameters {
+ if let file = value as? UploadFile {
+ switch file.type {
+ case let .url(url):
+ if let fileName = file.fileName, let mimeType = file.mimeType {
+ multipartFormData.append(url, withName: name, fileName: fileName, mimeType: mimeType)
+ } else {
+ multipartFormData.append(url, withName: name)
+ }
+ case let .data(data):
+ if let fileName = file.fileName, let mimeType = file.mimeType {
+ multipartFormData.append(data, withName: name, fileName: fileName, mimeType: mimeType)
+ } else {
+ multipartFormData.append(data, withName: name)
+ }
+ }
+ } else if let url = value as? URL {
+ multipartFormData.append(url, withName: name)
+ } else if let data = value as? Data {
+ multipartFormData.append(data, withName: name)
+ } else if let string = value as? String {
+ multipartFormData.append(Data(string.utf8), withName: name)
+ }
+ }
+ },
+ with: urlRequest
+ )
+ .responseData(queue: decodingQueue) { dataResponse in
+ self.handleResponse(request: request, requestBehaviour: requestBehaviour, dataResponse: dataResponse, completionQueue: completionQueue, complete: complete)
+ }
+ } else {
+ return sessionManager.request(urlRequest)
+ .responseData(queue: decodingQueue) { dataResponse in
+ self.handleResponse(request: request, requestBehaviour: requestBehaviour, dataResponse: dataResponse, completionQueue: completionQueue, complete: complete)
+
+ }
+ }
+ }
+
+ private func handleResponse(request: APIRequest, requestBehaviour: RequestBehaviourGroup, dataResponse: AFDataResponse, completionQueue: DispatchQueue, complete: @escaping (APIResponse) -> Void) {
+
+ let result: APIResult
+
+ switch dataResponse.result {
+ case .success(let value):
+ do {
+ guard let statusCode = dataResponse.response?.statusCode else {
+ throw InternalError.emptyResponse
+ }
+
+ let decoded = try T(statusCode: statusCode, data: value, decoder: jsonDecoder)
+ result = .success(decoded)
+ if decoded.successful {
+ requestBehaviour.onSuccess(result: decoded.response as Any)
+ }
+ } catch let error {
+ let apiError: APIClientError
+ if let error = error as? DecodingError {
+ apiError = APIClientError.decodingError(error)
+ } else if let error = error as? APIClientError {
+ apiError = error
+ } else {
+ apiError = APIClientError.unknownError(error)
+ }
+
+ result = .failure(apiError)
+ requestBehaviour.onFailure(error: apiError)
+ }
+ case .failure(let error):
+ let apiError = APIClientError.networkError(error)
+ result = .failure(apiError)
+ requestBehaviour.onFailure(error: apiError)
+ }
+ let response = APIResponse(request: request, result: result, urlRequest: dataResponse.request, urlResponse: dataResponse.response, data: dataResponse.data, metrics: dataResponse.metrics)
+ requestBehaviour.onResponse(response: response.asAny())
+
+ completionQueue.async {
+ complete(response)
+ }
+ }
+}
+
+private extension APIClient {
+ enum InternalError: Error {
+ case malformedURL
+ case emptyResponse
+ }
+}
+
+public class CancellableRequest {
+ /// The request used to make the actual network request
+ public let request: AnyRequest
+
+ init(request: AnyRequest) {
+ self.request = request
+ }
+
+ var networkRequest: Request?
+
+ /// cancels the request
+ public func cancel() {
+ networkRequest?.cancel()
+ }
+}
+
+// Helper extension for sending requests
+extension APIRequest {
+
+ /// makes a request using the default APIClient. Change your baseURL in APIClient.default.baseURL
+ public func makeRequest(complete: @escaping (APIResponse) -> Void) {
+ APIClient.default.makeRequest(self, complete: complete)
+ }
+}
+
+// Create URLRequest
+extension APIRequest {
+
+ public func createURLRequest(baseURL: URL, encoder: RequestEncoder = JSONEncoder()) throws -> URLRequest {
+ var urlRequest = URLRequest(url: baseURL.appendingPathComponent(path))
+ urlRequest.httpMethod = service.method
+ urlRequest.allHTTPHeaderFields = headers
+
+ // filter out parameters with empty string value
+ var queryParams: [String: Any] = [:]
+ for (key, value) in queryParameters {
+ if !String(describing: value).isEmpty {
+ queryParams[key] = value
+ }
+ }
+
+ if !queryParams.isEmpty {
+ urlRequest = try URLEncoding.queryString.encode(urlRequest, with: queryParams)
+ }
+
+ var formParams: [String: Any] = [:]
+ for (key, value) in formParameters {
+ if !String(describing: value).isEmpty {
+ formParams[key] = value
+ }
+ }
+
+ if !formParams.isEmpty {
+ urlRequest = try URLEncoding.httpBody.encode(urlRequest, with: formParams)
+ }
+
+ if let encodeBody = encodeBody {
+ urlRequest.httpBody = try encodeBody(encoder)
+ urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
+ }
+ return urlRequest
+ }
+}
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/APIClientError.swift b/Specs/BinaryUpload/generated/Swift/Sources/APIClientError.swift
new file mode 100644
index 000000000..82fb6db07
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/APIClientError.swift
@@ -0,0 +1,40 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+import Foundation
+
+public enum APIClientError: Error {
+ case unexpectedStatusCode(statusCode: Int, data: Data)
+ case decodingError(DecodingError)
+ case requestEncodingError(Error)
+ case validationError(Error)
+ case networkError(Error)
+ case unknownError(Error)
+
+ public var name:String {
+ switch self {
+ case .unexpectedStatusCode: return "Unexpected status code"
+ case .decodingError: return "Decoding error"
+ case .validationError: return "Request validation failed"
+ case .requestEncodingError: return "Request encoding failed"
+ case .networkError: return "Network error"
+ case .unknownError: return "Unknown error"
+ }
+ }
+}
+
+extension APIClientError: CustomStringConvertible {
+
+ public var description:String {
+ switch self {
+ case .unexpectedStatusCode(let statusCode, _): return "\(name): \(statusCode)"
+ case .decodingError(let error): return "\(name): \(error.localizedDescription)\n\(error)"
+ case .validationError(let error): return "\(name): \(error.localizedDescription)"
+ case .requestEncodingError(let error): return "\(name): \(error)"
+ case .networkError(let error): return "\(name): \(error.localizedDescription)"
+ case .unknownError(let error): return "\(name): \(error.localizedDescription)"
+ }
+ }
+}
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/APIRequest.swift b/Specs/BinaryUpload/generated/Swift/Sources/APIRequest.swift
new file mode 100644
index 000000000..49097b6c1
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/APIRequest.swift
@@ -0,0 +1,131 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+import Foundation
+
+public class APIRequest {
+
+ public let service: APIService
+ public private(set) var queryParameters: [String: Any]
+ public private(set) var formParameters: [String: Any]
+ public let encodeBody: ((RequestEncoder) throws -> Data)?
+ private(set) var headerParameters: [String: String]
+ public var customHeaders: [String: String] = [:]
+
+ public var headers: [String: String] {
+ return headerParameters.merging(customHeaders) { param, custom in return custom }
+ }
+
+ public var path: String {
+ return service.path
+ }
+
+ public init(service: APIService,
+ queryParameters: [String: Any] = [:],
+ formParameters: [String: Any] = [:],
+ headers: [String: String] = [:],
+ encodeBody: ((RequestEncoder) throws -> Data)? = nil) {
+ self.service = service
+ self.queryParameters = queryParameters
+ self.formParameters = formParameters
+ self.headerParameters = headers
+ self.encodeBody = encodeBody
+ }
+}
+
+extension APIRequest: CustomStringConvertible {
+
+ public var description: String {
+ var string = "\(service.name): \(service.method) \(path)"
+ if !queryParameters.isEmpty {
+ string += "?" + queryParameters.map {"\($0)=\($1)"}.joined(separator: "&")
+ }
+ return string
+ }
+}
+
+extension APIRequest: CustomDebugStringConvertible {
+
+ public var debugDescription: String {
+ var string = description
+ if let encodeBody = encodeBody,
+ let data = try? encodeBody(JSONEncoder()),
+ let bodyString = String(data: data, encoding: .utf8) {
+ string += "\nbody: \(bodyString)"
+ }
+ return string
+ }
+}
+
+/// A file upload
+public struct UploadFile: Equatable, Codable {
+
+ public let type: FileType
+ public let fileName: String?
+ public let mimeType: String?
+
+ public init(type: FileType) {
+ self.type = type
+ self.fileName = nil
+ self.mimeType = nil
+ }
+
+ public init(type: FileType, fileName: String, mimeType: String) {
+ self.type = type
+ self.fileName = fileName
+ self.mimeType = mimeType
+ }
+
+ public enum FileType: Equatable, Codable {
+ case data(Data)
+ case url(URL)
+
+ enum CodingKeys: CodingKey {
+ case data
+ case url
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ switch self {
+ case let .data(value):
+ try container.encode(value, forKey: .data)
+ case let .url(value):
+ try container.encode(value, forKey: .url)
+ }
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let key = container.allKeys.first
+
+ switch key {
+ case .data:
+ let value = try container.decode(
+ Data.self,
+ forKey: .data
+ )
+ self = .data(value)
+ case .url:
+ let value = try container.decode(
+ URL.self,
+ forKey: .url
+ )
+ self = .url(value)
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: container.codingPath,
+ debugDescription: "Unabled to decode FileType."
+ )
+ )
+ }
+ }
+ }
+
+ func encode() -> Any {
+ return self
+ }
+}
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/APIResponse.swift b/Specs/BinaryUpload/generated/Swift/Sources/APIResponse.swift
new file mode 100644
index 000000000..7fdc7bb41
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/APIResponse.swift
@@ -0,0 +1,101 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+import Foundation
+import Alamofire
+
+public protocol APIResponseValue: CustomDebugStringConvertible, CustomStringConvertible {
+ associatedtype SuccessType
+ var statusCode: Int { get }
+ var successful: Bool { get }
+ var response: Any { get }
+ init(statusCode: Int, data: Data, decoder: ResponseDecoder) throws
+ var success: SuccessType? { get }
+}
+
+public enum APIResponseResult: CustomStringConvertible, CustomDebugStringConvertible {
+ case success(SuccessType)
+ case failure(FailureType)
+
+ public var value: Any {
+ switch self {
+ case .success(let value): return value
+ case .failure(let value): return value
+ }
+ }
+
+ public var successful: Bool {
+ switch self {
+ case .success: return true
+ case .failure: return false
+ }
+ }
+
+ public var description: String {
+ return "\(successful ? "success" : "failure")"
+ }
+
+ public var debugDescription: String {
+ return "\(description):\n\(value)"
+ }
+}
+
+public struct APIResponse {
+
+ /// The APIRequest used for this response
+ public let request: APIRequest
+
+ /// The result of the response .
+ public let result: APIResult
+
+ /// The URL request sent to the server.
+ public let urlRequest: URLRequest?
+
+ /// The server's response to the URL request.
+ public let urlResponse: HTTPURLResponse?
+
+ /// The data returned by the server.
+ public let data: Data?
+
+ /// The timeline of the complete lifecycle of the request.
+ public let metrics: URLSessionTaskMetrics?
+
+ init(request: APIRequest, result: APIResult, urlRequest: URLRequest? = nil, urlResponse: HTTPURLResponse? = nil, data: Data? = nil, metrics: URLSessionTaskMetrics? = nil) {
+ self.request = request
+ self.result = result
+ self.urlRequest = urlRequest
+ self.urlResponse = urlResponse
+ self.data = data
+ self.metrics = metrics
+ }
+}
+
+extension APIResponse: CustomStringConvertible, CustomDebugStringConvertible {
+
+ public var description:String {
+ var string = "\(request)"
+
+ switch result {
+ case .success(let value):
+ string += " returned \(value.statusCode)"
+ let responseString = "\(type(of: value.response))"
+ if responseString != "()" {
+ string += ": \(responseString)"
+ }
+ case .failure(let error): string += " failed: \(error)"
+ }
+ return string
+ }
+
+ public var debugDescription: String {
+ var string = description
+ if let response = try? result.get().response {
+ if let debugStringConvertible = response as? CustomDebugStringConvertible {
+ string += "\n\(debugStringConvertible.debugDescription)"
+ }
+ }
+ return string
+ }
+}
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/APIResult.swift b/Specs/BinaryUpload/generated/Swift/Sources/APIResult.swift
new file mode 100644
index 000000000..718c5ceb8
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/APIResult.swift
@@ -0,0 +1,6 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+public typealias APIResult = Result
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/APIService.swift b/Specs/BinaryUpload/generated/Swift/Sources/APIService.swift
new file mode 100644
index 000000000..4cf2264f2
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/APIService.swift
@@ -0,0 +1,47 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+public struct APIService {
+
+ public let id: String
+ public let tag: String
+ public let method: String
+ public let path: String
+ public let hasBody: Bool
+ public let isUpload: Bool
+ public let securityRequirements: [SecurityRequirement]
+
+ public init(id: String, tag: String = "", method:String, path:String, hasBody: Bool, isUpload: Bool = false, securityRequirements: [SecurityRequirement] = []) {
+ self.id = id
+ self.tag = tag
+ self.method = method
+ self.path = path
+ self.hasBody = hasBody
+ self.isUpload = isUpload
+ self.securityRequirements = securityRequirements
+ }
+}
+
+extension APIService: CustomStringConvertible {
+
+ public var name: String {
+ return "\(tag.isEmpty ? "" : "\(tag).")\(id)"
+ }
+
+ public var description: String {
+ return "\(name): \(method) \(path)"
+ }
+}
+
+public struct SecurityRequirement {
+ public let type: String
+ public let scopes: [String]
+
+ public init(type: String, scopes: [String]) {
+ self.type = type
+ self.scopes = scopes
+ }
+}
+
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/AnyCodable.swift b/Specs/BinaryUpload/generated/Swift/Sources/AnyCodable.swift
new file mode 100644
index 000000000..e263e9005
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/AnyCodable.swift
@@ -0,0 +1,188 @@
+import Foundation
+
+public struct AnyCodable {
+ let value: Any
+
+ init(_ value: T?) {
+ self.value = value ?? ()
+ }
+}
+
+extension AnyCodable: Codable {
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+
+ if container.decodeNil() {
+ self.init(())
+ } else if let bool = try? container.decode(Bool.self) {
+ self.init(bool)
+ } else if let int = try? container.decode(Int.self) {
+ self.init(int)
+ } else if let uint = try? container.decode(UInt.self) {
+ self.init(uint)
+ } else if let double = try? container.decode(Double.self) {
+ self.init(double)
+ } else if let string = try? container.decode(String.self) {
+ self.init(string)
+ } else if let array = try? container.decode([AnyCodable].self) {
+ self.init(array.map { $0.value })
+ } else if let dictionary = try? container.decode([String: AnyCodable].self) {
+ self.init(dictionary.mapValues { $0.value })
+ } else {
+ throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyCodable value cannot be decoded")
+ }
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+
+ switch self.value {
+ case is Void:
+ try container.encodeNil()
+ case let bool as Bool:
+ try container.encode(bool)
+ case let int as Int:
+ try container.encode(int)
+ case let int8 as Int8:
+ try container.encode(int8)
+ case let int16 as Int16:
+ try container.encode(int16)
+ case let int32 as Int32:
+ try container.encode(int32)
+ case let int64 as Int64:
+ try container.encode(int64)
+ case let uint as UInt:
+ try container.encode(uint)
+ case let uint8 as UInt8:
+ try container.encode(uint8)
+ case let uint16 as UInt16:
+ try container.encode(uint16)
+ case let uint32 as UInt32:
+ try container.encode(uint32)
+ case let uint64 as UInt64:
+ try container.encode(uint64)
+ case let float as Float:
+ try container.encode(float)
+ case let double as Double:
+ try container.encode(double)
+ case let string as String:
+ try container.encode(string)
+ case let date as Date:
+ try container.encode(date)
+ case let url as URL:
+ try container.encode(url)
+ case let array as [Any?]:
+ try container.encode(array.map { AnyCodable($0) })
+ case let dictionary as [String: Any?]:
+ try container.encode(dictionary.mapValues { AnyCodable($0) })
+ case let object as Encodable:
+ try object.encode(to: encoder)
+ default:
+ let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyCodable value cannot be encoded")
+ throw EncodingError.invalidValue(self.value, context)
+ }
+ }
+}
+
+extension AnyCodable: Equatable {
+ static public func ==(lhs: AnyCodable, rhs: AnyCodable) -> Bool {
+ switch (lhs.value, rhs.value) {
+ case is (Void, Void):
+ return true
+ case let (lhs as Bool, rhs as Bool):
+ return lhs == rhs
+ case let (lhs as Int, rhs as Int):
+ return lhs == rhs
+ case let (lhs as Int8, rhs as Int8):
+ return lhs == rhs
+ case let (lhs as Int16, rhs as Int16):
+ return lhs == rhs
+ case let (lhs as Int32, rhs as Int32):
+ return lhs == rhs
+ case let (lhs as Int64, rhs as Int64):
+ return lhs == rhs
+ case let (lhs as UInt, rhs as UInt):
+ return lhs == rhs
+ case let (lhs as UInt8, rhs as UInt8):
+ return lhs == rhs
+ case let (lhs as UInt16, rhs as UInt16):
+ return lhs == rhs
+ case let (lhs as UInt32, rhs as UInt32):
+ return lhs == rhs
+ case let (lhs as UInt64, rhs as UInt64):
+ return lhs == rhs
+ case let (lhs as Float, rhs as Float):
+ return lhs == rhs
+ case let (lhs as Double, rhs as Double):
+ return lhs == rhs
+ case let (lhs as String, rhs as String):
+ return lhs == rhs
+ case (let lhs as [String: AnyCodable], let rhs as [String: AnyCodable]):
+ return lhs == rhs
+ case (let lhs as [AnyCodable], let rhs as [AnyCodable]):
+ return lhs == rhs
+ default:
+ return false
+ }
+ }
+}
+
+extension AnyCodable: CustomStringConvertible {
+ public var description: String {
+ switch value {
+ case is Void:
+ return String(describing: nil as Any?)
+ case let value as CustomStringConvertible:
+ return value.description
+ default:
+ return String(describing: value)
+ }
+ }
+}
+
+extension AnyCodable: CustomDebugStringConvertible {
+ public var debugDescription: String {
+ switch value {
+ case let value as CustomDebugStringConvertible:
+ return "AnyCodable(\(value.debugDescription))"
+ default:
+ return "AnyCodable(\(self.description))"
+ }
+ }
+}
+
+extension AnyCodable: ExpressibleByNilLiteral, ExpressibleByBooleanLiteral, ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, ExpressibleByStringLiteral, ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {
+
+ public init(nilLiteral: ()) {
+ self.init(nil as Any?)
+ }
+
+ public init(booleanLiteral value: Bool) {
+ self.init(value)
+ }
+
+ public init(integerLiteral value: Int) {
+ self.init(value)
+ }
+
+ public init(floatLiteral value: Double) {
+ self.init(value)
+ }
+
+ public init(extendedGraphemeClusterLiteral value: String) {
+ self.init(value)
+ }
+
+ public init(stringLiteral value: String) {
+ self.init(value)
+ }
+
+ public init(arrayLiteral elements: Any...) {
+ self.init(elements)
+ }
+
+ public init(dictionaryLiteral elements: (AnyHashable, Any)...) {
+ self.init(Dictionary(elements, uniquingKeysWith: { (first, _) in first }))
+ }
+}
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/Coding.swift b/Specs/BinaryUpload/generated/Swift/Sources/Coding.swift
new file mode 100644
index 000000000..7aa59e4c8
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/Coding.swift
@@ -0,0 +1,367 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+import Foundation
+
+public protocol APIModel: Codable, Equatable { }
+
+public typealias DateTime = Date
+public typealias File = UploadFile
+public typealias ID = UUID
+
+public protocol ResponseDecoder {
+
+ func decode(_ type: T.Type, from: Data) throws -> T
+}
+
+extension JSONDecoder: ResponseDecoder {}
+
+public protocol RequestEncoder {
+
+ func encode(_ value: T) throws -> Data
+}
+
+extension JSONEncoder: RequestEncoder {}
+
+extension APIModel {
+ func encode() -> [String: Any] {
+ guard
+ let jsonData = try? JSONEncoder().encode(self),
+ let jsonValue = try? JSONSerialization.jsonObject(with: jsonData),
+ let jsonDictionary = jsonValue as? [String: Any] else {
+ return [:]
+ }
+ return jsonDictionary
+ }
+}
+
+struct StringCodingKey: CodingKey, ExpressibleByStringLiteral {
+
+ private let string: String
+ private let int: Int?
+
+ var stringValue: String { return string }
+
+ init(string: String) {
+ self.string = string
+ int = nil
+ }
+ init?(stringValue: String) {
+ string = stringValue
+ int = nil
+ }
+
+ var intValue: Int? { return int }
+ init?(intValue: Int) {
+ string = String(describing: intValue)
+ int = intValue
+ }
+
+ init(stringLiteral value: String) {
+ string = value
+ int = nil
+ }
+}
+
+// any json decoding
+extension ResponseDecoder {
+
+ func decodeAny(_ type: T.Type, from data: Data) throws -> T {
+ guard let decoded = try decode(AnyCodable.self, from: data) as? T else {
+ throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: [StringCodingKey(string: "")], debugDescription: "Decoding of \(T.self) failed"))
+ }
+ return decoded
+ }
+}
+
+// any decoding
+extension KeyedDecodingContainer {
+
+ func decodeAny(_ type: T.Type, forKey key: K) throws -> T {
+ guard let value = try decode(AnyCodable.self, forKey: key).value as? T else {
+ throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: codingPath, debugDescription: "Decoding of \(T.self) failed"))
+ }
+ return value
+ }
+
+ func decodeAnyIfPresent(_ type: T.Type, forKey key: K) throws -> T? {
+ return try decodeOptional {
+ guard let value = try decodeIfPresent(AnyCodable.self, forKey: key)?.value else { return nil }
+ if let typedValue = value as? T {
+ return typedValue
+ } else {
+ throw DecodingError.typeMismatch(T.self, DecodingError.Context(codingPath: codingPath, debugDescription: "Decoding of \(T.self) failed"))
+ }
+ }
+ }
+
+ func toDictionary() throws -> [String: Any] {
+ var dictionary: [String: Any] = [:]
+ for key in allKeys {
+ dictionary[key.stringValue] = try decodeAny(key)
+ }
+ return dictionary
+ }
+
+ func decode(_ key: KeyedDecodingContainer.Key) throws -> T where T: Decodable {
+ return try decode(T.self, forKey: key)
+ }
+
+ func decodeIfPresent(_ key: KeyedDecodingContainer.Key) throws -> T? where T: Decodable {
+ return try decodeOptional {
+ try decodeIfPresent(T.self, forKey: key)
+ }
+ }
+
+ func decodeAny(_ key: K) throws -> T {
+ return try decodeAny(T.self, forKey: key)
+ }
+
+ func decodeAnyIfPresent(_ key: K) throws -> T? {
+ return try decodeAnyIfPresent(T.self, forKey: key)
+ }
+
+ public func decodeArray(_ key: K) throws -> [T] {
+ var container: UnkeyedDecodingContainer
+ var array: [T] = []
+
+ do {
+ container = try nestedUnkeyedContainer(forKey: key)
+ } catch {
+ if BinaryUpload.safeArrayDecoding {
+ return array
+ } else {
+ throw error
+ }
+ }
+
+ while !container.isAtEnd {
+ do {
+ let element = try container.decode(T.self)
+ array.append(element)
+ } catch {
+ if BinaryUpload.safeArrayDecoding {
+ // hack to advance the current index
+ _ = try? container.decode(AnyCodable.self)
+ } else {
+ throw error
+ }
+ }
+ }
+ return array
+ }
+
+ public func decodeArrayIfPresent(_ key: K) throws -> [T]? {
+ return try decodeOptional {
+ if contains(key) {
+ return try decodeArray(key)
+ } else {
+ return nil
+ }
+ }
+ }
+
+ fileprivate func decodeOptional(_ closure: () throws -> T? ) throws -> T? {
+ if BinaryUpload.safeOptionalDecoding {
+ do {
+ return try closure()
+ } catch {
+ return nil
+ }
+ } else {
+ return try closure()
+ }
+ }
+}
+
+// any encoding
+extension KeyedEncodingContainer {
+
+ mutating func encodeAnyIfPresent(_ value: T?, forKey key: K) throws {
+ guard let value = value else { return }
+ try encodeIfPresent(AnyCodable(value), forKey: key)
+ }
+
+ mutating func encodeAny(_ value: T, forKey key: K) throws {
+ try encode(AnyCodable(value), forKey: key)
+ }
+}
+
+// Date structs for date and date-time formats
+
+extension DateFormatter {
+
+ convenience init(formatString: String, locale: Locale? = nil, timeZone: TimeZone? = nil, calendar: Calendar? = nil) {
+ self.init()
+ dateFormat = formatString
+ if let locale = locale {
+ self.locale = locale
+ }
+ if let timeZone = timeZone {
+ self.timeZone = timeZone
+ }
+ if let calendar = calendar {
+ self.calendar = calendar
+ }
+ }
+
+ convenience init(dateStyle: DateFormatter.Style, timeStyle: DateFormatter.Style) {
+ self.init()
+ self.dateStyle = dateStyle
+ self.timeStyle = timeStyle
+ }
+}
+
+let dateDecoder: (Decoder) throws -> Date = { decoder in
+ let container = try decoder.singleValueContainer()
+ let string = try container.decode(String.self)
+
+ let formatterWithMilliseconds = DateFormatter()
+ formatterWithMilliseconds.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
+ formatterWithMilliseconds.locale = Locale(identifier: "en_US_POSIX")
+ formatterWithMilliseconds.timeZone = TimeZone(identifier: "UTC")
+ formatterWithMilliseconds.calendar = Calendar(identifier: .gregorian)
+
+ let formatterWithoutMilliseconds = DateFormatter()
+ formatterWithoutMilliseconds.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
+ formatterWithoutMilliseconds.locale = Locale(identifier: "en_US_POSIX")
+ formatterWithoutMilliseconds.timeZone = TimeZone(identifier: "UTC")
+ formatterWithoutMilliseconds.calendar = Calendar(identifier: .gregorian)
+
+ guard let date = formatterWithMilliseconds.date(from: string) ??
+ formatterWithoutMilliseconds.date(from: string) else {
+ throw DecodingError.dataCorruptedError(in: container, debugDescription: "Could not decode date")
+ }
+ return date
+ }
+
+public struct DateDay: Codable, Comparable {
+
+ /// The date formatter used for encoding and decoding
+ public static let dateFormatter: DateFormatter = {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "yyyy-MM-dd"
+ formatter.calendar = .current
+ return formatter
+ }()
+
+ public let date: Date
+ public let year: Int
+ public let month: Int
+ public let day: Int
+
+ public init(date: Date = Date()) {
+ self.date = date
+ let dateComponents = Calendar.current.dateComponents([.day, .month, .year], from: date)
+ guard let year = dateComponents.year,
+ let month = dateComponents.month,
+ let day = dateComponents.day else {
+ fatalError("Date does not contain correct components")
+ }
+ self.year = year
+ self.month = month
+ self.day = day
+ }
+
+ public init(year: Int, month: Int, day: Int) {
+ let dateComponents = DateComponents(calendar: .current, year: year, month: month, day: day)
+ guard let date = dateComponents.date else {
+ fatalError("Could not create date in current calendar")
+ }
+ self.date = date
+ self.year = year
+ self.month = month
+ self.day = day
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+ let string = try container.decode(String.self)
+ guard let date = DateDay.dateFormatter.date(from: string) else {
+ throw DecodingError.dataCorruptedError(in: container, debugDescription: "Date not in correct format of \(DateDay.dateFormatter.dateFormat ?? "")")
+ }
+ self.init(date: date)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ let string = DateDay.dateFormatter.string(from: date)
+ try container.encode(string)
+ }
+
+ public static func == (lhs: DateDay, rhs: DateDay) -> Bool {
+ return lhs.year == rhs.year &&
+ lhs.month == rhs.month &&
+ lhs.day == rhs.day
+ }
+
+ public static func < (lhs: DateDay, rhs: DateDay) -> Bool {
+ return lhs.date < rhs.date
+ }
+}
+
+extension DateFormatter {
+
+ public func string(from dateDay: DateDay) -> String {
+ return string(from: dateDay.date)
+ }
+}
+
+// for parameter encoding
+
+extension DateDay {
+ func encode() -> Any {
+ return DateDay.dateFormatter.string(from: date)
+ }
+}
+
+extension Date {
+ func encode() -> Any {
+ return BinaryUpload.dateEncodingFormatter.string(from: self)
+ }
+}
+
+extension URL {
+ func encode() -> Any {
+ return absoluteString
+ }
+}
+
+extension RawRepresentable {
+ func encode() -> Any {
+ return rawValue
+ }
+}
+
+extension Array where Element: RawRepresentable {
+ func encode() -> [Any] {
+ return map { $0.rawValue }
+ }
+}
+
+extension Dictionary where Key == String, Value: RawRepresentable {
+ func encode() -> [String: Any] {
+ return mapValues { $0.rawValue }
+ }
+}
+
+extension UUID {
+ func encode() -> Any {
+ return uuidString
+ }
+}
+
+extension String {
+ func encode() -> Any {
+ return self
+ }
+}
+
+extension Data {
+
+ func encode() -> Any {
+ return self
+ }
+}
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/Models/UploadModel.swift b/Specs/BinaryUpload/generated/Swift/Sources/Models/UploadModel.swift
new file mode 100644
index 000000000..ce44bbdc7
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/Models/UploadModel.swift
@@ -0,0 +1,43 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+import Foundation
+
+public class UploadModel: APIModel {
+
+ public var binary: File?
+
+ public var id: String?
+
+ public init(binary: File? = nil, id: String? = nil) {
+ self.binary = binary
+ self.id = id
+ }
+
+ public required init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: StringCodingKey.self)
+
+ binary = try container.decodeIfPresent("binary")
+ id = try container.decodeIfPresent("id")
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: StringCodingKey.self)
+
+ try container.encodeIfPresent(binary, forKey: "binary")
+ try container.encodeIfPresent(id, forKey: "id")
+ }
+
+ public func isEqual(to object: Any?) -> Bool {
+ guard let object = object as? UploadModel else { return false }
+ guard self.binary == object.binary else { return false }
+ guard self.id == object.id else { return false }
+ return true
+ }
+
+ public static func == (lhs: UploadModel, rhs: UploadModel) -> Bool {
+ return lhs.isEqual(to: rhs)
+ }
+}
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/RequestBehaviour.swift b/Specs/BinaryUpload/generated/Swift/Sources/RequestBehaviour.swift
new file mode 100644
index 000000000..ed2d38a31
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/RequestBehaviour.swift
@@ -0,0 +1,192 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+import Foundation
+
+public protocol RequestBehaviour {
+
+ /// runs first and allows the requests to be modified. If modifying asynchronously use validate
+ func modifyRequest(request: AnyRequest, urlRequest: URLRequest) -> URLRequest
+
+ /// validates and modifies the request. complete must be called with either .success or .fail
+ func validate(request: AnyRequest, urlRequest: URLRequest, complete: @escaping (RequestValidationResult) -> Void)
+
+ /// called before request is sent
+ func beforeSend(request: AnyRequest)
+
+ /// called when request successfuly returns a 200 range response
+ func onSuccess(request: AnyRequest, result: Any)
+
+ /// called when request fails with an error. This will not be called if the request returns a known response even if the a status code is out of the 200 range
+ func onFailure(request: AnyRequest, error: APIClientError)
+
+ /// called if the request recieves a network response. This is not called if request fails validation or encoding
+ func onResponse(request: AnyRequest, response: AnyResponse)
+}
+
+public enum RequestValidationResult {
+ case success(URLRequest)
+ case failure(Error)
+}
+
+// Provides empty defaults so that each function becomes optional
+public extension RequestBehaviour {
+ func modifyRequest(request: AnyRequest, urlRequest: URLRequest) -> URLRequest { return urlRequest }
+ func validate(request: AnyRequest, urlRequest: URLRequest, complete: @escaping (RequestValidationResult) -> Void) {
+ complete(.success(urlRequest))
+ }
+ func beforeSend(request: AnyRequest) {}
+ func onSuccess(request: AnyRequest, result: Any) {}
+ func onFailure(request: AnyRequest, error: APIClientError) {}
+ func onResponse(request: AnyRequest, response: AnyResponse) {}
+}
+
+// Group different RequestBehaviours together
+struct RequestBehaviourGroup {
+
+ let request: AnyRequest
+ let behaviours: [RequestBehaviour]
+
+ init(request: APIRequest, behaviours: [RequestBehaviour]) {
+ self.request = request.asAny()
+ self.behaviours = behaviours
+ }
+
+ func beforeSend() {
+ behaviours.forEach {
+ $0.beforeSend(request: request)
+ }
+ }
+
+ func validate(_ urlRequest: URLRequest, complete: @escaping (RequestValidationResult) -> Void) {
+ if behaviours.isEmpty {
+ complete(.success(urlRequest))
+ return
+ }
+
+ var count = 0
+ var modifiedRequest = urlRequest
+ func validateNext() {
+ let behaviour = behaviours[count]
+ behaviour.validate(request: request, urlRequest: modifiedRequest) { result in
+ count += 1
+ switch result {
+ case .success(let urlRequest):
+ modifiedRequest = urlRequest
+ if count == self.behaviours.count {
+ complete(.success(modifiedRequest))
+ } else {
+ validateNext()
+ }
+ case .failure(let error):
+ complete(.failure(error))
+ }
+ }
+ }
+ validateNext()
+ }
+
+ func onSuccess(result: Any) {
+ behaviours.forEach {
+ $0.onSuccess(request: request, result: result)
+ }
+ }
+
+ func onFailure(error: APIClientError) {
+ behaviours.forEach {
+ $0.onFailure(request: request, error: error)
+ }
+ }
+
+ func onResponse(response: AnyResponse) {
+ behaviours.forEach {
+ $0.onResponse(request: request, response: response)
+ }
+ }
+
+ func modifyRequest(_ urlRequest: URLRequest) -> URLRequest {
+ var urlRequest = urlRequest
+ behaviours.forEach {
+ urlRequest = $0.modifyRequest(request: request, urlRequest: urlRequest)
+ }
+ return urlRequest
+ }
+}
+
+//MARK: Type erased Requests and Responses
+
+public typealias AnyResponse = APIResponse
+
+public class AnyRequest: APIRequest {
+ private let requestPath: String
+
+ override public var path: String {
+ return requestPath
+ }
+
+ init(request: APIRequest) {
+ requestPath = request.path
+ super.init(service: request.service.asAny(), queryParameters: request.queryParameters, formParameters: request.formParameters, headers: request.headers, encodeBody: request.encodeBody)
+ }
+}
+
+public struct AnyResponseValue: APIResponseValue, CustomDebugStringConvertible, CustomStringConvertible {
+
+ public typealias SuccessType = Any
+
+ public let statusCode: Int
+ public let successful: Bool
+ public let response: Any
+ public let responseEnum: Any
+ public let success: Any?
+
+ public init(statusCode: Int, successful: Bool, response: Any, responseEnum: Any, success: Any?) {
+ self.statusCode = statusCode
+ self.successful = successful
+ self.response = response
+ self.responseEnum = responseEnum
+ self.success = success
+ }
+
+ public init(statusCode: Int, data: Data, decoder: ResponseDecoder) throws {
+ fatalError()
+ }
+
+ public var description:String {
+ return "\(responseEnum)"
+ }
+
+ public var debugDescription: String {
+ if let debugDescription = responseEnum as? CustomDebugStringConvertible {
+ return debugDescription.debugDescription
+ } else {
+ return "\(responseEnum)"
+ }
+ }
+}
+
+extension APIResponseValue {
+ public func asAny() -> AnyResponseValue {
+ return AnyResponseValue(statusCode: statusCode, successful: successful, response: response, responseEnum: self, success: success)
+ }
+}
+
+extension APIResponse {
+ public func asAny() -> APIResponse {
+ return APIResponse(request: request.asAny(), result: result.map{ $0.asAny() }, urlRequest: urlRequest, urlResponse: urlResponse, data: data, metrics: metrics)
+ }
+}
+
+extension APIRequest {
+ public func asAny() -> AnyRequest {
+ return AnyRequest(request: self)
+ }
+}
+
+extension APIService {
+ public func asAny() -> APIService {
+ return APIService(id: id, tag: tag, method: method, path: path, hasBody: hasBody, securityRequirements: securityRequirements)
+ }
+}
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/Requests/PostUser.swift b/Specs/BinaryUpload/generated/Swift/Sources/Requests/PostUser.swift
new file mode 100644
index 000000000..54eaf0992
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/Requests/PostUser.swift
@@ -0,0 +1,120 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+import Foundation
+
+extension BinaryUpload {
+
+ /**
+ Create New User
+
+ Create a new user.
+ */
+ public enum PostUser {
+
+ public static let service = APIService(id: "post-user", tag: "", method: "POST", path: "/withoutModel", hasBody: true, securityRequirements: [])
+
+ public final class Request: APIRequest {
+
+ /** Create a new user. */
+ public class Body: APIModel {
+
+ public var binary: File
+
+ public init(binary: File) {
+ self.binary = binary
+ }
+
+ public required init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: StringCodingKey.self)
+
+ binary = try container.decode("binary")
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: StringCodingKey.self)
+
+ try container.encode(binary, forKey: "binary")
+ }
+
+ public func isEqual(to object: Any?) -> Bool {
+ guard let object = object as? Body else { return false }
+ guard self.binary == object.binary else { return false }
+ return true
+ }
+
+ public static func == (lhs: Body, rhs: Body) -> Bool {
+ return lhs.isEqual(to: rhs)
+ }
+ }
+
+ public var body: Body?
+
+ public init(body: Body?, encoder: RequestEncoder? = nil) {
+ self.body = body
+ super.init(service: PostUser.service) { defaultEncoder in
+ return try (encoder ?? defaultEncoder).encode(body)
+ }
+ }
+ }
+
+ public enum Response: APIResponseValue, CustomStringConvertible, CustomDebugStringConvertible {
+ public typealias SuccessType = Void
+
+ /** Missing Required Information */
+ case status400
+
+ /** Email Already Taken */
+ case status409
+
+ public var success: Void? {
+ switch self {
+ default: return nil
+ }
+ }
+
+ public var response: Any {
+ switch self {
+ default: return ()
+ }
+ }
+
+ public var statusCode: Int {
+ switch self {
+ case .status400: return 400
+ case .status409: return 409
+ }
+ }
+
+ public var successful: Bool {
+ switch self {
+ case .status400: return false
+ case .status409: return false
+ }
+ }
+
+ public init(statusCode: Int, data: Data, decoder: ResponseDecoder) throws {
+ switch statusCode {
+ case 400: self = .status400
+ case 409: self = .status409
+ default: throw APIClientError.unexpectedStatusCode(statusCode: statusCode, data: data)
+ }
+ }
+
+ public var description: String {
+ return "\(statusCode) \(successful ? "success" : "failure")"
+ }
+
+ public var debugDescription: String {
+ var string = description
+ let responseString = "\(response)"
+ if responseString != "()" {
+ string += "\n\(responseString)"
+ }
+ return string
+ }
+ }
+ }
+}
diff --git a/Specs/BinaryUpload/generated/Swift/Sources/Requests/PostWithType.swift b/Specs/BinaryUpload/generated/Swift/Sources/Requests/PostWithType.swift
new file mode 100644
index 000000000..4928f2de3
--- /dev/null
+++ b/Specs/BinaryUpload/generated/Swift/Sources/Requests/PostWithType.swift
@@ -0,0 +1,108 @@
+//
+// Generated by SwagGen
+// https://github.com/yonaskolb/SwagGen
+//
+
+import Foundation
+
+extension BinaryUpload {
+
+ public enum PostWithType {
+
+ public static let service = APIService(id: "post-withType", tag: "", method: "POST", path: "/withModel", hasBody: true, securityRequirements: [])
+
+ public final class Request: APIRequest {
+
+ public class Body: APIModel {
+
+ public var uploadData: UploadModel?
+
+ public init(uploadData: UploadModel? = nil) {
+ self.uploadData = uploadData
+ }
+
+ public required init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: StringCodingKey.self)
+
+ uploadData = try container.decodeIfPresent("uploadData")
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: StringCodingKey.self)
+
+ try container.encodeIfPresent(uploadData, forKey: "uploadData")
+ }
+
+ public func isEqual(to object: Any?) -> Bool {
+ guard let object = object as? Body else { return false }
+ guard self.uploadData == object.uploadData else { return false }
+ return true
+ }
+
+ public static func == (lhs: Body, rhs: Body) -> Bool {
+ return lhs.isEqual(to: rhs)
+ }
+ }
+
+ public var body: Body?
+
+ public init(body: Body?, encoder: RequestEncoder? = nil) {
+ self.body = body
+ super.init(service: PostWithType.service) { defaultEncoder in
+ return try (encoder ?? defaultEncoder).encode(body)
+ }
+ }
+ }
+
+ public enum Response: APIResponseValue, CustomStringConvertible, CustomDebugStringConvertible {
+ public typealias SuccessType = Void
+
+ /** OK */
+ case status200
+
+ public var success: Void? {
+ switch self {
+ case .status200: return ()
+ }
+ }
+
+ public var response: Any {
+ switch self {
+ default: return ()
+ }
+ }
+
+ public var statusCode: Int {
+ switch self {
+ case .status200: return 200
+ }
+ }
+
+ public var successful: Bool {
+ switch self {
+ case .status200: return true
+ }
+ }
+
+ public init(statusCode: Int, data: Data, decoder: ResponseDecoder) throws {
+ switch statusCode {
+ case 200: self = .status200
+ default: throw APIClientError.unexpectedStatusCode(statusCode: statusCode, data: data)
+ }
+ }
+
+ public var description: String {
+ return "\(statusCode) \(successful ? "success" : "failure")"
+ }
+
+ public var debugDescription: String {
+ var string = description
+ let responseString = "\(response)"
+ if responseString != "()" {
+ string += "\n\(responseString)"
+ }
+ return string
+ }
+ }
+ }
+}
diff --git a/Specs/BinaryUpload/spec.yml b/Specs/BinaryUpload/spec.yml
new file mode 100644
index 000000000..37c508dd3
--- /dev/null
+++ b/Specs/BinaryUpload/spec.yml
@@ -0,0 +1,64 @@
+openapi: 3.1.0
+info:
+ title: test
+ version: '1.0'
+servers:
+ - url: 'http://localhost:3000'
+paths:
+ /withoutModel:
+ post:
+ summary: Create New User
+ operationId: post-user
+ responses:
+ '400':
+ description: Missing Required Information
+ '409':
+ description: Email Already Taken
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ binary:
+ type: string
+ format: binary
+ required:
+ - binary
+ examples:
+ Create User Bob Fellow:
+ value:
+ firstName: Bob
+ lastName: Fellow
+ email: bob.fellow@gmail.com
+ dateOfBirth: '1996-08-24'
+ description: Post the necessary fields for the API to create a new user.
+ description: Create a new user.
+ parameters: []
+ /withModel:
+ post:
+ summary: ''
+ operationId: post-withType
+ responses:
+ '200':
+ description: OK
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ uploadData:
+ $ref: '#/components/schemas/UploadModel'
+ parameters: []
+components:
+ schemas:
+ UploadModel:
+ title: UploadModel
+ type: object
+ properties:
+ id:
+ type: string
+ binary:
+ type: string
+ format: binary
diff --git a/Specs/Petstore/generated/Swift/Sources/APIRequest.swift b/Specs/Petstore/generated/Swift/Sources/APIRequest.swift
index e529d1cb0..49097b6c1 100644
--- a/Specs/Petstore/generated/Swift/Sources/APIRequest.swift
+++ b/Specs/Petstore/generated/Swift/Sources/APIRequest.swift
@@ -60,7 +60,7 @@ extension APIRequest: CustomDebugStringConvertible {
}
/// A file upload
-public struct UploadFile: Equatable {
+public struct UploadFile: Equatable, Codable {
public let type: FileType
public let fileName: String?
@@ -78,9 +78,51 @@ public struct UploadFile: Equatable {
self.mimeType = mimeType
}
- public enum FileType: Equatable {
+ public enum FileType: Equatable, Codable {
case data(Data)
case url(URL)
+
+ enum CodingKeys: CodingKey {
+ case data
+ case url
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ switch self {
+ case let .data(value):
+ try container.encode(value, forKey: .data)
+ case let .url(value):
+ try container.encode(value, forKey: .url)
+ }
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let key = container.allKeys.first
+
+ switch key {
+ case .data:
+ let value = try container.decode(
+ Data.self,
+ forKey: .data
+ )
+ self = .data(value)
+ case .url:
+ let value = try container.decode(
+ URL.self,
+ forKey: .url
+ )
+ self = .url(value)
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: container.codingPath,
+ debugDescription: "Unabled to decode FileType."
+ )
+ )
+ }
+ }
}
func encode() -> Any {
diff --git a/Specs/PetstoreTest/generated/Swift/Sources/APIRequest.swift b/Specs/PetstoreTest/generated/Swift/Sources/APIRequest.swift
index e529d1cb0..49097b6c1 100644
--- a/Specs/PetstoreTest/generated/Swift/Sources/APIRequest.swift
+++ b/Specs/PetstoreTest/generated/Swift/Sources/APIRequest.swift
@@ -60,7 +60,7 @@ extension APIRequest: CustomDebugStringConvertible {
}
/// A file upload
-public struct UploadFile: Equatable {
+public struct UploadFile: Equatable, Codable {
public let type: FileType
public let fileName: String?
@@ -78,9 +78,51 @@ public struct UploadFile: Equatable {
self.mimeType = mimeType
}
- public enum FileType: Equatable {
+ public enum FileType: Equatable, Codable {
case data(Data)
case url(URL)
+
+ enum CodingKeys: CodingKey {
+ case data
+ case url
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ switch self {
+ case let .data(value):
+ try container.encode(value, forKey: .data)
+ case let .url(value):
+ try container.encode(value, forKey: .url)
+ }
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let key = container.allKeys.first
+
+ switch key {
+ case .data:
+ let value = try container.decode(
+ Data.self,
+ forKey: .data
+ )
+ self = .data(value)
+ case .url:
+ let value = try container.decode(
+ URL.self,
+ forKey: .url
+ )
+ self = .url(value)
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: container.codingPath,
+ debugDescription: "Unabled to decode FileType."
+ )
+ )
+ }
+ }
}
func encode() -> Any {
diff --git a/Specs/Rocket/generated/Swift/Sources/APIRequest.swift b/Specs/Rocket/generated/Swift/Sources/APIRequest.swift
index e529d1cb0..49097b6c1 100644
--- a/Specs/Rocket/generated/Swift/Sources/APIRequest.swift
+++ b/Specs/Rocket/generated/Swift/Sources/APIRequest.swift
@@ -60,7 +60,7 @@ extension APIRequest: CustomDebugStringConvertible {
}
/// A file upload
-public struct UploadFile: Equatable {
+public struct UploadFile: Equatable, Codable {
public let type: FileType
public let fileName: String?
@@ -78,9 +78,51 @@ public struct UploadFile: Equatable {
self.mimeType = mimeType
}
- public enum FileType: Equatable {
+ public enum FileType: Equatable, Codable {
case data(Data)
case url(URL)
+
+ enum CodingKeys: CodingKey {
+ case data
+ case url
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ switch self {
+ case let .data(value):
+ try container.encode(value, forKey: .data)
+ case let .url(value):
+ try container.encode(value, forKey: .url)
+ }
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let key = container.allKeys.first
+
+ switch key {
+ case .data:
+ let value = try container.decode(
+ Data.self,
+ forKey: .data
+ )
+ self = .data(value)
+ case .url:
+ let value = try container.decode(
+ URL.self,
+ forKey: .url
+ )
+ self = .url(value)
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: container.codingPath,
+ debugDescription: "Unabled to decode FileType."
+ )
+ )
+ }
+ }
}
func encode() -> Any {
diff --git a/Specs/TBX/generated/Swift/Sources/APIRequest.swift b/Specs/TBX/generated/Swift/Sources/APIRequest.swift
index e529d1cb0..49097b6c1 100644
--- a/Specs/TBX/generated/Swift/Sources/APIRequest.swift
+++ b/Specs/TBX/generated/Swift/Sources/APIRequest.swift
@@ -60,7 +60,7 @@ extension APIRequest: CustomDebugStringConvertible {
}
/// A file upload
-public struct UploadFile: Equatable {
+public struct UploadFile: Equatable, Codable {
public let type: FileType
public let fileName: String?
@@ -78,9 +78,51 @@ public struct UploadFile: Equatable {
self.mimeType = mimeType
}
- public enum FileType: Equatable {
+ public enum FileType: Equatable, Codable {
case data(Data)
case url(URL)
+
+ enum CodingKeys: CodingKey {
+ case data
+ case url
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ switch self {
+ case let .data(value):
+ try container.encode(value, forKey: .data)
+ case let .url(value):
+ try container.encode(value, forKey: .url)
+ }
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let key = container.allKeys.first
+
+ switch key {
+ case .data:
+ let value = try container.decode(
+ Data.self,
+ forKey: .data
+ )
+ self = .data(value)
+ case .url:
+ let value = try container.decode(
+ URL.self,
+ forKey: .url
+ )
+ self = .url(value)
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: container.codingPath,
+ debugDescription: "Unabled to decode FileType."
+ )
+ )
+ }
+ }
}
func encode() -> Any {
diff --git a/Specs/TFL/generated/Swift/Sources/APIRequest.swift b/Specs/TFL/generated/Swift/Sources/APIRequest.swift
index e529d1cb0..49097b6c1 100644
--- a/Specs/TFL/generated/Swift/Sources/APIRequest.swift
+++ b/Specs/TFL/generated/Swift/Sources/APIRequest.swift
@@ -60,7 +60,7 @@ extension APIRequest: CustomDebugStringConvertible {
}
/// A file upload
-public struct UploadFile: Equatable {
+public struct UploadFile: Equatable, Codable {
public let type: FileType
public let fileName: String?
@@ -78,9 +78,51 @@ public struct UploadFile: Equatable {
self.mimeType = mimeType
}
- public enum FileType: Equatable {
+ public enum FileType: Equatable, Codable {
case data(Data)
case url(URL)
+
+ enum CodingKeys: CodingKey {
+ case data
+ case url
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ switch self {
+ case let .data(value):
+ try container.encode(value, forKey: .data)
+ case let .url(value):
+ try container.encode(value, forKey: .url)
+ }
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let key = container.allKeys.first
+
+ switch key {
+ case .data:
+ let value = try container.decode(
+ Data.self,
+ forKey: .data
+ )
+ self = .data(value)
+ case .url:
+ let value = try container.decode(
+ URL.self,
+ forKey: .url
+ )
+ self = .url(value)
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: container.codingPath,
+ debugDescription: "Unabled to decode FileType."
+ )
+ )
+ }
+ }
}
func encode() -> Any {
diff --git a/Specs/TestSpec/generated/Swift/Sources/APIRequest.swift b/Specs/TestSpec/generated/Swift/Sources/APIRequest.swift
index e529d1cb0..49097b6c1 100644
--- a/Specs/TestSpec/generated/Swift/Sources/APIRequest.swift
+++ b/Specs/TestSpec/generated/Swift/Sources/APIRequest.swift
@@ -60,7 +60,7 @@ extension APIRequest: CustomDebugStringConvertible {
}
/// A file upload
-public struct UploadFile: Equatable {
+public struct UploadFile: Equatable, Codable {
public let type: FileType
public let fileName: String?
@@ -78,9 +78,51 @@ public struct UploadFile: Equatable {
self.mimeType = mimeType
}
- public enum FileType: Equatable {
+ public enum FileType: Equatable, Codable {
case data(Data)
case url(URL)
+
+ enum CodingKeys: CodingKey {
+ case data
+ case url
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ switch self {
+ case let .data(value):
+ try container.encode(value, forKey: .data)
+ case let .url(value):
+ try container.encode(value, forKey: .url)
+ }
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let key = container.allKeys.first
+
+ switch key {
+ case .data:
+ let value = try container.decode(
+ Data.self,
+ forKey: .data
+ )
+ self = .data(value)
+ case .url:
+ let value = try container.decode(
+ URL.self,
+ forKey: .url
+ )
+ self = .url(value)
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: container.codingPath,
+ debugDescription: "Unabled to decode FileType."
+ )
+ )
+ }
+ }
}
func encode() -> Any {
diff --git a/Templates/Swift/Sources/APIRequest.swift b/Templates/Swift/Sources/APIRequest.swift
index 9795e84b5..ed0a0a049 100644
--- a/Templates/Swift/Sources/APIRequest.swift
+++ b/Templates/Swift/Sources/APIRequest.swift
@@ -57,7 +57,7 @@ extension APIRequest: CustomDebugStringConvertible {
}
/// A file upload
-public struct UploadFile: Equatable {
+public struct UploadFile: Equatable, Codable {
public let type: FileType
public let fileName: String?
@@ -75,9 +75,51 @@ public struct UploadFile: Equatable {
self.mimeType = mimeType
}
- public enum FileType: Equatable {
+ public enum FileType: Equatable, Codable {
case data(Data)
case url(URL)
+
+ enum CodingKeys: CodingKey {
+ case data
+ case url
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ switch self {
+ case let .data(value):
+ try container.encode(value, forKey: .data)
+ case let .url(value):
+ try container.encode(value, forKey: .url)
+ }
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let key = container.allKeys.first
+
+ switch key {
+ case .data:
+ let value = try container.decode(
+ Data.self,
+ forKey: .data
+ )
+ self = .data(value)
+ case .url:
+ let value = try container.decode(
+ URL.self,
+ forKey: .url
+ )
+ self = .url(value)
+ default:
+ throw DecodingError.dataCorrupted(
+ DecodingError.Context(
+ codingPath: container.codingPath,
+ debugDescription: "Unabled to decode FileType."
+ )
+ )
+ }
+ }
}
func encode() -> Any {