forked from loafle/openapi-generator-original
[Swift4] Expanding CodableHelper with added date and JSON customisations (#3365)
* Expanding CodableHelper with a more customisable dateFormatter and JSON en-/decoder. * Ran ./bin/swift4-petstore.sh * Ran ./bin/swift4-petstore-all.sh again after merge from master. * Ran ./bin/swift4-petstore-all.sh again after building. * Ran ./bin/swift4-petstore-all.sh again after rebase latest from upstream master. * sync master, update samples * Built and ran ./bin/swift4-petstore-all.sh * Re-adding code which disappeared in rebase from master. * Fixed test * [swift] remove old classes
This commit is contained in:
committed by
William Cheng
parent
5f5401bca6
commit
f6dbd48b9c
@@ -11,27 +11,48 @@ import Foundation
|
|||||||
|
|
||||||
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class CodableHelper {
|
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class CodableHelper {
|
||||||
|
|
||||||
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var dateformatter: DateFormatter?
|
private static var customDateFormatter: DateFormatter?
|
||||||
|
private static var defaultDateFormatter: DateFormatter = {
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
dateFormatter.calendar = Calendar(identifier: .iso8601)
|
||||||
|
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
|
||||||
|
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
|
||||||
|
dateFormatter.dateFormat = Configuration.dateFormat
|
||||||
|
return dateFormatter
|
||||||
|
}()
|
||||||
|
private static var customJSONDecoder: JSONDecoder?
|
||||||
|
private static var defaultJSONDecoder: JSONDecoder = {
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
decoder.dateDecodingStrategy = .formatted(CodableHelper.dateFormatter)
|
||||||
|
return decoder
|
||||||
|
}()
|
||||||
|
private static var customJSONEncoder: JSONEncoder?
|
||||||
|
private static var defaultJSONEncoder: JSONEncoder = {
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
encoder.dateEncodingStrategy = .formatted(CodableHelper.dateFormatter)
|
||||||
|
encoder.outputFormatting = .prettyPrinted
|
||||||
|
return encoder
|
||||||
|
}()
|
||||||
|
|
||||||
|
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var dateFormatter: DateFormatter {
|
||||||
|
get { return self.customDateFormatter ?? self.defaultDateFormatter }
|
||||||
|
set { self.customDateFormatter = newValue }
|
||||||
|
}
|
||||||
|
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var jsonDecoder: JSONDecoder {
|
||||||
|
get { return self.customJSONDecoder ?? self.defaultJSONDecoder }
|
||||||
|
set { self.customJSONDecoder = newValue }
|
||||||
|
}
|
||||||
|
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} static var jsonEncoder: JSONEncoder {
|
||||||
|
get { return self.customJSONEncoder ?? self.defaultJSONEncoder }
|
||||||
|
set { self.customJSONEncoder = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func decode<T>(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T : Decodable {
|
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func decode<T>(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T : Decodable {
|
||||||
var returnedDecodable: T? = nil
|
var returnedDecodable: T? = nil
|
||||||
var returnedError: Error? = nil
|
var returnedError: Error? = nil
|
||||||
|
|
||||||
let decoder = JSONDecoder()
|
|
||||||
if let df = self.dateformatter {
|
|
||||||
decoder.dateDecodingStrategy = .formatted(df)
|
|
||||||
} else {
|
|
||||||
decoder.dataDecodingStrategy = .base64
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.calendar = Calendar(identifier: .iso8601)
|
|
||||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
||||||
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
|
||||||
formatter.dateFormat = Configuration.dateFormat
|
|
||||||
decoder.dateDecodingStrategy = .formatted(formatter)
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
returnedDecodable = try decoder.decode(type, from: data)
|
returnedDecodable = try self.jsonDecoder.decode(type, from: data)
|
||||||
} catch {
|
} catch {
|
||||||
returnedError = error
|
returnedError = error
|
||||||
}
|
}
|
||||||
@@ -39,33 +60,16 @@ import Foundation
|
|||||||
return (returnedDecodable, returnedError)
|
return (returnedDecodable, returnedError)
|
||||||
}
|
}
|
||||||
|
|
||||||
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func encode<T>(_ value: T, prettyPrint: Bool = false) -> EncodeResult where T : Encodable {
|
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}open{{/nonPublicApi}} class func encode<T>(_ value: T) -> EncodeResult where T : Encodable {
|
||||||
var returnedData: Data?
|
var returnedData: Data?
|
||||||
var returnedError: Error? = nil
|
var returnedError: Error? = nil
|
||||||
|
|
||||||
let encoder = JSONEncoder()
|
|
||||||
if prettyPrint {
|
|
||||||
encoder.outputFormatting = .prettyPrinted
|
|
||||||
}
|
|
||||||
if let df = self.dateformatter {
|
|
||||||
encoder.dateEncodingStrategy = .formatted(df)
|
|
||||||
} else {
|
|
||||||
encoder.dataEncodingStrategy = .base64
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.calendar = Calendar(identifier: .iso8601)
|
|
||||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
||||||
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
|
||||||
formatter.dateFormat = Configuration.dateFormat
|
|
||||||
encoder.dateEncodingStrategy = .formatted(formatter)
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
returnedData = try encoder.encode(value)
|
returnedData = try self.jsonEncoder.encode(value)
|
||||||
} catch {
|
} catch {
|
||||||
returnedError = error
|
returnedError = error
|
||||||
}
|
}
|
||||||
|
|
||||||
return (returnedData, returnedError)
|
return (returnedData, returnedError)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -69,22 +69,9 @@ extension Data: JSONEncodable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let dateFormatter: DateFormatter = {
|
|
||||||
if let formatter = CodableHelper.dateformatter {
|
|
||||||
return formatter
|
|
||||||
} else {
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.calendar = Calendar(identifier: .iso8601)
|
|
||||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
||||||
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
|
||||||
formatter.dateFormat = Configuration.dateFormat
|
|
||||||
return formatter
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
extension Date: JSONEncodable {
|
extension Date: JSONEncodable {
|
||||||
func encodeToJSON() -> Any {
|
func encodeToJSON() -> Any {
|
||||||
return dateFormatter.string(from: self) as Any
|
return CodableHelper.dateFormatter.string(from: self) as Any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import Alamofire
|
|||||||
|
|
||||||
// Encode the Encodable object
|
// Encode the Encodable object
|
||||||
if let encodableObj = encodableObj {
|
if let encodableObj = encodableObj {
|
||||||
let encodeResult = CodableHelper.encode(encodableObj, prettyPrint: true)
|
let encodeResult = CodableHelper.encode(encodableObj)
|
||||||
if encodeResult.error == nil {
|
if encodeResult.error == nil {
|
||||||
params = JSONDataEncoding.encodingParameters(jsonData: encodeResult.data)
|
params = JSONDataEncoding.encodingParameters(jsonData: encodeResult.data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
{{#description}}
|
{{#description}}
|
||||||
|
|
||||||
/** {{description}} */{{/description}}
|
/** {{description}} */{{/description}}
|
||||||
{{#isArrayModel}}
|
{{#isArrayModel}}
|
||||||
{{> modelArray}}
|
{{> modelArray}}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} struct {{classname}}: Codable {
|
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} struct {{classname}}: Codable {
|
||||||
|
|
||||||
{{#allVars}}
|
{{#allVars}}
|
||||||
@@ -78,5 +77,4 @@
|
|||||||
case {{name}}{{#vendorExtensions.x-codegen-escaped-property-name}} = "{{{baseName}}}"{{/vendorExtensions.x-codegen-escaped-property-name}}{{/allVars}}
|
case {{name}}{{#vendorExtensions.x-codegen-escaped-property-name}} = "{{{baseName}}}"{{/vendorExtensions.x-codegen-escaped-property-name}}{{/allVars}}
|
||||||
}
|
}
|
||||||
{{/vendorExtensions.x-codegen-has-escaped-property-names}}{{/additionalPropertiesType}}
|
{{/vendorExtensions.x-codegen-has-escaped-property-names}}{{/additionalPropertiesType}}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
4.1.3-SNAPSHOT
|
4.2.0-SNAPSHOT
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
*
|
*
|
||||||
* OpenAPI spec version: 1.0.0
|
* The version of the OpenAPI document: 1.0.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
*
|
*
|
||||||
* OpenAPI spec version: 1.0.0
|
* The version of the OpenAPI document: 1.0.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
@@ -25,12 +25,13 @@ import com.sun.jersey.multipart.file.FileDataBodyPart
|
|||||||
import com.wordnik.swagger.client._
|
import com.wordnik.swagger.client._
|
||||||
import com.wordnik.swagger.client.ClientResponseReaders.Json4sFormatsReader._
|
import com.wordnik.swagger.client.ClientResponseReaders.Json4sFormatsReader._
|
||||||
import com.wordnik.swagger.client.RequestWriters.Json4sFormatsWriter._
|
import com.wordnik.swagger.client.RequestWriters.Json4sFormatsWriter._
|
||||||
|
import javax.ws.rs.core.Response.Status.Family
|
||||||
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.{MediaType, Response}
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent._
|
import scala.concurrent._
|
||||||
@@ -309,7 +310,11 @@ class PetApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +336,11 @@ class PetApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("DELETE", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("DELETE", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,7 +357,11 @@ class PetApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +378,11 @@ class PetApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +398,11 @@ class PetApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +418,11 @@ class PetApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("PUT", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
val resFuture = client.submit("PUT", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,7 +441,11 @@ class PetApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,7 +464,11 @@ class PetApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
*
|
*
|
||||||
* OpenAPI spec version: 1.0.0
|
* The version of the OpenAPI document: 1.0.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
@@ -23,12 +23,13 @@ import com.sun.jersey.multipart.file.FileDataBodyPart
|
|||||||
import com.wordnik.swagger.client._
|
import com.wordnik.swagger.client._
|
||||||
import com.wordnik.swagger.client.ClientResponseReaders.Json4sFormatsReader._
|
import com.wordnik.swagger.client.ClientResponseReaders.Json4sFormatsReader._
|
||||||
import com.wordnik.swagger.client.RequestWriters.Json4sFormatsWriter._
|
import com.wordnik.swagger.client.RequestWriters.Json4sFormatsWriter._
|
||||||
|
import javax.ws.rs.core.Response.Status.Family
|
||||||
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.{MediaType, Response}
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent._
|
import scala.concurrent._
|
||||||
@@ -193,7 +194,11 @@ class StoreApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extend
|
|||||||
|
|
||||||
val resFuture = client.submit("DELETE", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("DELETE", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +213,11 @@ class StoreApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extend
|
|||||||
|
|
||||||
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +233,11 @@ class StoreApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extend
|
|||||||
|
|
||||||
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +253,11 @@ class StoreApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extend
|
|||||||
|
|
||||||
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
*
|
*
|
||||||
* OpenAPI spec version: 1.0.0
|
* The version of the OpenAPI document: 1.0.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
@@ -23,12 +23,13 @@ import com.sun.jersey.multipart.file.FileDataBodyPart
|
|||||||
import com.wordnik.swagger.client._
|
import com.wordnik.swagger.client._
|
||||||
import com.wordnik.swagger.client.ClientResponseReaders.Json4sFormatsReader._
|
import com.wordnik.swagger.client.ClientResponseReaders.Json4sFormatsReader._
|
||||||
import com.wordnik.swagger.client.RequestWriters.Json4sFormatsWriter._
|
import com.wordnik.swagger.client.RequestWriters.Json4sFormatsWriter._
|
||||||
|
import javax.ws.rs.core.Response.Status.Family
|
||||||
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
import javax.ws.rs.core.MediaType
|
import javax.ws.rs.core.{MediaType, Response}
|
||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent._
|
import scala.concurrent._
|
||||||
@@ -299,7 +300,11 @@ class UserApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +320,11 @@ class UserApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +340,11 @@ class UserApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
val resFuture = client.submit("POST", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,7 +362,11 @@ class UserApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("DELETE", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("DELETE", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,7 +384,11 @@ class UserApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,7 +410,11 @@ class UserApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,7 +429,11 @@ class UserApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
val resFuture = client.submit("GET", path, queryParams.toMap, headerParams.toMap, "")
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,7 +453,11 @@ class UserApiAsyncHelper(client: TransportClient, config: SwaggerConfig) extends
|
|||||||
|
|
||||||
val resFuture = client.submit("PUT", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
val resFuture = client.submit("PUT", path, queryParams.toMap, headerParams.toMap, writer.write(body))
|
||||||
resFuture flatMap { resp =>
|
resFuture flatMap { resp =>
|
||||||
process(reader.read(resp))
|
val status = Response.Status.fromStatusCode(resp.statusCode)
|
||||||
|
status.getFamily match {
|
||||||
|
case Family.SUCCESSFUL | Family.REDIRECTION | Family.INFORMATIONAL => process(reader.read(resp))
|
||||||
|
case _ => throw new ApiException(resp.statusCode, resp.statusText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
*
|
*
|
||||||
* OpenAPI spec version: 1.0.0
|
* The version of the OpenAPI document: 1.0.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
*
|
*
|
||||||
* OpenAPI spec version: 1.0.0
|
* The version of the OpenAPI document: 1.0.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
*
|
*
|
||||||
* OpenAPI spec version: 1.0.0
|
* The version of the OpenAPI document: 1.0.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
*
|
*
|
||||||
* OpenAPI spec version: 1.0.0
|
* The version of the OpenAPI document: 1.0.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
*
|
*
|
||||||
* OpenAPI spec version: 1.0.0
|
* The version of the OpenAPI document: 1.0.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
*
|
*
|
||||||
* OpenAPI spec version: 1.0.0
|
* The version of the OpenAPI document: 1.0.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
@@ -7,5 +7,4 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public typealias AnimalFarm = [Animal]
|
public typealias AnimalFarm = [Animal]
|
||||||
|
|||||||
@@ -7,5 +7,4 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public typealias AnimalFarm = [Animal]
|
public typealias AnimalFarm = [Animal]
|
||||||
|
|||||||
@@ -10,4 +10,3 @@
|
|||||||
public struct Disposables {
|
public struct Disposables {
|
||||||
private init() {}
|
private init() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
/// Represents a disposable that does nothing on disposal.
|
/// Represents a disposable that does nothing on disposal.
|
||||||
///
|
///
|
||||||
/// Nop = No Operation
|
/// Nop = No Operation
|
||||||
fileprivate struct NopDisposable : Disposable {
|
private struct NopDisposable: Disposable {
|
||||||
|
|
||||||
fileprivate static let noOp: Disposable = NopDisposable()
|
fileprivate static let noOp: Disposable = NopDisposable()
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ let RxErrorDomain = "RxErrorDomain"
|
|||||||
let RxCompositeFailures = "RxCompositeFailures"
|
let RxCompositeFailures = "RxCompositeFailures"
|
||||||
|
|
||||||
/// Generic Rx error codes.
|
/// Generic Rx error codes.
|
||||||
public enum RxError
|
public enum RxError: Swift.Error, CustomDebugStringConvertible {
|
||||||
: Swift.Error
|
|
||||||
, CustomDebugStringConvertible {
|
|
||||||
/// Unknown error occurred.
|
/// Unknown error occurred.
|
||||||
case unknown
|
case unknown
|
||||||
/// Performing an action on disposed object.
|
/// Performing an action on disposed object.
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
protocol ScheduledItemType
|
protocol ScheduledItemType: Cancelable, InvocableType {
|
||||||
: Cancelable
|
|
||||||
, InvocableType {
|
|
||||||
func invoke()
|
func invoke()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
4.2.2-SNAPSHOT
|
4.2.3-SNAPSHOT
|
||||||
@@ -9,11 +9,11 @@ let package = Package(
|
|||||||
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
||||||
.library(
|
.library(
|
||||||
name: "PetstoreClient",
|
name: "PetstoreClient",
|
||||||
targets: ["PetstoreClient"])
|
targets: ["PetstoreClient"]),
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
// Dependencies declare other packages that this package depends on.
|
// Dependencies declare other packages that this package depends on.
|
||||||
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.9.0")
|
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.9.0"),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||||
@@ -22,6 +22,6 @@ let package = Package(
|
|||||||
name: "PetstoreClient",
|
name: "PetstoreClient",
|
||||||
dependencies: ["Alamofire"],
|
dependencies: ["Alamofire"],
|
||||||
path: "PetstoreClient/Classes"
|
path: "PetstoreClient/Classes"
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public struct APIHelper {
|
|||||||
|
|
||||||
public static func rejectNilHeaders(_ source: [String:Any?]) -> [String:String] {
|
public static func rejectNilHeaders(_ source: [String:Any?]) -> [String:String] {
|
||||||
return source.reduce(into: [String: String]()) { (result, item) in
|
return source.reduce(into: [String: String]()) { (result, item) in
|
||||||
if let collection = item.value as? [Any?] {
|
if let collection = item.value as? Array<Any?> {
|
||||||
result[item.key] = collection.filter({ $0 != nil }).map{ "\($0!)" }.joined(separator: ",")
|
result[item.key] = collection.filter({ $0 != nil }).map{ "\($0!)" }.joined(separator: ",")
|
||||||
} else if let value: Any = item.value {
|
} else if let value: Any = item.value {
|
||||||
result[item.key] = "\(value)"
|
result[item.key] = "\(value)"
|
||||||
@@ -46,7 +46,7 @@ public struct APIHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static func mapValueToPathItem(_ source: Any) -> Any {
|
public static func mapValueToPathItem(_ source: Any) -> Any {
|
||||||
if let collection = source as? [Any?] {
|
if let collection = source as? Array<Any?> {
|
||||||
return collection.filter({ $0 != nil }).map({"\($0!)"}).joined(separator: ",")
|
return collection.filter({ $0 != nil }).map({"\($0!)"}).joined(separator: ",")
|
||||||
}
|
}
|
||||||
return source
|
return source
|
||||||
@@ -54,7 +54,7 @@ public struct APIHelper {
|
|||||||
|
|
||||||
public static func mapValuesToQueryItems(_ source: [String:Any?]) -> [URLQueryItem]? {
|
public static func mapValuesToQueryItems(_ source: [String:Any?]) -> [URLQueryItem]? {
|
||||||
let destination = source.filter({ $0.value != nil}).reduce(into: [URLQueryItem]()) { (result, item) in
|
let destination = source.filter({ $0.value != nil}).reduce(into: [URLQueryItem]()) { (result, item) in
|
||||||
if let collection = item.value as? [Any?] {
|
if let collection = item.value as? Array<Any?> {
|
||||||
let value = collection.filter({ $0 != nil }).map({"\($0!)"}).joined(separator: ",")
|
let value = collection.filter({ $0 != nil }).map({"\($0!)"}).joined(separator: ",")
|
||||||
result.append(URLQueryItem(name: item.key, value: value))
|
result.append(URLQueryItem(name: item.key, value: value))
|
||||||
} else if let value = item.value {
|
} else if let value = item.value {
|
||||||
@@ -68,3 +68,4 @@ public struct APIHelper {
|
|||||||
return destination
|
return destination
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ open class RequestBuilder<T> {
|
|||||||
public let URLString: String
|
public let URLString: String
|
||||||
|
|
||||||
/// Optional block to obtain a reference to the request's progress instance when available.
|
/// Optional block to obtain a reference to the request's progress instance when available.
|
||||||
public var onProgressReady: ((Progress) -> Void)?
|
public var onProgressReady: ((Progress) -> ())?
|
||||||
|
|
||||||
required public init(method: String, URLString: String, parameters: [String:Any]?, isBody: Bool, headers: [String:String] = [:]) {
|
required public init(method: String, URLString: String, parameters: [String:Any]?, isBody: Bool, headers: [String:String] = [:]) {
|
||||||
self.method = method
|
self.method = method
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
open class AnotherFakeAPI {
|
open class AnotherFakeAPI {
|
||||||
/**
|
/**
|
||||||
To test special tags
|
To test special tags
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
open class FakeAPI {
|
open class FakeAPI {
|
||||||
/**
|
/**
|
||||||
|
|
||||||
@@ -130,7 +132,7 @@ open class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func testBodyWithFileSchema(body: FileSchemaTestClass, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func testBodyWithFileSchema(body: FileSchemaTestClass, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testBodyWithFileSchemaWithRequestBuilder(body: body).execute { (_, error) -> Void in
|
testBodyWithFileSchemaWithRequestBuilder(body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -164,7 +166,7 @@ open class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func testBodyWithQueryParams(query: String, body: User, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func testBodyWithQueryParams(query: String, body: User, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testBodyWithQueryParamsWithRequestBuilder(query: query, body: body).execute { (_, error) -> Void in
|
testBodyWithQueryParamsWithRequestBuilder(query: query, body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -245,7 +247,7 @@ open class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func testEndpointParameters(number: Double, double: Double, patternWithoutDelimiter: String, byte: Data, integer: Int? = nil, int32: Int? = nil, int64: Int64? = nil, float: Float? = nil, string: String? = nil, binary: URL? = nil, date: Date? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func testEndpointParameters(number: Double, double: Double, patternWithoutDelimiter: String, byte: Data, integer: Int? = nil, int32: Int? = nil, int64: Int64? = nil, float: Float? = nil, string: String? = nil, binary: URL? = nil, date: Date? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testEndpointParametersWithRequestBuilder(number: number, double: double, patternWithoutDelimiter: patternWithoutDelimiter, byte: byte, integer: integer, int32: int32, int64: int64, float: float, string: string, binary: binary, date: date, dateTime: dateTime, password: password, callback: callback).execute { (_, error) -> Void in
|
testEndpointParametersWithRequestBuilder(number: number, double: double, patternWithoutDelimiter: patternWithoutDelimiter, byte: byte, integer: integer, int32: int32, int64: int64, float: float, string: string, binary: binary, date: date, dateTime: dateTime, password: password, callback: callback).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -388,7 +390,7 @@ open class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func testEnumParameters(enumHeaderStringArray: [String]? = nil, enumHeaderString: EnumHeaderString_testEnumParameters? = nil, enumQueryStringArray: [String]? = nil, enumQueryString: EnumQueryString_testEnumParameters? = nil, enumQueryInteger: EnumQueryInteger_testEnumParameters? = nil, enumQueryDouble: EnumQueryDouble_testEnumParameters? = nil, enumFormStringArray: [String]? = nil, enumFormString: EnumFormString_testEnumParameters? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func testEnumParameters(enumHeaderStringArray: [String]? = nil, enumHeaderString: EnumHeaderString_testEnumParameters? = nil, enumQueryStringArray: [String]? = nil, enumQueryString: EnumQueryString_testEnumParameters? = nil, enumQueryInteger: EnumQueryInteger_testEnumParameters? = nil, enumQueryDouble: EnumQueryDouble_testEnumParameters? = nil, enumFormStringArray: [String]? = nil, enumFormString: EnumFormString_testEnumParameters? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testEnumParametersWithRequestBuilder(enumHeaderStringArray: enumHeaderStringArray, enumHeaderString: enumHeaderString, enumQueryStringArray: enumQueryStringArray, enumQueryString: enumQueryString, enumQueryInteger: enumQueryInteger, enumQueryDouble: enumQueryDouble, enumFormStringArray: enumFormStringArray, enumFormString: enumFormString).execute { (_, error) -> Void in
|
testEnumParametersWithRequestBuilder(enumHeaderStringArray: enumHeaderStringArray, enumHeaderString: enumHeaderString, enumQueryStringArray: enumQueryStringArray, enumQueryString: enumQueryString, enumQueryInteger: enumQueryInteger, enumQueryDouble: enumQueryDouble, enumFormStringArray: enumFormStringArray, enumFormString: enumFormString).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -452,7 +454,7 @@ open class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func testGroupParameters(requiredStringGroup: Int, requiredBooleanGroup: Bool, requiredInt64Group: Int64, stringGroup: Int? = nil, booleanGroup: Bool? = nil, int64Group: Int64? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func testGroupParameters(requiredStringGroup: Int, requiredBooleanGroup: Bool, requiredInt64Group: Int64, stringGroup: Int? = nil, booleanGroup: Bool? = nil, int64Group: Int64? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testGroupParametersWithRequestBuilder(requiredStringGroup: requiredStringGroup, requiredBooleanGroup: requiredBooleanGroup, requiredInt64Group: requiredInt64Group, stringGroup: stringGroup, booleanGroup: booleanGroup, int64Group: int64Group).execute { (_, error) -> Void in
|
testGroupParametersWithRequestBuilder(requiredStringGroup: requiredStringGroup, requiredBooleanGroup: requiredBooleanGroup, requiredInt64Group: requiredInt64Group, stringGroup: stringGroup, booleanGroup: booleanGroup, int64Group: int64Group).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -503,7 +505,7 @@ open class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func testInlineAdditionalProperties(param: [String:String], completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func testInlineAdditionalProperties(param: [String:String], completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testInlineAdditionalPropertiesWithRequestBuilder(param: param).execute { (_, error) -> Void in
|
testInlineAdditionalPropertiesWithRequestBuilder(param: param).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -538,7 +540,7 @@ open class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func testJsonFormData(param: String, param2: String, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func testJsonFormData(param: String, param2: String, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testJsonFormDataWithRequestBuilder(param: param, param2: param2).execute { (_, error) -> Void in
|
testJsonFormDataWithRequestBuilder(param: param, param2: param2).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
open class FakeClassnameTags123API {
|
open class FakeClassnameTags123API {
|
||||||
/**
|
/**
|
||||||
To test class name in snake case
|
To test class name in snake case
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
open class PetAPI {
|
open class PetAPI {
|
||||||
/**
|
/**
|
||||||
Add a new pet to the store
|
Add a new pet to the store
|
||||||
@@ -15,7 +17,7 @@ open class PetAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func addPet(body: Pet, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func addPet(body: Pet, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
addPetWithRequestBuilder(body: body).execute { (_, error) -> Void in
|
addPetWithRequestBuilder(body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -53,7 +55,7 @@ open class PetAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func deletePet(petId: Int64, apiKey: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func deletePet(petId: Int64, apiKey: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
deletePetWithRequestBuilder(petId: petId, apiKey: apiKey).execute { (_, error) -> Void in
|
deletePetWithRequestBuilder(petId: petId, apiKey: apiKey).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -218,7 +220,7 @@ open class PetAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func updatePet(body: Pet, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func updatePet(body: Pet, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
updatePetWithRequestBuilder(body: body).execute { (_, error) -> Void in
|
updatePetWithRequestBuilder(body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -257,7 +259,7 @@ open class PetAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func updatePetWithForm(petId: Int64, name: String? = nil, status: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func updatePetWithForm(petId: Int64, name: String? = nil, status: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
updatePetWithFormWithRequestBuilder(petId: petId, name: name, status: status).execute { (_, error) -> Void in
|
updatePetWithFormWithRequestBuilder(petId: petId, name: name, status: status).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
open class StoreAPI {
|
open class StoreAPI {
|
||||||
/**
|
/**
|
||||||
Delete purchase order by ID
|
Delete purchase order by ID
|
||||||
@@ -15,7 +17,7 @@ open class StoreAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func deleteOrder(orderId: String, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func deleteOrder(orderId: String, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
deleteOrderWithRequestBuilder(orderId: orderId).execute { (_, error) -> Void in
|
deleteOrderWithRequestBuilder(orderId: orderId).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
open class UserAPI {
|
open class UserAPI {
|
||||||
/**
|
/**
|
||||||
Create user
|
Create user
|
||||||
@@ -15,7 +17,7 @@ open class UserAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func createUser(body: User, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func createUser(body: User, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
createUserWithRequestBuilder(body: body).execute { (_, error) -> Void in
|
createUserWithRequestBuilder(body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -50,7 +52,7 @@ open class UserAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func createUsersWithArrayInput(body: [User], completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func createUsersWithArrayInput(body: [User], completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
createUsersWithArrayInputWithRequestBuilder(body: body).execute { (_, error) -> Void in
|
createUsersWithArrayInputWithRequestBuilder(body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -84,7 +86,7 @@ open class UserAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func createUsersWithListInput(body: [User], completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func createUsersWithListInput(body: [User], completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
createUsersWithListInputWithRequestBuilder(body: body).execute { (_, error) -> Void in
|
createUsersWithListInputWithRequestBuilder(body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -118,7 +120,7 @@ open class UserAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func deleteUser(username: String, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func deleteUser(username: String, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
deleteUserWithRequestBuilder(username: username).execute { (_, error) -> Void in
|
deleteUserWithRequestBuilder(username: username).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -225,7 +227,7 @@ open class UserAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func logoutUser(completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func logoutUser(completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
logoutUserWithRequestBuilder().execute { (_, error) -> Void in
|
logoutUserWithRequestBuilder().execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -259,7 +261,7 @@ open class UserAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
open class func updateUser(username: String, body: User, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
open class func updateUser(username: String, body: User, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
updateUserWithRequestBuilder(username: username, body: body).execute { (_, error) -> Void in
|
updateUserWithRequestBuilder(username: username, body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -111,7 +111,8 @@ open class AlamofireRequestBuilder<T>: RequestBuilder<T> {
|
|||||||
case let fileURL as URL:
|
case let fileURL as URL:
|
||||||
if let mimeType = self.contentTypeForFormPart(fileURL: fileURL) {
|
if let mimeType = self.contentTypeForFormPart(fileURL: fileURL) {
|
||||||
mpForm.append(fileURL, withName: k, fileName: fileURL.lastPathComponent, mimeType: mimeType)
|
mpForm.append(fileURL, withName: k, fileName: fileURL.lastPathComponent, mimeType: mimeType)
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
mpForm.append(fileURL, withName: k)
|
mpForm.append(fileURL, withName: k)
|
||||||
}
|
}
|
||||||
case let string as String:
|
case let string as String:
|
||||||
@@ -283,7 +284,7 @@ open class AlamofireRequestBuilder<T>: RequestBuilder<T> {
|
|||||||
|
|
||||||
let items = contentDisposition.components(separatedBy: ";")
|
let items = contentDisposition.components(separatedBy: ";")
|
||||||
|
|
||||||
var filename: String?
|
var filename : String? = nil
|
||||||
|
|
||||||
for contentItem in items {
|
for contentItem in items {
|
||||||
|
|
||||||
@@ -328,7 +329,7 @@ open class AlamofireRequestBuilder<T>: RequestBuilder<T> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum DownloadException: Error {
|
fileprivate enum DownloadException : Error {
|
||||||
case responseDataMissing
|
case responseDataMissing
|
||||||
case responseFailed
|
case responseFailed
|
||||||
case requestMissing
|
case requestMissing
|
||||||
@@ -435,7 +436,7 @@ open class AlamofireDecodableRequestBuilder<T: Decodable>: AlamofireRequestBuild
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseObj: Response<T>?
|
var responseObj: Response<T>? = nil
|
||||||
|
|
||||||
let decodeResult: (decodableObj: T?, error: Error?) = CodableHelper.decode(T.self, from: data)
|
let decodeResult: (decodableObj: T?, error: Error?) = CodableHelper.decode(T.self, from: data)
|
||||||
if decodeResult.error == nil {
|
if decodeResult.error == nil {
|
||||||
|
|||||||
@@ -11,27 +11,48 @@ public typealias EncodeResult = (data: Data?, error: Error?)
|
|||||||
|
|
||||||
open class CodableHelper {
|
open class CodableHelper {
|
||||||
|
|
||||||
public static var dateformatter: DateFormatter?
|
private static var customDateFormatter: DateFormatter?
|
||||||
|
private static var defaultDateFormatter: DateFormatter = {
|
||||||
open class func decode<T>(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T: Decodable {
|
let dateFormatter = DateFormatter()
|
||||||
var returnedDecodable: T?
|
dateFormatter.calendar = Calendar(identifier: .iso8601)
|
||||||
var returnedError: Error?
|
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
|
||||||
|
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
|
||||||
|
dateFormatter.dateFormat = Configuration.dateFormat
|
||||||
|
return dateFormatter
|
||||||
|
}()
|
||||||
|
private static var customJSONDecoder: JSONDecoder?
|
||||||
|
private static var defaultJSONDecoder: JSONDecoder = {
|
||||||
let decoder = JSONDecoder()
|
let decoder = JSONDecoder()
|
||||||
if let df = self.dateformatter {
|
decoder.dateDecodingStrategy = .formatted(CodableHelper.dateFormatter)
|
||||||
decoder.dateDecodingStrategy = .formatted(df)
|
return decoder
|
||||||
} else {
|
}()
|
||||||
decoder.dataDecodingStrategy = .base64
|
private static var customJSONEncoder: JSONEncoder?
|
||||||
let formatter = DateFormatter()
|
private static var defaultJSONEncoder: JSONEncoder = {
|
||||||
formatter.calendar = Calendar(identifier: .iso8601)
|
let encoder = JSONEncoder()
|
||||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
encoder.dateEncodingStrategy = .formatted(CodableHelper.dateFormatter)
|
||||||
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
encoder.outputFormatting = .prettyPrinted
|
||||||
formatter.dateFormat = Configuration.dateFormat
|
return encoder
|
||||||
decoder.dateDecodingStrategy = .formatted(formatter)
|
}()
|
||||||
|
|
||||||
|
public static var dateFormatter: DateFormatter {
|
||||||
|
get { return self.customDateFormatter ?? self.defaultDateFormatter }
|
||||||
|
set { self.customDateFormatter = newValue }
|
||||||
|
}
|
||||||
|
public static var jsonDecoder: JSONDecoder {
|
||||||
|
get { return self.customJSONDecoder ?? self.defaultJSONDecoder }
|
||||||
|
set { self.customJSONDecoder = newValue }
|
||||||
|
}
|
||||||
|
public static var jsonEncoder: JSONEncoder {
|
||||||
|
get { return self.customJSONEncoder ?? self.defaultJSONEncoder }
|
||||||
|
set { self.customJSONEncoder = newValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class func decode<T>(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T : Decodable {
|
||||||
|
var returnedDecodable: T? = nil
|
||||||
|
var returnedError: Error? = nil
|
||||||
|
|
||||||
do {
|
do {
|
||||||
returnedDecodable = try decoder.decode(type, from: data)
|
returnedDecodable = try self.jsonDecoder.decode(type, from: data)
|
||||||
} catch {
|
} catch {
|
||||||
returnedError = error
|
returnedError = error
|
||||||
}
|
}
|
||||||
@@ -39,33 +60,16 @@ open class CodableHelper {
|
|||||||
return (returnedDecodable, returnedError)
|
return (returnedDecodable, returnedError)
|
||||||
}
|
}
|
||||||
|
|
||||||
open class func encode<T>(_ value: T, prettyPrint: Bool = false) -> EncodeResult where T: Encodable {
|
open class func encode<T>(_ value: T) -> EncodeResult where T : Encodable {
|
||||||
var returnedData: Data?
|
var returnedData: Data?
|
||||||
var returnedError: Error?
|
var returnedError: Error? = nil
|
||||||
|
|
||||||
let encoder = JSONEncoder()
|
|
||||||
if prettyPrint {
|
|
||||||
encoder.outputFormatting = .prettyPrinted
|
|
||||||
}
|
|
||||||
if let df = self.dateformatter {
|
|
||||||
encoder.dateEncodingStrategy = .formatted(df)
|
|
||||||
} else {
|
|
||||||
encoder.dataEncodingStrategy = .base64
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.calendar = Calendar(identifier: .iso8601)
|
|
||||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
||||||
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
|
||||||
formatter.dateFormat = Configuration.dateFormat
|
|
||||||
encoder.dateEncodingStrategy = .formatted(formatter)
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
returnedData = try encoder.encode(value)
|
returnedData = try self.jsonEncoder.encode(value)
|
||||||
} catch {
|
} catch {
|
||||||
returnedError = error
|
returnedError = error
|
||||||
}
|
}
|
||||||
|
|
||||||
return (returnedData, returnedError)
|
return (returnedData, returnedError)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,22 +68,9 @@ extension Data: JSONEncodable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let dateFormatter: DateFormatter = {
|
|
||||||
if let formatter = CodableHelper.dateformatter {
|
|
||||||
return formatter
|
|
||||||
} else {
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.calendar = Calendar(identifier: .iso8601)
|
|
||||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
|
||||||
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
|
||||||
formatter.dateFormat = Configuration.dateFormat
|
|
||||||
return formatter
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
extension Date: JSONEncodable {
|
extension Date: JSONEncodable {
|
||||||
func encodeToJSON() -> Any {
|
func encodeToJSON() -> Any {
|
||||||
return dateFormatter.string(from: self) as Any
|
return CodableHelper.dateFormatter.string(from: self) as Any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +148,7 @@ extension KeyedDecodingContainerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func decodeArrayIfPresent<T>(_ type: T.Type, forKey key: Self.Key) throws -> [T]? where T : Decodable {
|
public func decodeArrayIfPresent<T>(_ type: T.Type, forKey key: Self.Key) throws -> [T]? where T : Decodable {
|
||||||
var tmpArray: [T]?
|
var tmpArray: [T]? = nil
|
||||||
|
|
||||||
if contains(key) {
|
if contains(key) {
|
||||||
tmpArray = try decodeArray(T.self, forKey: key)
|
tmpArray = try decodeArray(T.self, forKey: key)
|
||||||
@@ -184,3 +171,5 @@ extension KeyedDecodingContainerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public struct JSONDataEncoding: ParameterEncoding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static func encodingParameters(jsonData: Data?) -> Parameters? {
|
public static func encodingParameters(jsonData: Data?) -> Parameters? {
|
||||||
var returnedParams: Parameters?
|
var returnedParams: Parameters? = nil
|
||||||
if let jsonData = jsonData, !jsonData.isEmpty {
|
if let jsonData = jsonData, !jsonData.isEmpty {
|
||||||
var params = Parameters()
|
var params = Parameters()
|
||||||
params[jsonDataKey] = jsonData
|
params[jsonDataKey] = jsonData
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import Alamofire
|
|||||||
open class JSONEncodingHelper {
|
open class JSONEncodingHelper {
|
||||||
|
|
||||||
open class func encodingParameters<T:Encodable>(forEncodableObject encodableObj: T?) -> Parameters? {
|
open class func encodingParameters<T:Encodable>(forEncodableObject encodableObj: T?) -> Parameters? {
|
||||||
var params: Parameters?
|
var params: Parameters? = nil
|
||||||
|
|
||||||
// Encode the Encodable object
|
// Encode the Encodable object
|
||||||
if let encodableObj = encodableObj {
|
if let encodableObj = encodableObj {
|
||||||
let encodeResult = CodableHelper.encode(encodableObj, prettyPrint: true)
|
let encodeResult = CodableHelper.encode(encodableObj)
|
||||||
if encodeResult.error == nil {
|
if encodeResult.error == nil {
|
||||||
params = JSONDataEncoding.encodingParameters(jsonData: encodeResult.data)
|
params = JSONDataEncoding.encodingParameters(jsonData: encodeResult.data)
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ open class JSONEncodingHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open class func encodingParameters(forEncodableObject encodableObj: Any?) -> Parameters? {
|
open class func encodingParameters(forEncodableObject encodableObj: Any?) -> Parameters? {
|
||||||
var params: Parameters?
|
var params: Parameters? = nil
|
||||||
|
|
||||||
if let encodableObj = encodableObj {
|
if let encodableObj = encodableObj {
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct AdditionalPropertiesClass: Codable {
|
public struct AdditionalPropertiesClass: Codable {
|
||||||
|
|
||||||
public var mapString: [String:String]?
|
public var mapString: [String:String]?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct Animal: Codable {
|
public struct Animal: Codable {
|
||||||
|
|
||||||
public var className: String
|
public var className: String
|
||||||
|
|||||||
@@ -7,4 +7,5 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public typealias AnimalFarm = [Animal]
|
public typealias AnimalFarm = [Animal]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct ApiResponse: Codable {
|
public struct ApiResponse: Codable {
|
||||||
|
|
||||||
public var code: Int?
|
public var code: Int?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct ArrayOfArrayOfNumberOnly: Codable {
|
public struct ArrayOfArrayOfNumberOnly: Codable {
|
||||||
|
|
||||||
public var arrayArrayNumber: [[Double]]?
|
public var arrayArrayNumber: [[Double]]?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct ArrayOfNumberOnly: Codable {
|
public struct ArrayOfNumberOnly: Codable {
|
||||||
|
|
||||||
public var arrayNumber: [Double]?
|
public var arrayNumber: [Double]?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct ArrayTest: Codable {
|
public struct ArrayTest: Codable {
|
||||||
|
|
||||||
public var arrayOfString: [String]?
|
public var arrayOfString: [String]?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct Capitalization: Codable {
|
public struct Capitalization: Codable {
|
||||||
|
|
||||||
public var smallCamel: String?
|
public var smallCamel: String?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct Cat: Codable {
|
public struct Cat: Codable {
|
||||||
|
|
||||||
public var className: String
|
public var className: String
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct CatAllOf: Codable {
|
public struct CatAllOf: Codable {
|
||||||
|
|
||||||
public var declawed: Bool?
|
public var declawed: Bool?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct Category: Codable {
|
public struct Category: Codable {
|
||||||
|
|
||||||
public var id: Int64?
|
public var id: Int64?
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/** Model for testing model with \"_class\" property */
|
/** Model for testing model with \"_class\" property */
|
||||||
|
|
||||||
public struct ClassModel: Codable {
|
public struct ClassModel: Codable {
|
||||||
|
|
||||||
public var _class: String?
|
public var _class: String?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct Client: Codable {
|
public struct Client: Codable {
|
||||||
|
|
||||||
public var client: String?
|
public var client: String?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct Dog: Codable {
|
public struct Dog: Codable {
|
||||||
|
|
||||||
public var className: String
|
public var className: String
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct DogAllOf: Codable {
|
public struct DogAllOf: Codable {
|
||||||
|
|
||||||
public var breed: String?
|
public var breed: String?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct EnumArrays: Codable {
|
public struct EnumArrays: Codable {
|
||||||
|
|
||||||
public enum JustSymbol: String, Codable {
|
public enum JustSymbol: String, Codable {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public enum EnumClass: String, Codable {
|
public enum EnumClass: String, Codable {
|
||||||
case abc = "_abc"
|
case abc = "_abc"
|
||||||
case efg = "-efg"
|
case efg = "-efg"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct EnumTest: Codable {
|
public struct EnumTest: Codable {
|
||||||
|
|
||||||
public enum EnumString: String, Codable {
|
public enum EnumString: String, Codable {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/** Must be named `File` for test. */
|
/** Must be named `File` for test. */
|
||||||
|
|
||||||
public struct File: Codable {
|
public struct File: Codable {
|
||||||
|
|
||||||
/** Test capitalization */
|
/** Test capitalization */
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct FileSchemaTestClass: Codable {
|
public struct FileSchemaTestClass: Codable {
|
||||||
|
|
||||||
public var file: File?
|
public var file: File?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct FormatTest: Codable {
|
public struct FormatTest: Codable {
|
||||||
|
|
||||||
public var integer: Int?
|
public var integer: Int?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct HasOnlyReadOnly: Codable {
|
public struct HasOnlyReadOnly: Codable {
|
||||||
|
|
||||||
public var bar: String?
|
public var bar: String?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct List: Codable {
|
public struct List: Codable {
|
||||||
|
|
||||||
public var _123list: String?
|
public var _123list: String?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct MapTest: Codable {
|
public struct MapTest: Codable {
|
||||||
|
|
||||||
public enum MapOfEnumString: String, Codable {
|
public enum MapOfEnumString: String, Codable {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct MixedPropertiesAndAdditionalPropertiesClass: Codable {
|
public struct MixedPropertiesAndAdditionalPropertiesClass: Codable {
|
||||||
|
|
||||||
public var uuid: UUID?
|
public var uuid: UUID?
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/** Model for testing model name starting with number */
|
/** Model for testing model name starting with number */
|
||||||
|
|
||||||
public struct Model200Response: Codable {
|
public struct Model200Response: Codable {
|
||||||
|
|
||||||
public var name: Int?
|
public var name: Int?
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/** Model for testing model name same as property name */
|
/** Model for testing model name same as property name */
|
||||||
|
|
||||||
public struct Name: Codable {
|
public struct Name: Codable {
|
||||||
|
|
||||||
public var name: Int
|
public var name: Int
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct NumberOnly: Codable {
|
public struct NumberOnly: Codable {
|
||||||
|
|
||||||
public var justNumber: Double?
|
public var justNumber: Double?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct Order: Codable {
|
public struct Order: Codable {
|
||||||
|
|
||||||
public enum Status: String, Codable {
|
public enum Status: String, Codable {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct OuterComposite: Codable {
|
public struct OuterComposite: Codable {
|
||||||
|
|
||||||
public var myNumber: Double?
|
public var myNumber: Double?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public enum OuterEnum: String, Codable {
|
public enum OuterEnum: String, Codable {
|
||||||
case placed = "placed"
|
case placed = "placed"
|
||||||
case approved = "approved"
|
case approved = "approved"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct Pet: Codable {
|
public struct Pet: Codable {
|
||||||
|
|
||||||
public enum Status: String, Codable {
|
public enum Status: String, Codable {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct ReadOnlyFirst: Codable {
|
public struct ReadOnlyFirst: Codable {
|
||||||
|
|
||||||
public var bar: String?
|
public var bar: String?
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/** Model for testing reserved words */
|
/** Model for testing reserved words */
|
||||||
|
|
||||||
public struct Return: Codable {
|
public struct Return: Codable {
|
||||||
|
|
||||||
public var _return: Int?
|
public var _return: Int?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct SpecialModelName: Codable {
|
public struct SpecialModelName: Codable {
|
||||||
|
|
||||||
public var specialPropertyName: Int64?
|
public var specialPropertyName: Int64?
|
||||||
|
|||||||
@@ -7,8 +7,10 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct StringBooleanMap: Codable {
|
public struct StringBooleanMap: Codable {
|
||||||
|
|
||||||
|
|
||||||
public var additionalProperties: [String:Bool] = [:]
|
public var additionalProperties: [String:Bool] = [:]
|
||||||
|
|
||||||
public subscript(key: String) -> Bool? {
|
public subscript(key: String) -> Bool? {
|
||||||
@@ -42,4 +44,5 @@ public struct StringBooleanMap: Codable {
|
|||||||
additionalProperties = try container.decodeMap(Bool.self, excludedKeys: nonAdditionalPropertyKeys)
|
additionalProperties = try container.decodeMap(Bool.self, excludedKeys: nonAdditionalPropertyKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct Tag: Codable {
|
public struct Tag: Codable {
|
||||||
|
|
||||||
public var id: Int64?
|
public var id: Int64?
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct TypeHolderDefault: Codable {
|
public struct TypeHolderDefault: Codable {
|
||||||
|
|
||||||
public var stringItem: String = "what"
|
public var stringItem: String = "what"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct TypeHolderExample: Codable {
|
public struct TypeHolderExample: Codable {
|
||||||
|
|
||||||
public var stringItem: String
|
public var stringItem: String
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
public struct User: Codable {
|
public struct User: Codable {
|
||||||
|
|
||||||
public var id: Int64?
|
public var id: Int64?
|
||||||
|
|||||||
456
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/Alamofire.swift
generated
Normal file
456
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/Alamofire.swift
generated
Normal file
@@ -0,0 +1,456 @@
|
|||||||
|
//
|
||||||
|
// Alamofire.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Types adopting the `URLConvertible` protocol can be used to construct URLs, which are then used to construct
|
||||||
|
/// URL requests.
|
||||||
|
public protocol URLConvertible {
|
||||||
|
/// Returns a URL that conforms to RFC 2396 or throws an `Error`.
|
||||||
|
///
|
||||||
|
/// - throws: An `Error` if the type cannot be converted to a `URL`.
|
||||||
|
///
|
||||||
|
/// - returns: A URL or throws an `Error`.
|
||||||
|
func asURL() throws -> URL
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String: URLConvertible {
|
||||||
|
/// Returns a URL if `self` represents a valid URL string that conforms to RFC 2396 or throws an `AFError`.
|
||||||
|
///
|
||||||
|
/// - throws: An `AFError.invalidURL` if `self` is not a valid URL string.
|
||||||
|
///
|
||||||
|
/// - returns: A URL or throws an `AFError`.
|
||||||
|
public func asURL() throws -> URL {
|
||||||
|
guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) }
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension URL: URLConvertible {
|
||||||
|
/// Returns self.
|
||||||
|
public func asURL() throws -> URL { return self }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension URLComponents: URLConvertible {
|
||||||
|
/// Returns a URL if `url` is not nil, otherwise throws an `Error`.
|
||||||
|
///
|
||||||
|
/// - throws: An `AFError.invalidURL` if `url` is `nil`.
|
||||||
|
///
|
||||||
|
/// - returns: A URL or throws an `AFError`.
|
||||||
|
public func asURL() throws -> URL {
|
||||||
|
guard let url = url else { throw AFError.invalidURL(url: self) }
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests.
|
||||||
|
public protocol URLRequestConvertible {
|
||||||
|
/// Returns a URL request or throws if an `Error` was encountered.
|
||||||
|
///
|
||||||
|
/// - throws: An `Error` if the underlying `URLRequest` is `nil`.
|
||||||
|
///
|
||||||
|
/// - returns: A URL request.
|
||||||
|
func asURLRequest() throws -> URLRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
extension URLRequestConvertible {
|
||||||
|
/// The URL request.
|
||||||
|
public var urlRequest: URLRequest? { return try? asURLRequest() }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension URLRequest: URLRequestConvertible {
|
||||||
|
/// Returns a URL request or throws if an `Error` was encountered.
|
||||||
|
public func asURLRequest() throws -> URLRequest { return self }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
extension URLRequest {
|
||||||
|
/// Creates an instance with the specified `method`, `urlString` and `headers`.
|
||||||
|
///
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The new `URLRequest` instance.
|
||||||
|
public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws {
|
||||||
|
let url = try url.asURL()
|
||||||
|
|
||||||
|
self.init(url: url)
|
||||||
|
|
||||||
|
httpMethod = method.rawValue
|
||||||
|
|
||||||
|
if let headers = headers {
|
||||||
|
for (headerField, headerValue) in headers {
|
||||||
|
setValue(headerValue, forHTTPHeaderField: headerField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func adapt(using adapter: RequestAdapter?) throws -> URLRequest {
|
||||||
|
guard let adapter = adapter else { return self }
|
||||||
|
return try adapter.adapt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Data Request
|
||||||
|
|
||||||
|
/// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of the specified `url`,
|
||||||
|
/// `method`, `parameters`, `encoding` and `headers`.
|
||||||
|
///
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.get` by default.
|
||||||
|
/// - parameter parameters: The parameters. `nil` by default.
|
||||||
|
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `DataRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func request(
|
||||||
|
_ url: URLConvertible,
|
||||||
|
method: HTTPMethod = .get,
|
||||||
|
parameters: Parameters? = nil,
|
||||||
|
encoding: ParameterEncoding = URLEncoding.default,
|
||||||
|
headers: HTTPHeaders? = nil)
|
||||||
|
-> DataRequest {
|
||||||
|
return SessionManager.default.request(
|
||||||
|
url,
|
||||||
|
method: method,
|
||||||
|
parameters: parameters,
|
||||||
|
encoding: encoding,
|
||||||
|
headers: headers
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
|
||||||
|
/// specified `urlRequest`.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The URL request
|
||||||
|
///
|
||||||
|
/// - returns: The created `DataRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
|
||||||
|
return SessionManager.default.request(urlRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Download Request
|
||||||
|
|
||||||
|
// MARK: URL Request
|
||||||
|
|
||||||
|
/// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of the specified `url`,
|
||||||
|
/// `method`, `parameters`, `encoding`, `headers` and save them to the `destination`.
|
||||||
|
///
|
||||||
|
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||||
|
/// underlying URL session.
|
||||||
|
///
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.get` by default.
|
||||||
|
/// - parameter parameters: The parameters. `nil` by default.
|
||||||
|
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `DownloadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func download(
|
||||||
|
_ url: URLConvertible,
|
||||||
|
method: HTTPMethod = .get,
|
||||||
|
parameters: Parameters? = nil,
|
||||||
|
encoding: ParameterEncoding = URLEncoding.default,
|
||||||
|
headers: HTTPHeaders? = nil,
|
||||||
|
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||||
|
-> DownloadRequest {
|
||||||
|
return SessionManager.default.download(
|
||||||
|
url,
|
||||||
|
method: method,
|
||||||
|
parameters: parameters,
|
||||||
|
encoding: encoding,
|
||||||
|
headers: headers,
|
||||||
|
to: destination
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
|
||||||
|
/// specified `urlRequest` and save them to the `destination`.
|
||||||
|
///
|
||||||
|
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||||
|
/// underlying URL session.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The URL request.
|
||||||
|
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `DownloadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func download(
|
||||||
|
_ urlRequest: URLRequestConvertible,
|
||||||
|
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||||
|
-> DownloadRequest {
|
||||||
|
return SessionManager.default.download(urlRequest, to: destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Resume Data
|
||||||
|
|
||||||
|
/// Creates a `DownloadRequest` using the default `SessionManager` from the `resumeData` produced from a
|
||||||
|
/// previous request cancellation to retrieve the contents of the original request and save them to the `destination`.
|
||||||
|
///
|
||||||
|
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||||
|
/// underlying URL session.
|
||||||
|
///
|
||||||
|
/// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken
|
||||||
|
/// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the
|
||||||
|
/// data is written incorrectly and will always fail to resume the download. For more information about the bug and
|
||||||
|
/// possible workarounds, please refer to the following Stack Overflow post:
|
||||||
|
///
|
||||||
|
/// - http://stackoverflow.com/a/39347461/1342462
|
||||||
|
///
|
||||||
|
/// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask`
|
||||||
|
/// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for additional
|
||||||
|
/// information.
|
||||||
|
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `DownloadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func download(
|
||||||
|
resumingWith resumeData: Data,
|
||||||
|
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||||
|
-> DownloadRequest {
|
||||||
|
return SessionManager.default.download(resumingWith: resumeData, to: destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Upload Request
|
||||||
|
|
||||||
|
// MARK: File
|
||||||
|
|
||||||
|
/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers`
|
||||||
|
/// for uploading the `file`.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file to upload.
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.post` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func upload(
|
||||||
|
_ fileURL: URL,
|
||||||
|
to url: URLConvertible,
|
||||||
|
method: HTTPMethod = .post,
|
||||||
|
headers: HTTPHeaders? = nil)
|
||||||
|
-> UploadRequest {
|
||||||
|
return SessionManager.default.upload(fileURL, to: url, method: method, headers: headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
|
||||||
|
/// uploading the `file`.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file to upload.
|
||||||
|
/// - parameter urlRequest: The URL request.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||||
|
return SessionManager.default.upload(fileURL, with: urlRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Data
|
||||||
|
|
||||||
|
/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers`
|
||||||
|
/// for uploading the `data`.
|
||||||
|
///
|
||||||
|
/// - parameter data: The data to upload.
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.post` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func upload(
|
||||||
|
_ data: Data,
|
||||||
|
to url: URLConvertible,
|
||||||
|
method: HTTPMethod = .post,
|
||||||
|
headers: HTTPHeaders? = nil)
|
||||||
|
-> UploadRequest {
|
||||||
|
return SessionManager.default.upload(data, to: url, method: method, headers: headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
|
||||||
|
/// uploading the `data`.
|
||||||
|
///
|
||||||
|
/// - parameter data: The data to upload.
|
||||||
|
/// - parameter urlRequest: The URL request.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||||
|
return SessionManager.default.upload(data, with: urlRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: InputStream
|
||||||
|
|
||||||
|
/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers`
|
||||||
|
/// for uploading the `stream`.
|
||||||
|
///
|
||||||
|
/// - parameter stream: The stream to upload.
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.post` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func upload(
|
||||||
|
_ stream: InputStream,
|
||||||
|
to url: URLConvertible,
|
||||||
|
method: HTTPMethod = .post,
|
||||||
|
headers: HTTPHeaders? = nil)
|
||||||
|
-> UploadRequest {
|
||||||
|
return SessionManager.default.upload(stream, to: url, method: method, headers: headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
|
||||||
|
/// uploading the `stream`.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The URL request.
|
||||||
|
/// - parameter stream: The stream to upload.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
public func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||||
|
return SessionManager.default.upload(stream, with: urlRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: MultipartFormData
|
||||||
|
|
||||||
|
/// Encodes `multipartFormData` using `encodingMemoryThreshold` with the default `SessionManager` and calls
|
||||||
|
/// `encodingCompletion` with new `UploadRequest` using the `url`, `method` and `headers`.
|
||||||
|
///
|
||||||
|
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
|
||||||
|
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
|
||||||
|
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
|
||||||
|
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
|
||||||
|
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
|
||||||
|
/// used for larger payloads such as video content.
|
||||||
|
///
|
||||||
|
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
|
||||||
|
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
|
||||||
|
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
|
||||||
|
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
|
||||||
|
/// technique was used.
|
||||||
|
///
|
||||||
|
/// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
|
||||||
|
/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
|
||||||
|
/// `multipartFormDataEncodingMemoryThreshold` by default.
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.post` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
/// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
|
||||||
|
public func upload(
|
||||||
|
multipartFormData: @escaping (MultipartFormData) -> Void,
|
||||||
|
usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
|
||||||
|
to url: URLConvertible,
|
||||||
|
method: HTTPMethod = .post,
|
||||||
|
headers: HTTPHeaders? = nil,
|
||||||
|
encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?) {
|
||||||
|
return SessionManager.default.upload(
|
||||||
|
multipartFormData: multipartFormData,
|
||||||
|
usingThreshold: encodingMemoryThreshold,
|
||||||
|
to: url,
|
||||||
|
method: method,
|
||||||
|
headers: headers,
|
||||||
|
encodingCompletion: encodingCompletion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes `multipartFormData` using `encodingMemoryThreshold` and the default `SessionManager` and
|
||||||
|
/// calls `encodingCompletion` with new `UploadRequest` using the `urlRequest`.
|
||||||
|
///
|
||||||
|
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
|
||||||
|
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
|
||||||
|
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
|
||||||
|
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
|
||||||
|
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
|
||||||
|
/// used for larger payloads such as video content.
|
||||||
|
///
|
||||||
|
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
|
||||||
|
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
|
||||||
|
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
|
||||||
|
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
|
||||||
|
/// technique was used.
|
||||||
|
///
|
||||||
|
/// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
|
||||||
|
/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
|
||||||
|
/// `multipartFormDataEncodingMemoryThreshold` by default.
|
||||||
|
/// - parameter urlRequest: The URL request.
|
||||||
|
/// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
|
||||||
|
public func upload(
|
||||||
|
multipartFormData: @escaping (MultipartFormData) -> Void,
|
||||||
|
usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
|
||||||
|
with urlRequest: URLRequestConvertible,
|
||||||
|
encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?) {
|
||||||
|
return SessionManager.default.upload(
|
||||||
|
multipartFormData: multipartFormData,
|
||||||
|
usingThreshold: encodingMemoryThreshold,
|
||||||
|
with: urlRequest,
|
||||||
|
encodingCompletion: encodingCompletion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !os(watchOS)
|
||||||
|
|
||||||
|
// MARK: - Stream Request
|
||||||
|
|
||||||
|
// MARK: Hostname and Port
|
||||||
|
|
||||||
|
/// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `hostname`
|
||||||
|
/// and `port`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter hostName: The hostname of the server to connect to.
|
||||||
|
/// - parameter port: The port of the server to connect to.
|
||||||
|
///
|
||||||
|
/// - returns: The created `StreamRequest`.
|
||||||
|
@discardableResult
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
public func stream(withHostName hostName: String, port: Int) -> StreamRequest {
|
||||||
|
return SessionManager.default.stream(withHostName: hostName, port: port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: NetService
|
||||||
|
|
||||||
|
/// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `netService`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter netService: The net service used to identify the endpoint.
|
||||||
|
///
|
||||||
|
/// - returns: The created `StreamRequest`.
|
||||||
|
@discardableResult
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
public func stream(with netService: NetService) -> StreamRequest {
|
||||||
|
return SessionManager.default.stream(with: netService)
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
578
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/MultipartFormData.swift
generated
Normal file
578
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/MultipartFormData.swift
generated
Normal file
@@ -0,0 +1,578 @@
|
|||||||
|
//
|
||||||
|
// MultipartFormData.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
#if os(iOS) || os(watchOS) || os(tvOS)
|
||||||
|
import MobileCoreServices
|
||||||
|
#elseif os(macOS)
|
||||||
|
import CoreServices
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode
|
||||||
|
/// multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead
|
||||||
|
/// to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the
|
||||||
|
/// data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for
|
||||||
|
/// larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset.
|
||||||
|
///
|
||||||
|
/// For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well
|
||||||
|
/// and the w3 form documentation.
|
||||||
|
///
|
||||||
|
/// - https://www.ietf.org/rfc/rfc2388.txt
|
||||||
|
/// - https://www.ietf.org/rfc/rfc2045.txt
|
||||||
|
/// - https://www.w3.org/TR/html401/interact/forms.html#h-17.13
|
||||||
|
open class MultipartFormData {
|
||||||
|
|
||||||
|
// MARK: - Helper Types
|
||||||
|
|
||||||
|
struct EncodingCharacters {
|
||||||
|
static let crlf = "\r\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BoundaryGenerator {
|
||||||
|
enum BoundaryType {
|
||||||
|
case initial, encapsulated, final
|
||||||
|
}
|
||||||
|
|
||||||
|
static func randomBoundary() -> String {
|
||||||
|
return String(format: "alamofire.boundary.%08x%08x", arc4random(), arc4random())
|
||||||
|
}
|
||||||
|
|
||||||
|
static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data {
|
||||||
|
let boundaryText: String
|
||||||
|
|
||||||
|
switch boundaryType {
|
||||||
|
case .initial:
|
||||||
|
boundaryText = "--\(boundary)\(EncodingCharacters.crlf)"
|
||||||
|
case .encapsulated:
|
||||||
|
boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)"
|
||||||
|
case .final:
|
||||||
|
boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return boundaryText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BodyPart {
|
||||||
|
let headers: HTTPHeaders
|
||||||
|
let bodyStream: InputStream
|
||||||
|
let bodyContentLength: UInt64
|
||||||
|
var hasInitialBoundary = false
|
||||||
|
var hasFinalBoundary = false
|
||||||
|
|
||||||
|
init(headers: HTTPHeaders, bodyStream: InputStream, bodyContentLength: UInt64) {
|
||||||
|
self.headers = headers
|
||||||
|
self.bodyStream = bodyStream
|
||||||
|
self.bodyContentLength = bodyContentLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
/// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`.
|
||||||
|
open lazy var contentType: String = "multipart/form-data; boundary=\(self.boundary)"
|
||||||
|
|
||||||
|
/// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries.
|
||||||
|
public var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } }
|
||||||
|
|
||||||
|
/// The boundary used to separate the body parts in the encoded form data.
|
||||||
|
public var boundary: String
|
||||||
|
|
||||||
|
private var bodyParts: [BodyPart]
|
||||||
|
private var bodyPartError: AFError?
|
||||||
|
private let streamBufferSize: Int
|
||||||
|
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
|
/// Creates a multipart form data object.
|
||||||
|
///
|
||||||
|
/// - returns: The multipart form data object.
|
||||||
|
public init() {
|
||||||
|
self.boundary = BoundaryGenerator.randomBoundary()
|
||||||
|
self.bodyParts = []
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more
|
||||||
|
/// information, please refer to the following article:
|
||||||
|
/// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html
|
||||||
|
///
|
||||||
|
|
||||||
|
self.streamBufferSize = 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Body Parts
|
||||||
|
|
||||||
|
/// Creates a body part from the data and appends it to the multipart form data object.
|
||||||
|
///
|
||||||
|
/// The body part data will be encoded using the following format:
|
||||||
|
///
|
||||||
|
/// - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
|
||||||
|
/// - Encoded data
|
||||||
|
/// - Multipart form boundary
|
||||||
|
///
|
||||||
|
/// - parameter data: The data to encode into the multipart form data.
|
||||||
|
/// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header.
|
||||||
|
public func append(_ data: Data, withName name: String) {
|
||||||
|
let headers = contentHeaders(withName: name)
|
||||||
|
let stream = InputStream(data: data)
|
||||||
|
let length = UInt64(data.count)
|
||||||
|
|
||||||
|
append(stream, withLength: length, headers: headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a body part from the data and appends it to the multipart form data object.
|
||||||
|
///
|
||||||
|
/// The body part data will be encoded using the following format:
|
||||||
|
///
|
||||||
|
/// - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
|
||||||
|
/// - `Content-Type: #{generated mimeType}` (HTTP Header)
|
||||||
|
/// - Encoded data
|
||||||
|
/// - Multipart form boundary
|
||||||
|
///
|
||||||
|
/// - parameter data: The data to encode into the multipart form data.
|
||||||
|
/// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header.
|
||||||
|
/// - parameter mimeType: The MIME type to associate with the data content type in the `Content-Type` HTTP header.
|
||||||
|
public func append(_ data: Data, withName name: String, mimeType: String) {
|
||||||
|
let headers = contentHeaders(withName: name, mimeType: mimeType)
|
||||||
|
let stream = InputStream(data: data)
|
||||||
|
let length = UInt64(data.count)
|
||||||
|
|
||||||
|
append(stream, withLength: length, headers: headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a body part from the data and appends it to the multipart form data object.
|
||||||
|
///
|
||||||
|
/// The body part data will be encoded using the following format:
|
||||||
|
///
|
||||||
|
/// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
|
||||||
|
/// - `Content-Type: #{mimeType}` (HTTP Header)
|
||||||
|
/// - Encoded file data
|
||||||
|
/// - Multipart form boundary
|
||||||
|
///
|
||||||
|
/// - parameter data: The data to encode into the multipart form data.
|
||||||
|
/// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header.
|
||||||
|
/// - parameter fileName: The filename to associate with the data in the `Content-Disposition` HTTP header.
|
||||||
|
/// - parameter mimeType: The MIME type to associate with the data in the `Content-Type` HTTP header.
|
||||||
|
public func append(_ data: Data, withName name: String, fileName: String, mimeType: String) {
|
||||||
|
let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
|
||||||
|
let stream = InputStream(data: data)
|
||||||
|
let length = UInt64(data.count)
|
||||||
|
|
||||||
|
append(stream, withLength: length, headers: headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a body part from the file and appends it to the multipart form data object.
|
||||||
|
///
|
||||||
|
/// The body part data will be encoded using the following format:
|
||||||
|
///
|
||||||
|
/// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header)
|
||||||
|
/// - `Content-Type: #{generated mimeType}` (HTTP Header)
|
||||||
|
/// - Encoded file data
|
||||||
|
/// - Multipart form boundary
|
||||||
|
///
|
||||||
|
/// The filename in the `Content-Disposition` HTTP header is generated from the last path component of the
|
||||||
|
/// `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the
|
||||||
|
/// system associated MIME type.
|
||||||
|
///
|
||||||
|
/// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data.
|
||||||
|
/// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header.
|
||||||
|
public func append(_ fileURL: URL, withName name: String) {
|
||||||
|
let fileName = fileURL.lastPathComponent
|
||||||
|
let pathExtension = fileURL.pathExtension
|
||||||
|
|
||||||
|
if !fileName.isEmpty && !pathExtension.isEmpty {
|
||||||
|
let mime = mimeType(forPathExtension: pathExtension)
|
||||||
|
append(fileURL, withName: name, fileName: fileName, mimeType: mime)
|
||||||
|
} else {
|
||||||
|
setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a body part from the file and appends it to the multipart form data object.
|
||||||
|
///
|
||||||
|
/// The body part data will be encoded using the following format:
|
||||||
|
///
|
||||||
|
/// - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header)
|
||||||
|
/// - Content-Type: #{mimeType} (HTTP Header)
|
||||||
|
/// - Encoded file data
|
||||||
|
/// - Multipart form boundary
|
||||||
|
///
|
||||||
|
/// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data.
|
||||||
|
/// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header.
|
||||||
|
/// - parameter fileName: The filename to associate with the file content in the `Content-Disposition` HTTP header.
|
||||||
|
/// - parameter mimeType: The MIME type to associate with the file content in the `Content-Type` HTTP header.
|
||||||
|
public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) {
|
||||||
|
let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
|
||||||
|
|
||||||
|
//============================================================
|
||||||
|
// Check 1 - is file URL?
|
||||||
|
//============================================================
|
||||||
|
|
||||||
|
guard fileURL.isFileURL else {
|
||||||
|
setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================
|
||||||
|
// Check 2 - is file URL reachable?
|
||||||
|
//============================================================
|
||||||
|
|
||||||
|
do {
|
||||||
|
let isReachable = try fileURL.checkPromisedItemIsReachable()
|
||||||
|
guard isReachable else {
|
||||||
|
setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================
|
||||||
|
// Check 3 - is file URL a directory?
|
||||||
|
//============================================================
|
||||||
|
|
||||||
|
var isDirectory: ObjCBool = false
|
||||||
|
let path = fileURL.path
|
||||||
|
|
||||||
|
guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else {
|
||||||
|
setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================
|
||||||
|
// Check 4 - can the file size be extracted?
|
||||||
|
//============================================================
|
||||||
|
|
||||||
|
let bodyContentLength: UInt64
|
||||||
|
|
||||||
|
do {
|
||||||
|
guard let fileSize = try FileManager.default.attributesOfItem(atPath: path)[.size] as? NSNumber else {
|
||||||
|
setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyContentLength = fileSize.uint64Value
|
||||||
|
} catch {
|
||||||
|
setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================
|
||||||
|
// Check 5 - can a stream be created from file URL?
|
||||||
|
//============================================================
|
||||||
|
|
||||||
|
guard let stream = InputStream(url: fileURL) else {
|
||||||
|
setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
append(stream, withLength: bodyContentLength, headers: headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a body part from the stream and appends it to the multipart form data object.
|
||||||
|
///
|
||||||
|
/// The body part data will be encoded using the following format:
|
||||||
|
///
|
||||||
|
/// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
|
||||||
|
/// - `Content-Type: #{mimeType}` (HTTP Header)
|
||||||
|
/// - Encoded stream data
|
||||||
|
/// - Multipart form boundary
|
||||||
|
///
|
||||||
|
/// - parameter stream: The input stream to encode in the multipart form data.
|
||||||
|
/// - parameter length: The content length of the stream.
|
||||||
|
/// - parameter name: The name to associate with the stream content in the `Content-Disposition` HTTP header.
|
||||||
|
/// - parameter fileName: The filename to associate with the stream content in the `Content-Disposition` HTTP header.
|
||||||
|
/// - parameter mimeType: The MIME type to associate with the stream content in the `Content-Type` HTTP header.
|
||||||
|
public func append(
|
||||||
|
_ stream: InputStream,
|
||||||
|
withLength length: UInt64,
|
||||||
|
name: String,
|
||||||
|
fileName: String,
|
||||||
|
mimeType: String) {
|
||||||
|
let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
|
||||||
|
append(stream, withLength: length, headers: headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a body part with the headers, stream and length and appends it to the multipart form data object.
|
||||||
|
///
|
||||||
|
/// The body part data will be encoded using the following format:
|
||||||
|
///
|
||||||
|
/// - HTTP headers
|
||||||
|
/// - Encoded stream data
|
||||||
|
/// - Multipart form boundary
|
||||||
|
///
|
||||||
|
/// - parameter stream: The input stream to encode in the multipart form data.
|
||||||
|
/// - parameter length: The content length of the stream.
|
||||||
|
/// - parameter headers: The HTTP headers for the body part.
|
||||||
|
public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) {
|
||||||
|
let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
|
||||||
|
bodyParts.append(bodyPart)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Data Encoding
|
||||||
|
|
||||||
|
/// Encodes all the appended body parts into a single `Data` value.
|
||||||
|
///
|
||||||
|
/// It is important to note that this method will load all the appended body parts into memory all at the same
|
||||||
|
/// time. This method should only be used when the encoded data will have a small memory footprint. For large data
|
||||||
|
/// cases, please use the `writeEncodedDataToDisk(fileURL:completionHandler:)` method.
|
||||||
|
///
|
||||||
|
/// - throws: An `AFError` if encoding encounters an error.
|
||||||
|
///
|
||||||
|
/// - returns: The encoded `Data` if encoding is successful.
|
||||||
|
public func encode() throws -> Data {
|
||||||
|
if let bodyPartError = bodyPartError {
|
||||||
|
throw bodyPartError
|
||||||
|
}
|
||||||
|
|
||||||
|
var encoded = Data()
|
||||||
|
|
||||||
|
bodyParts.first?.hasInitialBoundary = true
|
||||||
|
bodyParts.last?.hasFinalBoundary = true
|
||||||
|
|
||||||
|
for bodyPart in bodyParts {
|
||||||
|
let encodedData = try encode(bodyPart)
|
||||||
|
encoded.append(encodedData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the appended body parts into the given file URL.
|
||||||
|
///
|
||||||
|
/// This process is facilitated by reading and writing with input and output streams, respectively. Thus,
|
||||||
|
/// this approach is very memory efficient and should be used for large body part data.
|
||||||
|
///
|
||||||
|
/// - parameter fileURL: The file URL to write the multipart form data into.
|
||||||
|
///
|
||||||
|
/// - throws: An `AFError` if encoding encounters an error.
|
||||||
|
public func writeEncodedData(to fileURL: URL) throws {
|
||||||
|
if let bodyPartError = bodyPartError {
|
||||||
|
throw bodyPartError
|
||||||
|
}
|
||||||
|
|
||||||
|
if FileManager.default.fileExists(atPath: fileURL.path) {
|
||||||
|
throw AFError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL))
|
||||||
|
} else if !fileURL.isFileURL {
|
||||||
|
throw AFError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL))
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let outputStream = OutputStream(url: fileURL, append: false) else {
|
||||||
|
throw AFError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL))
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.open()
|
||||||
|
defer { outputStream.close() }
|
||||||
|
|
||||||
|
self.bodyParts.first?.hasInitialBoundary = true
|
||||||
|
self.bodyParts.last?.hasFinalBoundary = true
|
||||||
|
|
||||||
|
for bodyPart in self.bodyParts {
|
||||||
|
try write(bodyPart, to: outputStream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private - Body Part Encoding
|
||||||
|
|
||||||
|
private func encode(_ bodyPart: BodyPart) throws -> Data {
|
||||||
|
var encoded = Data()
|
||||||
|
|
||||||
|
let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
|
||||||
|
encoded.append(initialData)
|
||||||
|
|
||||||
|
let headerData = encodeHeaders(for: bodyPart)
|
||||||
|
encoded.append(headerData)
|
||||||
|
|
||||||
|
let bodyStreamData = try encodeBodyStream(for: bodyPart)
|
||||||
|
encoded.append(bodyStreamData)
|
||||||
|
|
||||||
|
if bodyPart.hasFinalBoundary {
|
||||||
|
encoded.append(finalBoundaryData())
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
private func encodeHeaders(for bodyPart: BodyPart) -> Data {
|
||||||
|
var headerText = ""
|
||||||
|
|
||||||
|
for (key, value) in bodyPart.headers {
|
||||||
|
headerText += "\(key): \(value)\(EncodingCharacters.crlf)"
|
||||||
|
}
|
||||||
|
headerText += EncodingCharacters.crlf
|
||||||
|
|
||||||
|
return headerText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
|
||||||
|
}
|
||||||
|
|
||||||
|
private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data {
|
||||||
|
let inputStream = bodyPart.bodyStream
|
||||||
|
inputStream.open()
|
||||||
|
defer { inputStream.close() }
|
||||||
|
|
||||||
|
var encoded = Data()
|
||||||
|
|
||||||
|
while inputStream.hasBytesAvailable {
|
||||||
|
var buffer = [UInt8](repeating: 0, count: streamBufferSize)
|
||||||
|
let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
|
||||||
|
|
||||||
|
if let error = inputStream.streamError {
|
||||||
|
throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error))
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytesRead > 0 {
|
||||||
|
encoded.append(buffer, count: bytesRead)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private - Writing Body Part to Output Stream
|
||||||
|
|
||||||
|
private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
||||||
|
try writeInitialBoundaryData(for: bodyPart, to: outputStream)
|
||||||
|
try writeHeaderData(for: bodyPart, to: outputStream)
|
||||||
|
try writeBodyStream(for: bodyPart, to: outputStream)
|
||||||
|
try writeFinalBoundaryData(for: bodyPart, to: outputStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
||||||
|
let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
|
||||||
|
return try write(initialData, to: outputStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
||||||
|
let headerData = encodeHeaders(for: bodyPart)
|
||||||
|
return try write(headerData, to: outputStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
||||||
|
let inputStream = bodyPart.bodyStream
|
||||||
|
|
||||||
|
inputStream.open()
|
||||||
|
defer { inputStream.close() }
|
||||||
|
|
||||||
|
while inputStream.hasBytesAvailable {
|
||||||
|
var buffer = [UInt8](repeating: 0, count: streamBufferSize)
|
||||||
|
let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
|
||||||
|
|
||||||
|
if let streamError = inputStream.streamError {
|
||||||
|
throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError))
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytesRead > 0 {
|
||||||
|
if buffer.count != bytesRead {
|
||||||
|
buffer = Array(buffer[0..<bytesRead])
|
||||||
|
}
|
||||||
|
|
||||||
|
try write(&buffer, to: outputStream)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func writeFinalBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
||||||
|
if bodyPart.hasFinalBoundary {
|
||||||
|
return try write(finalBoundaryData(), to: outputStream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private - Writing Buffered Data to Output Stream
|
||||||
|
|
||||||
|
private func write(_ data: Data, to outputStream: OutputStream) throws {
|
||||||
|
var buffer = [UInt8](repeating: 0, count: data.count)
|
||||||
|
data.copyBytes(to: &buffer, count: data.count)
|
||||||
|
|
||||||
|
return try write(&buffer, to: outputStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func write(_ buffer: inout [UInt8], to outputStream: OutputStream) throws {
|
||||||
|
var bytesToWrite = buffer.count
|
||||||
|
|
||||||
|
while bytesToWrite > 0, outputStream.hasSpaceAvailable {
|
||||||
|
let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite)
|
||||||
|
|
||||||
|
if let error = outputStream.streamError {
|
||||||
|
throw AFError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error))
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesToWrite -= bytesWritten
|
||||||
|
|
||||||
|
if bytesToWrite > 0 {
|
||||||
|
buffer = Array(buffer[bytesWritten..<buffer.count])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private - Mime Type
|
||||||
|
|
||||||
|
private func mimeType(forPathExtension pathExtension: String) -> String {
|
||||||
|
if
|
||||||
|
let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
|
||||||
|
let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue()
|
||||||
|
{
|
||||||
|
return contentType as String
|
||||||
|
}
|
||||||
|
|
||||||
|
return "application/octet-stream"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private - Content Headers
|
||||||
|
|
||||||
|
private func contentHeaders(withName name: String, fileName: String? = nil, mimeType: String? = nil) -> [String: String] {
|
||||||
|
var disposition = "form-data; name=\"\(name)\""
|
||||||
|
if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" }
|
||||||
|
|
||||||
|
var headers = ["Content-Disposition": disposition]
|
||||||
|
if let mimeType = mimeType { headers["Content-Type"] = mimeType }
|
||||||
|
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private - Boundary Encoding
|
||||||
|
|
||||||
|
private func initialBoundaryData() -> Data {
|
||||||
|
return BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func encapsulatedBoundaryData() -> Data {
|
||||||
|
return BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func finalBoundaryData() -> Data {
|
||||||
|
return BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private - Errors
|
||||||
|
|
||||||
|
private func setBodyPartError(withReason reason: AFError.MultipartEncodingFailureReason) {
|
||||||
|
guard bodyPartError == nil else { return }
|
||||||
|
bodyPartError = AFError.multipartEncodingFailed(reason: reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
//
|
||||||
|
// NetworkReachabilityManager.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if !os(watchOS)
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SystemConfiguration
|
||||||
|
|
||||||
|
/// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both WWAN and
|
||||||
|
/// WiFi network interfaces.
|
||||||
|
///
|
||||||
|
/// Reachability can be used to determine background information about why a network operation failed, or to retry
|
||||||
|
/// network requests when a connection is established. It should not be used to prevent a user from initiating a network
|
||||||
|
/// request, as it's possible that an initial request may be required to establish reachability.
|
||||||
|
open class NetworkReachabilityManager {
|
||||||
|
/// Defines the various states of network reachability.
|
||||||
|
///
|
||||||
|
/// - unknown: It is unknown whether the network is reachable.
|
||||||
|
/// - notReachable: The network is not reachable.
|
||||||
|
/// - reachable: The network is reachable.
|
||||||
|
public enum NetworkReachabilityStatus {
|
||||||
|
case unknown
|
||||||
|
case notReachable
|
||||||
|
case reachable(ConnectionType)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines the various connection types detected by reachability flags.
|
||||||
|
///
|
||||||
|
/// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi.
|
||||||
|
/// - wwan: The connection type is a WWAN connection.
|
||||||
|
public enum ConnectionType {
|
||||||
|
case ethernetOrWiFi
|
||||||
|
case wwan
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A closure executed when the network reachability status changes. The closure takes a single argument: the
|
||||||
|
/// network reachability status.
|
||||||
|
public typealias Listener = (NetworkReachabilityStatus) -> Void
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
/// Whether the network is currently reachable.
|
||||||
|
open var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi }
|
||||||
|
|
||||||
|
/// Whether the network is currently reachable over the WWAN interface.
|
||||||
|
open var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) }
|
||||||
|
|
||||||
|
/// Whether the network is currently reachable over Ethernet or WiFi interface.
|
||||||
|
open var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) }
|
||||||
|
|
||||||
|
/// The current network reachability status.
|
||||||
|
open var networkReachabilityStatus: NetworkReachabilityStatus {
|
||||||
|
guard let flags = self.flags else { return .unknown }
|
||||||
|
return networkReachabilityStatusForFlags(flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The dispatch queue to execute the `listener` closure on.
|
||||||
|
open var listenerQueue: DispatchQueue = DispatchQueue.main
|
||||||
|
|
||||||
|
/// A closure executed when the network reachability status changes.
|
||||||
|
open var listener: Listener?
|
||||||
|
|
||||||
|
open var flags: SCNetworkReachabilityFlags? {
|
||||||
|
var flags = SCNetworkReachabilityFlags()
|
||||||
|
|
||||||
|
if SCNetworkReachabilityGetFlags(reachability, &flags) {
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private let reachability: SCNetworkReachability
|
||||||
|
open var previousFlags: SCNetworkReachabilityFlags
|
||||||
|
|
||||||
|
// MARK: - Initialization
|
||||||
|
|
||||||
|
/// Creates a `NetworkReachabilityManager` instance with the specified host.
|
||||||
|
///
|
||||||
|
/// - parameter host: The host used to evaluate network reachability.
|
||||||
|
///
|
||||||
|
/// - returns: The new `NetworkReachabilityManager` instance.
|
||||||
|
public convenience init?(host: String) {
|
||||||
|
guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil }
|
||||||
|
self.init(reachability: reachability)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0.
|
||||||
|
///
|
||||||
|
/// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing
|
||||||
|
/// status of the device, both IPv4 and IPv6.
|
||||||
|
///
|
||||||
|
/// - returns: The new `NetworkReachabilityManager` instance.
|
||||||
|
public convenience init?() {
|
||||||
|
var address = sockaddr_in()
|
||||||
|
address.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
|
||||||
|
address.sin_family = sa_family_t(AF_INET)
|
||||||
|
|
||||||
|
guard let reachability = withUnsafePointer(to: &address, { pointer in
|
||||||
|
return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout<sockaddr>.size) {
|
||||||
|
return SCNetworkReachabilityCreateWithAddress(nil, $0)
|
||||||
|
}
|
||||||
|
}) else { return nil }
|
||||||
|
|
||||||
|
self.init(reachability: reachability)
|
||||||
|
}
|
||||||
|
|
||||||
|
private init(reachability: SCNetworkReachability) {
|
||||||
|
self.reachability = reachability
|
||||||
|
|
||||||
|
// Set the previous flags to an unreserved value to represent unknown status
|
||||||
|
self.previousFlags = SCNetworkReachabilityFlags(rawValue: 1 << 30)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
stopListening()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Listening
|
||||||
|
|
||||||
|
/// Starts listening for changes in network reachability status.
|
||||||
|
///
|
||||||
|
/// - returns: `true` if listening was started successfully, `false` otherwise.
|
||||||
|
@discardableResult
|
||||||
|
open func startListening() -> Bool {
|
||||||
|
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
|
||||||
|
context.info = Unmanaged.passUnretained(self).toOpaque()
|
||||||
|
|
||||||
|
let callbackEnabled = SCNetworkReachabilitySetCallback(
|
||||||
|
reachability, { (_, flags, info) in
|
||||||
|
let reachability = Unmanaged<NetworkReachabilityManager>.fromOpaque(info!).takeUnretainedValue()
|
||||||
|
reachability.notifyListener(flags)
|
||||||
|
},
|
||||||
|
&context
|
||||||
|
)
|
||||||
|
|
||||||
|
let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue)
|
||||||
|
|
||||||
|
listenerQueue.async {
|
||||||
|
self.previousFlags = SCNetworkReachabilityFlags(rawValue: 1 << 30)
|
||||||
|
|
||||||
|
guard let flags = self.flags else { return }
|
||||||
|
|
||||||
|
self.notifyListener(flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
return callbackEnabled && queueEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops listening for changes in network reachability status.
|
||||||
|
open func stopListening() {
|
||||||
|
SCNetworkReachabilitySetCallback(reachability, nil, nil)
|
||||||
|
SCNetworkReachabilitySetDispatchQueue(reachability, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Internal - Listener Notification
|
||||||
|
|
||||||
|
func notifyListener(_ flags: SCNetworkReachabilityFlags) {
|
||||||
|
guard previousFlags != flags else { return }
|
||||||
|
previousFlags = flags
|
||||||
|
|
||||||
|
listener?(networkReachabilityStatusForFlags(flags))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Internal - Network Reachability Status
|
||||||
|
|
||||||
|
func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus {
|
||||||
|
guard isNetworkReachable(with: flags) else { return .notReachable }
|
||||||
|
|
||||||
|
var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi)
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return networkStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNetworkReachable(with flags: SCNetworkReachabilityFlags) -> Bool {
|
||||||
|
let isReachable = flags.contains(.reachable)
|
||||||
|
let needsConnection = flags.contains(.connectionRequired)
|
||||||
|
let canConnectAutomatically = flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic)
|
||||||
|
let canConnectWithoutUserInteraction = canConnectAutomatically && !flags.contains(.interventionRequired)
|
||||||
|
|
||||||
|
return isReachable && (!needsConnection || canConnectWithoutUserInteraction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {}
|
||||||
|
|
||||||
|
/// Returns whether the two network reachability status values are equal.
|
||||||
|
///
|
||||||
|
/// - parameter lhs: The left-hand side value to compare.
|
||||||
|
/// - parameter rhs: The right-hand side value to compare.
|
||||||
|
///
|
||||||
|
/// - returns: `true` if the two values are equal, `false` otherwise.
|
||||||
|
public func ==(
|
||||||
|
lhs: NetworkReachabilityManager.NetworkReachabilityStatus,
|
||||||
|
rhs: NetworkReachabilityManager.NetworkReachabilityStatus)
|
||||||
|
-> Bool {
|
||||||
|
switch (lhs, rhs) {
|
||||||
|
case (.unknown, .unknown):
|
||||||
|
return true
|
||||||
|
case (.notReachable, .notReachable):
|
||||||
|
return true
|
||||||
|
case let (.reachable(lhsConnectionType), .reachable(rhsConnectionType)):
|
||||||
|
return lhsConnectionType == rhsConnectionType
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
482
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/ParameterEncoding.swift
generated
Normal file
482
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/ParameterEncoding.swift
generated
Normal file
@@ -0,0 +1,482 @@
|
|||||||
|
//
|
||||||
|
// ParameterEncoding.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// HTTP method definitions.
|
||||||
|
///
|
||||||
|
/// See https://tools.ietf.org/html/rfc7231#section-4.3
|
||||||
|
public enum HTTPMethod: String {
|
||||||
|
case options = "OPTIONS"
|
||||||
|
case get = "GET"
|
||||||
|
case head = "HEAD"
|
||||||
|
case post = "POST"
|
||||||
|
case put = "PUT"
|
||||||
|
case patch = "PATCH"
|
||||||
|
case delete = "DELETE"
|
||||||
|
case trace = "TRACE"
|
||||||
|
case connect = "CONNECT"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// A dictionary of parameters to apply to a `URLRequest`.
|
||||||
|
public typealias Parameters = [String: Any]
|
||||||
|
|
||||||
|
/// A type used to define how a set of parameters are applied to a `URLRequest`.
|
||||||
|
public protocol ParameterEncoding {
|
||||||
|
/// Creates a URL request by encoding parameters and applying them onto an existing request.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The request to have parameters applied.
|
||||||
|
/// - parameter parameters: The parameters to apply.
|
||||||
|
///
|
||||||
|
/// - throws: An `AFError.parameterEncodingFailed` error if encoding fails.
|
||||||
|
///
|
||||||
|
/// - returns: The encoded request.
|
||||||
|
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP
|
||||||
|
/// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as
|
||||||
|
/// the HTTP body depends on the destination of the encoding.
|
||||||
|
///
|
||||||
|
/// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to
|
||||||
|
/// `application/x-www-form-urlencoded; charset=utf-8`.
|
||||||
|
///
|
||||||
|
/// There is no published specification for how to encode collection types. By default the convention of appending
|
||||||
|
/// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for
|
||||||
|
/// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the
|
||||||
|
/// square brackets appended to array keys.
|
||||||
|
///
|
||||||
|
/// `BoolEncoding` can be used to configure how boolean values are encoded. The default behavior is to encode
|
||||||
|
/// `true` as 1 and `false` as 0.
|
||||||
|
public struct URLEncoding: ParameterEncoding {
|
||||||
|
|
||||||
|
// MARK: Helper Types
|
||||||
|
|
||||||
|
/// Defines whether the url-encoded query string is applied to the existing query string or HTTP body of the
|
||||||
|
/// resulting URL request.
|
||||||
|
///
|
||||||
|
/// - methodDependent: Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE`
|
||||||
|
/// requests and sets as the HTTP body for requests with any other HTTP method.
|
||||||
|
/// - queryString: Sets or appends encoded query string result to existing query string.
|
||||||
|
/// - httpBody: Sets encoded query string result as the HTTP body of the URL request.
|
||||||
|
public enum Destination {
|
||||||
|
case methodDependent, queryString, httpBody
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures how `Array` parameters are encoded.
|
||||||
|
///
|
||||||
|
/// - brackets: An empty set of square brackets is appended to the key for every value.
|
||||||
|
/// This is the default behavior.
|
||||||
|
/// - noBrackets: No brackets are appended. The key is encoded as is.
|
||||||
|
public enum ArrayEncoding {
|
||||||
|
case brackets, noBrackets
|
||||||
|
|
||||||
|
func encode(key: String) -> String {
|
||||||
|
switch self {
|
||||||
|
case .brackets:
|
||||||
|
return "\(key)[]"
|
||||||
|
case .noBrackets:
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures how `Bool` parameters are encoded.
|
||||||
|
///
|
||||||
|
/// - numeric: Encode `true` as `1` and `false` as `0`. This is the default behavior.
|
||||||
|
/// - literal: Encode `true` and `false` as string literals.
|
||||||
|
public enum BoolEncoding {
|
||||||
|
case numeric, literal
|
||||||
|
|
||||||
|
func encode(value: Bool) -> String {
|
||||||
|
switch self {
|
||||||
|
case .numeric:
|
||||||
|
return value ? "1" : "0"
|
||||||
|
case .literal:
|
||||||
|
return value ? "true" : "false"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// Returns a default `URLEncoding` instance.
|
||||||
|
public static var `default`: URLEncoding { return URLEncoding() }
|
||||||
|
|
||||||
|
/// Returns a `URLEncoding` instance with a `.methodDependent` destination.
|
||||||
|
public static var methodDependent: URLEncoding { return URLEncoding() }
|
||||||
|
|
||||||
|
/// Returns a `URLEncoding` instance with a `.queryString` destination.
|
||||||
|
public static var queryString: URLEncoding { return URLEncoding(destination: .queryString) }
|
||||||
|
|
||||||
|
/// Returns a `URLEncoding` instance with an `.httpBody` destination.
|
||||||
|
public static var httpBody: URLEncoding { return URLEncoding(destination: .httpBody) }
|
||||||
|
|
||||||
|
/// The destination defining where the encoded query string is to be applied to the URL request.
|
||||||
|
public let destination: Destination
|
||||||
|
|
||||||
|
/// The encoding to use for `Array` parameters.
|
||||||
|
public let arrayEncoding: ArrayEncoding
|
||||||
|
|
||||||
|
/// The encoding to use for `Bool` parameters.
|
||||||
|
public let boolEncoding: BoolEncoding
|
||||||
|
|
||||||
|
// MARK: Initialization
|
||||||
|
|
||||||
|
/// Creates a `URLEncoding` instance using the specified destination.
|
||||||
|
///
|
||||||
|
/// - parameter destination: The destination defining where the encoded query string is to be applied.
|
||||||
|
/// - parameter arrayEncoding: The encoding to use for `Array` parameters.
|
||||||
|
/// - parameter boolEncoding: The encoding to use for `Bool` parameters.
|
||||||
|
///
|
||||||
|
/// - returns: The new `URLEncoding` instance.
|
||||||
|
public init(destination: Destination = .methodDependent, arrayEncoding: ArrayEncoding = .brackets, boolEncoding: BoolEncoding = .numeric) {
|
||||||
|
self.destination = destination
|
||||||
|
self.arrayEncoding = arrayEncoding
|
||||||
|
self.boolEncoding = boolEncoding
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Encoding
|
||||||
|
|
||||||
|
/// Creates a URL request by encoding parameters and applying them onto an existing request.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The request to have parameters applied.
|
||||||
|
/// - parameter parameters: The parameters to apply.
|
||||||
|
///
|
||||||
|
/// - throws: An `Error` if the encoding process encounters an error.
|
||||||
|
///
|
||||||
|
/// - returns: The encoded request.
|
||||||
|
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
|
||||||
|
var urlRequest = try urlRequest.asURLRequest()
|
||||||
|
|
||||||
|
guard let parameters = parameters else { return urlRequest }
|
||||||
|
|
||||||
|
if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
|
||||||
|
guard let url = urlRequest.url else {
|
||||||
|
throw AFError.parameterEncodingFailed(reason: .missingURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
|
||||||
|
let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
|
||||||
|
urlComponents.percentEncodedQuery = percentEncodedQuery
|
||||||
|
urlRequest.url = urlComponents.url
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
|
||||||
|
urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||||
|
}
|
||||||
|
|
||||||
|
urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return urlRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion.
|
||||||
|
///
|
||||||
|
/// - parameter key: The key of the query component.
|
||||||
|
/// - parameter value: The value of the query component.
|
||||||
|
///
|
||||||
|
/// - returns: The percent-escaped, URL encoded query string components.
|
||||||
|
public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
|
||||||
|
var components: [(String, String)] = []
|
||||||
|
|
||||||
|
if let dictionary = value as? [String: Any] {
|
||||||
|
for (nestedKey, value) in dictionary {
|
||||||
|
components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
|
||||||
|
}
|
||||||
|
} else if let array = value as? [Any] {
|
||||||
|
for value in array {
|
||||||
|
components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value)
|
||||||
|
}
|
||||||
|
} else if let value = value as? NSNumber {
|
||||||
|
if value.isBool {
|
||||||
|
components.append((escape(key), escape(boolEncoding.encode(value: value.boolValue))))
|
||||||
|
} else {
|
||||||
|
components.append((escape(key), escape("\(value)")))
|
||||||
|
}
|
||||||
|
} else if let bool = value as? Bool {
|
||||||
|
components.append((escape(key), escape(boolEncoding.encode(value: bool))))
|
||||||
|
} else {
|
||||||
|
components.append((escape(key), escape("\(value)")))
|
||||||
|
}
|
||||||
|
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a percent-escaped string following RFC 3986 for a query string key or value.
|
||||||
|
///
|
||||||
|
/// RFC 3986 states that the following characters are "reserved" characters.
|
||||||
|
///
|
||||||
|
/// - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
|
||||||
|
/// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
|
||||||
|
///
|
||||||
|
/// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
|
||||||
|
/// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
|
||||||
|
/// should be percent-escaped in the query string.
|
||||||
|
///
|
||||||
|
/// - parameter string: The string to be percent-escaped.
|
||||||
|
///
|
||||||
|
/// - returns: The percent-escaped string.
|
||||||
|
public func escape(_ string: String) -> String {
|
||||||
|
let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
|
||||||
|
let subDelimitersToEncode = "!$&'()*+,;="
|
||||||
|
|
||||||
|
var allowedCharacterSet = CharacterSet.urlQueryAllowed
|
||||||
|
allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
|
||||||
|
|
||||||
|
var escaped = ""
|
||||||
|
|
||||||
|
//==========================================================================================================
|
||||||
|
//
|
||||||
|
// Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few
|
||||||
|
// hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no
|
||||||
|
// longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more
|
||||||
|
// info, please refer to:
|
||||||
|
//
|
||||||
|
// - https://github.com/Alamofire/Alamofire/issues/206
|
||||||
|
//
|
||||||
|
//==========================================================================================================
|
||||||
|
|
||||||
|
if #available(iOS 8.3, *) {
|
||||||
|
escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
|
||||||
|
} else {
|
||||||
|
let batchSize = 50
|
||||||
|
var index = string.startIndex
|
||||||
|
|
||||||
|
while index != string.endIndex {
|
||||||
|
let startIndex = index
|
||||||
|
let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex
|
||||||
|
let range = startIndex..<endIndex
|
||||||
|
|
||||||
|
let substring = string[range]
|
||||||
|
|
||||||
|
escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? String(substring)
|
||||||
|
|
||||||
|
index = endIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return escaped
|
||||||
|
}
|
||||||
|
|
||||||
|
private func query(_ parameters: [String: Any]) -> String {
|
||||||
|
var components: [(String, String)] = []
|
||||||
|
|
||||||
|
for key in parameters.keys.sorted(by: <) {
|
||||||
|
let value = parameters[key]!
|
||||||
|
components += queryComponents(fromKey: key, value: value)
|
||||||
|
}
|
||||||
|
return components.map { "\($0)=\($1)" }.joined(separator: "&")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func encodesParametersInURL(with method: HTTPMethod) -> Bool {
|
||||||
|
switch destination {
|
||||||
|
case .queryString:
|
||||||
|
return true
|
||||||
|
case .httpBody:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case .get, .head, .delete:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the
|
||||||
|
/// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`.
|
||||||
|
public struct JSONEncoding: ParameterEncoding {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// Returns a `JSONEncoding` instance with default writing options.
|
||||||
|
public static var `default`: JSONEncoding { return JSONEncoding() }
|
||||||
|
|
||||||
|
/// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options.
|
||||||
|
public static var prettyPrinted: JSONEncoding { return JSONEncoding(options: .prettyPrinted) }
|
||||||
|
|
||||||
|
/// The options for writing the parameters as JSON data.
|
||||||
|
public let options: JSONSerialization.WritingOptions
|
||||||
|
|
||||||
|
// MARK: Initialization
|
||||||
|
|
||||||
|
/// Creates a `JSONEncoding` instance using the specified options.
|
||||||
|
///
|
||||||
|
/// - parameter options: The options for writing the parameters as JSON data.
|
||||||
|
///
|
||||||
|
/// - returns: The new `JSONEncoding` instance.
|
||||||
|
public init(options: JSONSerialization.WritingOptions = []) {
|
||||||
|
self.options = options
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Encoding
|
||||||
|
|
||||||
|
/// Creates a URL request by encoding parameters and applying them onto an existing request.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The request to have parameters applied.
|
||||||
|
/// - parameter parameters: The parameters to apply.
|
||||||
|
///
|
||||||
|
/// - throws: An `Error` if the encoding process encounters an error.
|
||||||
|
///
|
||||||
|
/// - returns: The encoded request.
|
||||||
|
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
|
||||||
|
var urlRequest = try urlRequest.asURLRequest()
|
||||||
|
|
||||||
|
guard let parameters = parameters else { return urlRequest }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let data = try JSONSerialization.data(withJSONObject: parameters, options: options)
|
||||||
|
|
||||||
|
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
|
||||||
|
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
}
|
||||||
|
|
||||||
|
urlRequest.httpBody = data
|
||||||
|
} catch {
|
||||||
|
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
|
||||||
|
}
|
||||||
|
|
||||||
|
return urlRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a URL request by encoding the JSON object and setting the resulting data on the HTTP body.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The request to apply the JSON object to.
|
||||||
|
/// - parameter jsonObject: The JSON object to apply to the request.
|
||||||
|
///
|
||||||
|
/// - throws: An `Error` if the encoding process encounters an error.
|
||||||
|
///
|
||||||
|
/// - returns: The encoded request.
|
||||||
|
public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest {
|
||||||
|
var urlRequest = try urlRequest.asURLRequest()
|
||||||
|
|
||||||
|
guard let jsonObject = jsonObject else { return urlRequest }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options)
|
||||||
|
|
||||||
|
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
|
||||||
|
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
}
|
||||||
|
|
||||||
|
urlRequest.httpBody = data
|
||||||
|
} catch {
|
||||||
|
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
|
||||||
|
}
|
||||||
|
|
||||||
|
return urlRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Uses `PropertyListSerialization` to create a plist representation of the parameters object, according to the
|
||||||
|
/// associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header
|
||||||
|
/// field of an encoded request is set to `application/x-plist`.
|
||||||
|
public struct PropertyListEncoding: ParameterEncoding {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// Returns a default `PropertyListEncoding` instance.
|
||||||
|
public static var `default`: PropertyListEncoding { return PropertyListEncoding() }
|
||||||
|
|
||||||
|
/// Returns a `PropertyListEncoding` instance with xml formatting and default writing options.
|
||||||
|
public static var xml: PropertyListEncoding { return PropertyListEncoding(format: .xml) }
|
||||||
|
|
||||||
|
/// Returns a `PropertyListEncoding` instance with binary formatting and default writing options.
|
||||||
|
public static var binary: PropertyListEncoding { return PropertyListEncoding(format: .binary) }
|
||||||
|
|
||||||
|
/// The property list serialization format.
|
||||||
|
public let format: PropertyListSerialization.PropertyListFormat
|
||||||
|
|
||||||
|
/// The options for writing the parameters as plist data.
|
||||||
|
public let options: PropertyListSerialization.WriteOptions
|
||||||
|
|
||||||
|
// MARK: Initialization
|
||||||
|
|
||||||
|
/// Creates a `PropertyListEncoding` instance using the specified format and options.
|
||||||
|
///
|
||||||
|
/// - parameter format: The property list serialization format.
|
||||||
|
/// - parameter options: The options for writing the parameters as plist data.
|
||||||
|
///
|
||||||
|
/// - returns: The new `PropertyListEncoding` instance.
|
||||||
|
public init(
|
||||||
|
format: PropertyListSerialization.PropertyListFormat = .xml,
|
||||||
|
options: PropertyListSerialization.WriteOptions = 0) {
|
||||||
|
self.format = format
|
||||||
|
self.options = options
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Encoding
|
||||||
|
|
||||||
|
/// Creates a URL request by encoding parameters and applying them onto an existing request.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The request to have parameters applied.
|
||||||
|
/// - parameter parameters: The parameters to apply.
|
||||||
|
///
|
||||||
|
/// - throws: An `Error` if the encoding process encounters an error.
|
||||||
|
///
|
||||||
|
/// - returns: The encoded request.
|
||||||
|
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
|
||||||
|
var urlRequest = try urlRequest.asURLRequest()
|
||||||
|
|
||||||
|
guard let parameters = parameters else { return urlRequest }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let data = try PropertyListSerialization.data(
|
||||||
|
fromPropertyList: parameters,
|
||||||
|
format: format,
|
||||||
|
options: options
|
||||||
|
)
|
||||||
|
|
||||||
|
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
|
||||||
|
urlRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type")
|
||||||
|
}
|
||||||
|
|
||||||
|
urlRequest.httpBody = data
|
||||||
|
} catch {
|
||||||
|
throw AFError.parameterEncodingFailed(reason: .propertyListEncodingFailed(error: error))
|
||||||
|
}
|
||||||
|
|
||||||
|
return urlRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
extension NSNumber {
|
||||||
|
fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) }
|
||||||
|
}
|
||||||
658
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/Request.swift
generated
Normal file
658
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/Request.swift
generated
Normal file
@@ -0,0 +1,658 @@
|
|||||||
|
//
|
||||||
|
// Request.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// A type that can inspect and optionally adapt a `URLRequest` in some manner if necessary.
|
||||||
|
public protocol RequestAdapter {
|
||||||
|
/// Inspects and adapts the specified `URLRequest` in some manner if necessary and returns the result.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The URL request to adapt.
|
||||||
|
///
|
||||||
|
/// - throws: An `Error` if the adaptation encounters an error.
|
||||||
|
///
|
||||||
|
/// - returns: The adapted `URLRequest`.
|
||||||
|
func adapt(_ urlRequest: URLRequest) throws -> URLRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// A closure executed when the `RequestRetrier` determines whether a `Request` should be retried or not.
|
||||||
|
public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void
|
||||||
|
|
||||||
|
/// A type that determines whether a request should be retried after being executed by the specified session manager
|
||||||
|
/// and encountering an error.
|
||||||
|
public protocol RequestRetrier {
|
||||||
|
/// Determines whether the `Request` should be retried by calling the `completion` closure.
|
||||||
|
///
|
||||||
|
/// This operation is fully asynchronous. Any amount of time can be taken to determine whether the request needs
|
||||||
|
/// to be retried. The one requirement is that the completion closure is called to ensure the request is properly
|
||||||
|
/// cleaned up after.
|
||||||
|
///
|
||||||
|
/// - parameter manager: The session manager the request was executed on.
|
||||||
|
/// - parameter request: The request that failed due to the encountered error.
|
||||||
|
/// - parameter error: The error encountered when executing the request.
|
||||||
|
/// - parameter completion: The completion closure to be executed when retry decision has been determined.
|
||||||
|
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
protocol TaskConvertible {
|
||||||
|
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A dictionary of headers to apply to a `URLRequest`.
|
||||||
|
public typealias HTTPHeaders = [String: String]
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Responsible for sending a request and receiving the response and associated data from the server, as well as
|
||||||
|
/// managing its underlying `URLSessionTask`.
|
||||||
|
open class Request {
|
||||||
|
|
||||||
|
// MARK: Helper Types
|
||||||
|
|
||||||
|
/// A closure executed when monitoring upload or download progress of a request.
|
||||||
|
public typealias ProgressHandler = (Progress) -> Void
|
||||||
|
|
||||||
|
enum RequestTask {
|
||||||
|
case data(TaskConvertible?, URLSessionTask?)
|
||||||
|
case download(TaskConvertible?, URLSessionTask?)
|
||||||
|
case upload(TaskConvertible?, URLSessionTask?)
|
||||||
|
case stream(TaskConvertible?, URLSessionTask?)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The delegate for the underlying task.
|
||||||
|
open internal(set) var delegate: TaskDelegate {
|
||||||
|
get {
|
||||||
|
taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
|
||||||
|
return taskDelegate
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
|
||||||
|
taskDelegate = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The underlying task.
|
||||||
|
open var task: URLSessionTask? { return delegate.task }
|
||||||
|
|
||||||
|
/// The session belonging to the underlying task.
|
||||||
|
public let session: URLSession
|
||||||
|
|
||||||
|
/// The request sent or to be sent to the server.
|
||||||
|
open var request: URLRequest? { return task?.originalRequest }
|
||||||
|
|
||||||
|
/// The response received from the server, if any.
|
||||||
|
open var response: HTTPURLResponse? { return task?.response as? HTTPURLResponse }
|
||||||
|
|
||||||
|
/// The number of times the request has been retried.
|
||||||
|
open internal(set) var retryCount: UInt = 0
|
||||||
|
|
||||||
|
let originalTask: TaskConvertible?
|
||||||
|
|
||||||
|
var startTime: CFAbsoluteTime?
|
||||||
|
var endTime: CFAbsoluteTime?
|
||||||
|
|
||||||
|
var validations: [() -> Void] = []
|
||||||
|
|
||||||
|
private var taskDelegate: TaskDelegate
|
||||||
|
private var taskDelegateLock = NSLock()
|
||||||
|
|
||||||
|
// MARK: Lifecycle
|
||||||
|
|
||||||
|
init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
|
||||||
|
self.session = session
|
||||||
|
|
||||||
|
switch requestTask {
|
||||||
|
case .data(let originalTask, let task):
|
||||||
|
taskDelegate = DataTaskDelegate(task: task)
|
||||||
|
self.originalTask = originalTask
|
||||||
|
case .download(let originalTask, let task):
|
||||||
|
taskDelegate = DownloadTaskDelegate(task: task)
|
||||||
|
self.originalTask = originalTask
|
||||||
|
case .upload(let originalTask, let task):
|
||||||
|
taskDelegate = UploadTaskDelegate(task: task)
|
||||||
|
self.originalTask = originalTask
|
||||||
|
case .stream(let originalTask, let task):
|
||||||
|
taskDelegate = TaskDelegate(task: task)
|
||||||
|
self.originalTask = originalTask
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate.error = error
|
||||||
|
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Authentication
|
||||||
|
|
||||||
|
/// Associates an HTTP Basic credential with the request.
|
||||||
|
///
|
||||||
|
/// - parameter user: The user.
|
||||||
|
/// - parameter password: The password.
|
||||||
|
/// - parameter persistence: The URL credential persistence. `.ForSession` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
open func authenticate(
|
||||||
|
user: String,
|
||||||
|
password: String,
|
||||||
|
persistence: URLCredential.Persistence = .forSession)
|
||||||
|
-> Self {
|
||||||
|
let credential = URLCredential(user: user, password: password, persistence: persistence)
|
||||||
|
return authenticate(usingCredential: credential)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associates a specified credential with the request.
|
||||||
|
///
|
||||||
|
/// - parameter credential: The credential.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
open func authenticate(usingCredential credential: URLCredential) -> Self {
|
||||||
|
delegate.credential = credential
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a base64 encoded basic authentication credential as an authorization header tuple.
|
||||||
|
///
|
||||||
|
/// - parameter user: The user.
|
||||||
|
/// - parameter password: The password.
|
||||||
|
///
|
||||||
|
/// - returns: A tuple with Authorization header and credential value if encoding succeeds, `nil` otherwise.
|
||||||
|
open class func authorizationHeader(user: String, password: String) -> (key: String, value: String)? {
|
||||||
|
guard let data = "\(user):\(password)".data(using: .utf8) else { return nil }
|
||||||
|
|
||||||
|
let credential = data.base64EncodedString(options: [])
|
||||||
|
|
||||||
|
return (key: "Authorization", value: "Basic \(credential)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: State
|
||||||
|
|
||||||
|
/// Resumes the request.
|
||||||
|
open func resume() {
|
||||||
|
guard let task = task else { delegate.queue.isSuspended = false ; return }
|
||||||
|
|
||||||
|
if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }
|
||||||
|
|
||||||
|
task.resume()
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: Notification.Name.Task.DidResume,
|
||||||
|
object: self,
|
||||||
|
userInfo: [Notification.Key.Task: task]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Suspends the request.
|
||||||
|
open func suspend() {
|
||||||
|
guard let task = task else { return }
|
||||||
|
|
||||||
|
task.suspend()
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: Notification.Name.Task.DidSuspend,
|
||||||
|
object: self,
|
||||||
|
userInfo: [Notification.Key.Task: task]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancels the request.
|
||||||
|
open func cancel() {
|
||||||
|
guard let task = task else { return }
|
||||||
|
|
||||||
|
task.cancel()
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: Notification.Name.Task.DidCancel,
|
||||||
|
object: self,
|
||||||
|
userInfo: [Notification.Key.Task: task]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - CustomStringConvertible
|
||||||
|
|
||||||
|
extension Request: CustomStringConvertible {
|
||||||
|
/// The textual representation used when written to an output stream, which includes the HTTP method and URL, as
|
||||||
|
/// well as the response status code if a response has been received.
|
||||||
|
open var description: String {
|
||||||
|
var components: [String] = []
|
||||||
|
|
||||||
|
if let HTTPMethod = request?.httpMethod {
|
||||||
|
components.append(HTTPMethod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let urlString = request?.url?.absoluteString {
|
||||||
|
components.append(urlString)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let response = response {
|
||||||
|
components.append("(\(response.statusCode))")
|
||||||
|
}
|
||||||
|
|
||||||
|
return components.joined(separator: " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - CustomDebugStringConvertible
|
||||||
|
|
||||||
|
extension Request: CustomDebugStringConvertible {
|
||||||
|
/// The textual representation used when written to an output stream, in the form of a cURL command.
|
||||||
|
open var debugDescription: String {
|
||||||
|
return cURLRepresentation()
|
||||||
|
}
|
||||||
|
|
||||||
|
func cURLRepresentation() -> String {
|
||||||
|
var components = ["$ curl -v"]
|
||||||
|
|
||||||
|
guard let request = self.request,
|
||||||
|
let url = request.url,
|
||||||
|
let host = url.host
|
||||||
|
else {
|
||||||
|
return "$ curl command could not be created"
|
||||||
|
}
|
||||||
|
|
||||||
|
if let httpMethod = request.httpMethod, httpMethod != "GET" {
|
||||||
|
components.append("-X \(httpMethod)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if let credentialStorage = self.session.configuration.urlCredentialStorage {
|
||||||
|
let protectionSpace = URLProtectionSpace(
|
||||||
|
host: host,
|
||||||
|
port: url.port ?? 0,
|
||||||
|
protocol: url.scheme,
|
||||||
|
realm: host,
|
||||||
|
authenticationMethod: NSURLAuthenticationMethodHTTPBasic
|
||||||
|
)
|
||||||
|
|
||||||
|
if let credentials = credentialStorage.credentials(for: protectionSpace)?.values {
|
||||||
|
for credential in credentials {
|
||||||
|
guard let user = credential.user, let password = credential.password else { continue }
|
||||||
|
components.append("-u \(user):\(password)")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let credential = delegate.credential, let user = credential.user, let password = credential.password {
|
||||||
|
components.append("-u \(user):\(password)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if session.configuration.httpShouldSetCookies {
|
||||||
|
if
|
||||||
|
let cookieStorage = session.configuration.httpCookieStorage,
|
||||||
|
let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty
|
||||||
|
{
|
||||||
|
let string = cookies.reduce("") { $0 + "\($1.name)=\($1.value);" }
|
||||||
|
|
||||||
|
#if swift(>=3.2)
|
||||||
|
components.append("-b \"\(string[..<string.index(before: string.endIndex)])\"")
|
||||||
|
#else
|
||||||
|
components.append("-b \"\(string.substring(to: string.characters.index(before: string.endIndex)))\"")
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var headers: [AnyHashable: Any] = [:]
|
||||||
|
|
||||||
|
session.configuration.httpAdditionalHeaders?.filter { $0.0 != AnyHashable("Cookie") }
|
||||||
|
.forEach { headers[$0.0] = $0.1 }
|
||||||
|
|
||||||
|
request.allHTTPHeaderFields?.filter { $0.0 != "Cookie" }
|
||||||
|
.forEach { headers[$0.0] = $0.1 }
|
||||||
|
|
||||||
|
components += headers.map {
|
||||||
|
let escapedValue = String(describing: $0.value).replacingOccurrences(of: "\"", with: "\\\"")
|
||||||
|
|
||||||
|
return "-H \"\($0.key): \(escapedValue)\""
|
||||||
|
}
|
||||||
|
|
||||||
|
if let httpBodyData = request.httpBody, let httpBody = String(data: httpBodyData, encoding: .utf8) {
|
||||||
|
var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"")
|
||||||
|
escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"")
|
||||||
|
|
||||||
|
components.append("-d \"\(escapedBody)\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
components.append("\"\(url.absoluteString)\"")
|
||||||
|
|
||||||
|
return components.joined(separator: " \\\n\t")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Specific type of `Request` that manages an underlying `URLSessionDataTask`.
|
||||||
|
open class DataRequest: Request {
|
||||||
|
|
||||||
|
// MARK: Helper Types
|
||||||
|
|
||||||
|
struct Requestable: TaskConvertible {
|
||||||
|
let urlRequest: URLRequest
|
||||||
|
|
||||||
|
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||||
|
do {
|
||||||
|
let urlRequest = try self.urlRequest.adapt(using: adapter)
|
||||||
|
return queue.sync { session.dataTask(with: urlRequest) }
|
||||||
|
} catch {
|
||||||
|
throw AdaptError(error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The request sent or to be sent to the server.
|
||||||
|
open override var request: URLRequest? {
|
||||||
|
if let request = super.request { return request }
|
||||||
|
if let requestable = originalTask as? Requestable { return requestable.urlRequest }
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The progress of fetching the response data from the server for the request.
|
||||||
|
open var progress: Progress { return dataDelegate.progress }
|
||||||
|
|
||||||
|
var dataDelegate: DataTaskDelegate { return delegate as! DataTaskDelegate }
|
||||||
|
|
||||||
|
// MARK: Stream
|
||||||
|
|
||||||
|
/// Sets a closure to be called periodically during the lifecycle of the request as data is read from the server.
|
||||||
|
///
|
||||||
|
/// This closure returns the bytes most recently received from the server, not including data from previous calls.
|
||||||
|
/// If this closure is set, data will only be available within this closure, and will not be saved elsewhere. It is
|
||||||
|
/// also important to note that the server data in any `Response` object will be `nil`.
|
||||||
|
///
|
||||||
|
/// - parameter closure: The code to be executed periodically during the lifecycle of the request.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
open func stream(closure: ((Data) -> Void)? = nil) -> Self {
|
||||||
|
dataDelegate.dataStream = closure
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Progress
|
||||||
|
|
||||||
|
/// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
|
||||||
|
///
|
||||||
|
/// - parameter queue: The dispatch queue to execute the closure on.
|
||||||
|
/// - parameter closure: The code to be executed periodically as data is read from the server.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
|
||||||
|
dataDelegate.progressHandler = (closure, queue)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Specific type of `Request` that manages an underlying `URLSessionDownloadTask`.
|
||||||
|
open class DownloadRequest: Request {
|
||||||
|
|
||||||
|
// MARK: Helper Types
|
||||||
|
|
||||||
|
/// A collection of options to be executed prior to moving a downloaded file from the temporary URL to the
|
||||||
|
/// destination URL.
|
||||||
|
public struct DownloadOptions: OptionSet {
|
||||||
|
/// Returns the raw bitmask value of the option and satisfies the `RawRepresentable` protocol.
|
||||||
|
public let rawValue: UInt
|
||||||
|
|
||||||
|
/// A `DownloadOptions` flag that creates intermediate directories for the destination URL if specified.
|
||||||
|
public static let createIntermediateDirectories = DownloadOptions(rawValue: 1 << 0)
|
||||||
|
|
||||||
|
/// A `DownloadOptions` flag that removes a previous file from the destination URL if specified.
|
||||||
|
public static let removePreviousFile = DownloadOptions(rawValue: 1 << 1)
|
||||||
|
|
||||||
|
/// Creates a `DownloadFileDestinationOptions` instance with the specified raw value.
|
||||||
|
///
|
||||||
|
/// - parameter rawValue: The raw bitmask value for the option.
|
||||||
|
///
|
||||||
|
/// - returns: A new log level instance.
|
||||||
|
public init(rawValue: UInt) {
|
||||||
|
self.rawValue = rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A closure executed once a download request has successfully completed in order to determine where to move the
|
||||||
|
/// temporary file written to during the download process. The closure takes two arguments: the temporary file URL
|
||||||
|
/// and the URL response, and returns a two arguments: the file URL where the temporary file should be moved and
|
||||||
|
/// the options defining how the file should be moved.
|
||||||
|
public typealias DownloadFileDestination = (
|
||||||
|
_ temporaryURL: URL,
|
||||||
|
_ response: HTTPURLResponse)
|
||||||
|
-> (destinationURL: URL, options: DownloadOptions)
|
||||||
|
|
||||||
|
enum Downloadable: TaskConvertible {
|
||||||
|
case request(URLRequest)
|
||||||
|
case resumeData(Data)
|
||||||
|
|
||||||
|
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||||
|
do {
|
||||||
|
let task: URLSessionTask
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case let .request(urlRequest):
|
||||||
|
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||||
|
task = queue.sync { session.downloadTask(with: urlRequest) }
|
||||||
|
case let .resumeData(resumeData):
|
||||||
|
task = queue.sync { session.downloadTask(withResumeData: resumeData) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return task
|
||||||
|
} catch {
|
||||||
|
throw AdaptError(error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The request sent or to be sent to the server.
|
||||||
|
open override var request: URLRequest? {
|
||||||
|
if let request = super.request { return request }
|
||||||
|
|
||||||
|
if let downloadable = originalTask as? Downloadable, case let .request(urlRequest) = downloadable {
|
||||||
|
return urlRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The resume data of the underlying download task if available after a failure.
|
||||||
|
open var resumeData: Data? { return downloadDelegate.resumeData }
|
||||||
|
|
||||||
|
/// The progress of downloading the response data from the server for the request.
|
||||||
|
open var progress: Progress { return downloadDelegate.progress }
|
||||||
|
|
||||||
|
var downloadDelegate: DownloadTaskDelegate { return delegate as! DownloadTaskDelegate }
|
||||||
|
|
||||||
|
// MARK: State
|
||||||
|
|
||||||
|
/// Cancels the request.
|
||||||
|
override open func cancel() {
|
||||||
|
cancel(createResumeData: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancels the request.
|
||||||
|
///
|
||||||
|
/// - parameter createResumeData: Determines whether resume data is created via the underlying download task or not.
|
||||||
|
open func cancel(createResumeData: Bool) {
|
||||||
|
if createResumeData {
|
||||||
|
downloadDelegate.downloadTask.cancel { self.downloadDelegate.resumeData = $0 }
|
||||||
|
} else {
|
||||||
|
downloadDelegate.downloadTask.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: Notification.Name.Task.DidCancel,
|
||||||
|
object: self,
|
||||||
|
userInfo: [Notification.Key.Task: task as Any]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Progress
|
||||||
|
|
||||||
|
/// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
|
||||||
|
///
|
||||||
|
/// - parameter queue: The dispatch queue to execute the closure on.
|
||||||
|
/// - parameter closure: The code to be executed periodically as data is read from the server.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
|
||||||
|
downloadDelegate.progressHandler = (closure, queue)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Destination
|
||||||
|
|
||||||
|
/// Creates a download file destination closure which uses the default file manager to move the temporary file to a
|
||||||
|
/// file URL in the first available directory with the specified search path directory and search path domain mask.
|
||||||
|
///
|
||||||
|
/// - parameter directory: The search path directory. `.DocumentDirectory` by default.
|
||||||
|
/// - parameter domain: The search path domain mask. `.UserDomainMask` by default.
|
||||||
|
///
|
||||||
|
/// - returns: A download file destination closure.
|
||||||
|
open class func suggestedDownloadDestination(
|
||||||
|
for directory: FileManager.SearchPathDirectory = .documentDirectory,
|
||||||
|
in domain: FileManager.SearchPathDomainMask = .userDomainMask)
|
||||||
|
-> DownloadFileDestination {
|
||||||
|
return { temporaryURL, response in
|
||||||
|
let directoryURLs = FileManager.default.urls(for: directory, in: domain)
|
||||||
|
|
||||||
|
if !directoryURLs.isEmpty {
|
||||||
|
return (directoryURLs[0].appendingPathComponent(response.suggestedFilename!), [])
|
||||||
|
}
|
||||||
|
|
||||||
|
return (temporaryURL, [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Specific type of `Request` that manages an underlying `URLSessionUploadTask`.
|
||||||
|
open class UploadRequest: DataRequest {
|
||||||
|
|
||||||
|
// MARK: Helper Types
|
||||||
|
|
||||||
|
enum Uploadable: TaskConvertible {
|
||||||
|
case data(Data, URLRequest)
|
||||||
|
case file(URL, URLRequest)
|
||||||
|
case stream(InputStream, URLRequest)
|
||||||
|
|
||||||
|
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||||
|
do {
|
||||||
|
let task: URLSessionTask
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case let .data(data, urlRequest):
|
||||||
|
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||||
|
task = queue.sync { session.uploadTask(with: urlRequest, from: data) }
|
||||||
|
case let .file(url, urlRequest):
|
||||||
|
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||||
|
task = queue.sync { session.uploadTask(with: urlRequest, fromFile: url) }
|
||||||
|
case let .stream(_, urlRequest):
|
||||||
|
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||||
|
task = queue.sync { session.uploadTask(withStreamedRequest: urlRequest) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return task
|
||||||
|
} catch {
|
||||||
|
throw AdaptError(error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The request sent or to be sent to the server.
|
||||||
|
open override var request: URLRequest? {
|
||||||
|
if let request = super.request { return request }
|
||||||
|
|
||||||
|
guard let uploadable = originalTask as? Uploadable else { return nil }
|
||||||
|
|
||||||
|
switch uploadable {
|
||||||
|
case .data(_, let urlRequest), .file(_, let urlRequest), .stream(_, let urlRequest):
|
||||||
|
return urlRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The progress of uploading the payload to the server for the upload request.
|
||||||
|
open var uploadProgress: Progress { return uploadDelegate.uploadProgress }
|
||||||
|
|
||||||
|
var uploadDelegate: UploadTaskDelegate { return delegate as! UploadTaskDelegate }
|
||||||
|
|
||||||
|
// MARK: Upload Progress
|
||||||
|
|
||||||
|
/// Sets a closure to be called periodically during the lifecycle of the `UploadRequest` as data is sent to
|
||||||
|
/// the server.
|
||||||
|
///
|
||||||
|
/// After the data is sent to the server, the `progress(queue:closure:)` APIs can be used to monitor the progress
|
||||||
|
/// of data being read from the server.
|
||||||
|
///
|
||||||
|
/// - parameter queue: The dispatch queue to execute the closure on.
|
||||||
|
/// - parameter closure: The code to be executed periodically as data is sent to the server.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
open func uploadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
|
||||||
|
uploadDelegate.uploadProgressHandler = (closure, queue)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
#if !os(watchOS)
|
||||||
|
|
||||||
|
/// Specific type of `Request` that manages an underlying `URLSessionStreamTask`.
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
open class StreamRequest: Request {
|
||||||
|
enum Streamable: TaskConvertible {
|
||||||
|
case stream(hostName: String, port: Int)
|
||||||
|
case netService(NetService)
|
||||||
|
|
||||||
|
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||||
|
let task: URLSessionTask
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case let .stream(hostName, port):
|
||||||
|
task = queue.sync { session.streamTask(withHostName: hostName, port: port) }
|
||||||
|
case let .netService(netService):
|
||||||
|
task = queue.sync { session.streamTask(with: netService) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
563
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/Response.swift
generated
Normal file
563
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/Response.swift
generated
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
//
|
||||||
|
// Response.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Used to store all data associated with an non-serialized response of a data or upload request.
|
||||||
|
public struct DefaultDataResponse {
|
||||||
|
/// The URL request sent to the server.
|
||||||
|
public let request: URLRequest?
|
||||||
|
|
||||||
|
/// The server's response to the URL request.
|
||||||
|
public let response: HTTPURLResponse?
|
||||||
|
|
||||||
|
/// The data returned by the server.
|
||||||
|
public let data: Data?
|
||||||
|
|
||||||
|
/// The error encountered while executing or validating the request.
|
||||||
|
public let error: Error?
|
||||||
|
|
||||||
|
/// The timeline of the complete lifecycle of the request.
|
||||||
|
public let timeline: Timeline
|
||||||
|
|
||||||
|
var _metrics: AnyObject?
|
||||||
|
|
||||||
|
/// Creates a `DefaultDataResponse` instance from the specified parameters.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - request: The URL request sent to the server.
|
||||||
|
/// - response: The server's response to the URL request.
|
||||||
|
/// - data: The data returned by the server.
|
||||||
|
/// - error: The error encountered while executing or validating the request.
|
||||||
|
/// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
|
||||||
|
/// - metrics: The task metrics containing the request / response statistics. `nil` by default.
|
||||||
|
public init(
|
||||||
|
request: URLRequest?,
|
||||||
|
response: HTTPURLResponse?,
|
||||||
|
data: Data?,
|
||||||
|
error: Error?,
|
||||||
|
timeline: Timeline = Timeline(),
|
||||||
|
metrics: AnyObject? = nil) {
|
||||||
|
self.request = request
|
||||||
|
self.response = response
|
||||||
|
self.data = data
|
||||||
|
self.error = error
|
||||||
|
self.timeline = timeline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Used to store all data associated with a serialized response of a data or upload request.
|
||||||
|
public struct DataResponse<Value> {
|
||||||
|
/// The URL request sent to the server.
|
||||||
|
public let request: URLRequest?
|
||||||
|
|
||||||
|
/// The server's response to the URL request.
|
||||||
|
public let response: HTTPURLResponse?
|
||||||
|
|
||||||
|
/// The data returned by the server.
|
||||||
|
public let data: Data?
|
||||||
|
|
||||||
|
/// The result of response serialization.
|
||||||
|
public let result: Result<Value>
|
||||||
|
|
||||||
|
/// The timeline of the complete lifecycle of the request.
|
||||||
|
public let timeline: Timeline
|
||||||
|
|
||||||
|
/// Returns the associated value of the result if it is a success, `nil` otherwise.
|
||||||
|
public var value: Value? { return result.value }
|
||||||
|
|
||||||
|
/// Returns the associated error value if the result if it is a failure, `nil` otherwise.
|
||||||
|
public var error: Error? { return result.error }
|
||||||
|
|
||||||
|
var _metrics: AnyObject?
|
||||||
|
|
||||||
|
/// Creates a `DataResponse` instance with the specified parameters derived from response serialization.
|
||||||
|
///
|
||||||
|
/// - parameter request: The URL request sent to the server.
|
||||||
|
/// - parameter response: The server's response to the URL request.
|
||||||
|
/// - parameter data: The data returned by the server.
|
||||||
|
/// - parameter result: The result of response serialization.
|
||||||
|
/// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
|
||||||
|
///
|
||||||
|
/// - returns: The new `DataResponse` instance.
|
||||||
|
public init(
|
||||||
|
request: URLRequest?,
|
||||||
|
response: HTTPURLResponse?,
|
||||||
|
data: Data?,
|
||||||
|
result: Result<Value>,
|
||||||
|
timeline: Timeline = Timeline()) {
|
||||||
|
self.request = request
|
||||||
|
self.response = response
|
||||||
|
self.data = data
|
||||||
|
self.result = result
|
||||||
|
self.timeline = timeline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible {
|
||||||
|
/// The textual representation used when written to an output stream, which includes whether the result was a
|
||||||
|
/// success or failure.
|
||||||
|
public var description: String {
|
||||||
|
return result.debugDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The debug textual representation used when written to an output stream, which includes the URL request, the URL
|
||||||
|
/// response, the server data, the response serialization result and the timeline.
|
||||||
|
public var debugDescription: String {
|
||||||
|
var output: [String] = []
|
||||||
|
|
||||||
|
output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil")
|
||||||
|
output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")
|
||||||
|
output.append("[Data]: \(data?.count ?? 0) bytes")
|
||||||
|
output.append("[Result]: \(result.debugDescription)")
|
||||||
|
output.append("[Timeline]: \(timeline.debugDescription)")
|
||||||
|
|
||||||
|
return output.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
extension DataResponse {
|
||||||
|
/// Evaluates the specified closure when the result of this `DataResponse` is a success, passing the unwrapped
|
||||||
|
/// result value as a parameter.
|
||||||
|
///
|
||||||
|
/// Use the `map` method with a closure that does not throw. For example:
|
||||||
|
///
|
||||||
|
/// let possibleData: DataResponse<Data> = ...
|
||||||
|
/// let possibleInt = possibleData.map { $0.count }
|
||||||
|
///
|
||||||
|
/// - parameter transform: A closure that takes the success value of the instance's result.
|
||||||
|
///
|
||||||
|
/// - returns: A `DataResponse` whose result wraps the value returned by the given closure. If this instance's
|
||||||
|
/// result is a failure, returns a response wrapping the same failure.
|
||||||
|
public func map<T>(_ transform: (Value) -> T) -> DataResponse<T> {
|
||||||
|
var response = DataResponse<T>(
|
||||||
|
request: request,
|
||||||
|
response: self.response,
|
||||||
|
data: data,
|
||||||
|
result: result.map(transform),
|
||||||
|
timeline: timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
response._metrics = _metrics
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result
|
||||||
|
/// value as a parameter.
|
||||||
|
///
|
||||||
|
/// Use the `flatMap` method with a closure that may throw an error. For example:
|
||||||
|
///
|
||||||
|
/// let possibleData: DataResponse<Data> = ...
|
||||||
|
/// let possibleObject = possibleData.flatMap {
|
||||||
|
/// try JSONSerialization.jsonObject(with: $0)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// - parameter transform: A closure that takes the success value of the instance's result.
|
||||||
|
///
|
||||||
|
/// - returns: A success or failure `DataResponse` depending on the result of the given closure. If this instance's
|
||||||
|
/// result is a failure, returns the same failure.
|
||||||
|
public func flatMap<T>(_ transform: (Value) throws -> T) -> DataResponse<T> {
|
||||||
|
var response = DataResponse<T>(
|
||||||
|
request: request,
|
||||||
|
response: self.response,
|
||||||
|
data: data,
|
||||||
|
result: result.flatMap(transform),
|
||||||
|
timeline: timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
response._metrics = _metrics
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter.
|
||||||
|
///
|
||||||
|
/// Use the `mapError` function with a closure that does not throw. For example:
|
||||||
|
///
|
||||||
|
/// let possibleData: DataResponse<Data> = ...
|
||||||
|
/// let withMyError = possibleData.mapError { MyError.error($0) }
|
||||||
|
///
|
||||||
|
/// - Parameter transform: A closure that takes the error of the instance.
|
||||||
|
/// - Returns: A `DataResponse` instance containing the result of the transform.
|
||||||
|
public func mapError<E: Error>(_ transform: (Error) -> E) -> DataResponse {
|
||||||
|
var response = DataResponse(
|
||||||
|
request: request,
|
||||||
|
response: self.response,
|
||||||
|
data: data,
|
||||||
|
result: result.mapError(transform),
|
||||||
|
timeline: timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
response._metrics = _metrics
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter.
|
||||||
|
///
|
||||||
|
/// Use the `flatMapError` function with a closure that may throw an error. For example:
|
||||||
|
///
|
||||||
|
/// let possibleData: DataResponse<Data> = ...
|
||||||
|
/// let possibleObject = possibleData.flatMapError {
|
||||||
|
/// try someFailableFunction(taking: $0)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// - Parameter transform: A throwing closure that takes the error of the instance.
|
||||||
|
///
|
||||||
|
/// - Returns: A `DataResponse` instance containing the result of the transform.
|
||||||
|
public func flatMapError<E: Error>(_ transform: (Error) throws -> E) -> DataResponse {
|
||||||
|
var response = DataResponse(
|
||||||
|
request: request,
|
||||||
|
response: self.response,
|
||||||
|
data: data,
|
||||||
|
result: result.flatMapError(transform),
|
||||||
|
timeline: timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
response._metrics = _metrics
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Used to store all data associated with an non-serialized response of a download request.
|
||||||
|
public struct DefaultDownloadResponse {
|
||||||
|
/// The URL request sent to the server.
|
||||||
|
public let request: URLRequest?
|
||||||
|
|
||||||
|
/// The server's response to the URL request.
|
||||||
|
public let response: HTTPURLResponse?
|
||||||
|
|
||||||
|
/// The temporary destination URL of the data returned from the server.
|
||||||
|
public let temporaryURL: URL?
|
||||||
|
|
||||||
|
/// The final destination URL of the data returned from the server if it was moved.
|
||||||
|
public let destinationURL: URL?
|
||||||
|
|
||||||
|
/// The resume data generated if the request was cancelled.
|
||||||
|
public let resumeData: Data?
|
||||||
|
|
||||||
|
/// The error encountered while executing or validating the request.
|
||||||
|
public let error: Error?
|
||||||
|
|
||||||
|
/// The timeline of the complete lifecycle of the request.
|
||||||
|
public let timeline: Timeline
|
||||||
|
|
||||||
|
var _metrics: AnyObject?
|
||||||
|
|
||||||
|
/// Creates a `DefaultDownloadResponse` instance from the specified parameters.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - request: The URL request sent to the server.
|
||||||
|
/// - response: The server's response to the URL request.
|
||||||
|
/// - temporaryURL: The temporary destination URL of the data returned from the server.
|
||||||
|
/// - destinationURL: The final destination URL of the data returned from the server if it was moved.
|
||||||
|
/// - resumeData: The resume data generated if the request was cancelled.
|
||||||
|
/// - error: The error encountered while executing or validating the request.
|
||||||
|
/// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
|
||||||
|
/// - metrics: The task metrics containing the request / response statistics. `nil` by default.
|
||||||
|
public init(
|
||||||
|
request: URLRequest?,
|
||||||
|
response: HTTPURLResponse?,
|
||||||
|
temporaryURL: URL?,
|
||||||
|
destinationURL: URL?,
|
||||||
|
resumeData: Data?,
|
||||||
|
error: Error?,
|
||||||
|
timeline: Timeline = Timeline(),
|
||||||
|
metrics: AnyObject? = nil) {
|
||||||
|
self.request = request
|
||||||
|
self.response = response
|
||||||
|
self.temporaryURL = temporaryURL
|
||||||
|
self.destinationURL = destinationURL
|
||||||
|
self.resumeData = resumeData
|
||||||
|
self.error = error
|
||||||
|
self.timeline = timeline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// Used to store all data associated with a serialized response of a download request.
|
||||||
|
public struct DownloadResponse<Value> {
|
||||||
|
/// The URL request sent to the server.
|
||||||
|
public let request: URLRequest?
|
||||||
|
|
||||||
|
/// The server's response to the URL request.
|
||||||
|
public let response: HTTPURLResponse?
|
||||||
|
|
||||||
|
/// The temporary destination URL of the data returned from the server.
|
||||||
|
public let temporaryURL: URL?
|
||||||
|
|
||||||
|
/// The final destination URL of the data returned from the server if it was moved.
|
||||||
|
public let destinationURL: URL?
|
||||||
|
|
||||||
|
/// The resume data generated if the request was cancelled.
|
||||||
|
public let resumeData: Data?
|
||||||
|
|
||||||
|
/// The result of response serialization.
|
||||||
|
public let result: Result<Value>
|
||||||
|
|
||||||
|
/// The timeline of the complete lifecycle of the request.
|
||||||
|
public let timeline: Timeline
|
||||||
|
|
||||||
|
/// Returns the associated value of the result if it is a success, `nil` otherwise.
|
||||||
|
public var value: Value? { return result.value }
|
||||||
|
|
||||||
|
/// Returns the associated error value if the result if it is a failure, `nil` otherwise.
|
||||||
|
public var error: Error? { return result.error }
|
||||||
|
|
||||||
|
var _metrics: AnyObject?
|
||||||
|
|
||||||
|
/// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization.
|
||||||
|
///
|
||||||
|
/// - parameter request: The URL request sent to the server.
|
||||||
|
/// - parameter response: The server's response to the URL request.
|
||||||
|
/// - parameter temporaryURL: The temporary destination URL of the data returned from the server.
|
||||||
|
/// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved.
|
||||||
|
/// - parameter resumeData: The resume data generated if the request was cancelled.
|
||||||
|
/// - parameter result: The result of response serialization.
|
||||||
|
/// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
|
||||||
|
///
|
||||||
|
/// - returns: The new `DownloadResponse` instance.
|
||||||
|
public init(
|
||||||
|
request: URLRequest?,
|
||||||
|
response: HTTPURLResponse?,
|
||||||
|
temporaryURL: URL?,
|
||||||
|
destinationURL: URL?,
|
||||||
|
resumeData: Data?,
|
||||||
|
result: Result<Value>,
|
||||||
|
timeline: Timeline = Timeline()) {
|
||||||
|
self.request = request
|
||||||
|
self.response = response
|
||||||
|
self.temporaryURL = temporaryURL
|
||||||
|
self.destinationURL = destinationURL
|
||||||
|
self.resumeData = resumeData
|
||||||
|
self.result = result
|
||||||
|
self.timeline = timeline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible {
|
||||||
|
/// The textual representation used when written to an output stream, which includes whether the result was a
|
||||||
|
/// success or failure.
|
||||||
|
public var description: String {
|
||||||
|
return result.debugDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The debug textual representation used when written to an output stream, which includes the URL request, the URL
|
||||||
|
/// response, the temporary and destination URLs, the resume data, the response serialization result and the
|
||||||
|
/// timeline.
|
||||||
|
public var debugDescription: String {
|
||||||
|
var output: [String] = []
|
||||||
|
|
||||||
|
output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil")
|
||||||
|
output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")
|
||||||
|
output.append("[TemporaryURL]: \(temporaryURL?.path ?? "nil")")
|
||||||
|
output.append("[DestinationURL]: \(destinationURL?.path ?? "nil")")
|
||||||
|
output.append("[ResumeData]: \(resumeData?.count ?? 0) bytes")
|
||||||
|
output.append("[Result]: \(result.debugDescription)")
|
||||||
|
output.append("[Timeline]: \(timeline.debugDescription)")
|
||||||
|
|
||||||
|
return output.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
extension DownloadResponse {
|
||||||
|
/// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped
|
||||||
|
/// result value as a parameter.
|
||||||
|
///
|
||||||
|
/// Use the `map` method with a closure that does not throw. For example:
|
||||||
|
///
|
||||||
|
/// let possibleData: DownloadResponse<Data> = ...
|
||||||
|
/// let possibleInt = possibleData.map { $0.count }
|
||||||
|
///
|
||||||
|
/// - parameter transform: A closure that takes the success value of the instance's result.
|
||||||
|
///
|
||||||
|
/// - returns: A `DownloadResponse` whose result wraps the value returned by the given closure. If this instance's
|
||||||
|
/// result is a failure, returns a response wrapping the same failure.
|
||||||
|
public func map<T>(_ transform: (Value) -> T) -> DownloadResponse<T> {
|
||||||
|
var response = DownloadResponse<T>(
|
||||||
|
request: request,
|
||||||
|
response: self.response,
|
||||||
|
temporaryURL: temporaryURL,
|
||||||
|
destinationURL: destinationURL,
|
||||||
|
resumeData: resumeData,
|
||||||
|
result: result.map(transform),
|
||||||
|
timeline: timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
response._metrics = _metrics
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped
|
||||||
|
/// result value as a parameter.
|
||||||
|
///
|
||||||
|
/// Use the `flatMap` method with a closure that may throw an error. For example:
|
||||||
|
///
|
||||||
|
/// let possibleData: DownloadResponse<Data> = ...
|
||||||
|
/// let possibleObject = possibleData.flatMap {
|
||||||
|
/// try JSONSerialization.jsonObject(with: $0)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// - parameter transform: A closure that takes the success value of the instance's result.
|
||||||
|
///
|
||||||
|
/// - returns: A success or failure `DownloadResponse` depending on the result of the given closure. If this
|
||||||
|
/// instance's result is a failure, returns the same failure.
|
||||||
|
public func flatMap<T>(_ transform: (Value) throws -> T) -> DownloadResponse<T> {
|
||||||
|
var response = DownloadResponse<T>(
|
||||||
|
request: request,
|
||||||
|
response: self.response,
|
||||||
|
temporaryURL: temporaryURL,
|
||||||
|
destinationURL: destinationURL,
|
||||||
|
resumeData: resumeData,
|
||||||
|
result: result.flatMap(transform),
|
||||||
|
timeline: timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
response._metrics = _metrics
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter.
|
||||||
|
///
|
||||||
|
/// Use the `mapError` function with a closure that does not throw. For example:
|
||||||
|
///
|
||||||
|
/// let possibleData: DownloadResponse<Data> = ...
|
||||||
|
/// let withMyError = possibleData.mapError { MyError.error($0) }
|
||||||
|
///
|
||||||
|
/// - Parameter transform: A closure that takes the error of the instance.
|
||||||
|
/// - Returns: A `DownloadResponse` instance containing the result of the transform.
|
||||||
|
public func mapError<E: Error>(_ transform: (Error) -> E) -> DownloadResponse {
|
||||||
|
var response = DownloadResponse(
|
||||||
|
request: request,
|
||||||
|
response: self.response,
|
||||||
|
temporaryURL: temporaryURL,
|
||||||
|
destinationURL: destinationURL,
|
||||||
|
resumeData: resumeData,
|
||||||
|
result: result.mapError(transform),
|
||||||
|
timeline: timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
response._metrics = _metrics
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter.
|
||||||
|
///
|
||||||
|
/// Use the `flatMapError` function with a closure that may throw an error. For example:
|
||||||
|
///
|
||||||
|
/// let possibleData: DownloadResponse<Data> = ...
|
||||||
|
/// let possibleObject = possibleData.flatMapError {
|
||||||
|
/// try someFailableFunction(taking: $0)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// - Parameter transform: A throwing closure that takes the error of the instance.
|
||||||
|
///
|
||||||
|
/// - Returns: A `DownloadResponse` instance containing the result of the transform.
|
||||||
|
public func flatMapError<E: Error>(_ transform: (Error) throws -> E) -> DownloadResponse {
|
||||||
|
var response = DownloadResponse(
|
||||||
|
request: request,
|
||||||
|
response: self.response,
|
||||||
|
temporaryURL: temporaryURL,
|
||||||
|
destinationURL: destinationURL,
|
||||||
|
resumeData: resumeData,
|
||||||
|
result: result.flatMapError(transform),
|
||||||
|
timeline: timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
response._metrics = _metrics
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
protocol Response {
|
||||||
|
/// The task metrics containing the request / response statistics.
|
||||||
|
var _metrics: AnyObject? { get set }
|
||||||
|
mutating func add(_ metrics: AnyObject?)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Response {
|
||||||
|
mutating func add(_ metrics: AnyObject?) {
|
||||||
|
#if !os(watchOS)
|
||||||
|
guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return }
|
||||||
|
guard let metrics = metrics as? URLSessionTaskMetrics else { return }
|
||||||
|
|
||||||
|
_metrics = metrics
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
|
||||||
|
extension DefaultDataResponse: Response {
|
||||||
|
#if !os(watchOS)
|
||||||
|
/// The task metrics containing the request / response statistics.
|
||||||
|
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
|
||||||
|
extension DataResponse: Response {
|
||||||
|
#if !os(watchOS)
|
||||||
|
/// The task metrics containing the request / response statistics.
|
||||||
|
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
|
||||||
|
extension DefaultDownloadResponse: Response {
|
||||||
|
#if !os(watchOS)
|
||||||
|
/// The task metrics containing the request / response statistics.
|
||||||
|
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
|
||||||
|
extension DownloadResponse: Response {
|
||||||
|
#if !os(watchOS)
|
||||||
|
/// The task metrics containing the request / response statistics.
|
||||||
|
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -0,0 +1,697 @@
|
|||||||
|
//
|
||||||
|
// ResponseSerialization.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// The type in which all data response serializers must conform to in order to serialize a response.
|
||||||
|
public protocol DataResponseSerializerProtocol {
|
||||||
|
/// The type of serialized object to be created by this `DataResponseSerializerType`.
|
||||||
|
associatedtype SerializedObject
|
||||||
|
|
||||||
|
/// A closure used by response handlers that takes a request, response, data and error and returns a result.
|
||||||
|
var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<SerializedObject> { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// A generic `DataResponseSerializerType` used to serialize a request, response, and data into a serialized object.
|
||||||
|
public struct DataResponseSerializer<Value>: DataResponseSerializerProtocol {
|
||||||
|
/// The type of serialized object to be created by this `DataResponseSerializer`.
|
||||||
|
public typealias SerializedObject = Value
|
||||||
|
|
||||||
|
/// A closure used by response handlers that takes a request, response, data and error and returns a result.
|
||||||
|
public var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>
|
||||||
|
|
||||||
|
/// Initializes the `ResponseSerializer` instance with the given serialize response closure.
|
||||||
|
///
|
||||||
|
/// - parameter serializeResponse: The closure used to serialize the response.
|
||||||
|
///
|
||||||
|
/// - returns: The new generic response serializer instance.
|
||||||
|
public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>) {
|
||||||
|
self.serializeResponse = serializeResponse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// The type in which all download response serializers must conform to in order to serialize a response.
|
||||||
|
public protocol DownloadResponseSerializerProtocol {
|
||||||
|
/// The type of serialized object to be created by this `DownloadResponseSerializerType`.
|
||||||
|
associatedtype SerializedObject
|
||||||
|
|
||||||
|
/// A closure used by response handlers that takes a request, response, url and error and returns a result.
|
||||||
|
var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<SerializedObject> { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
/// A generic `DownloadResponseSerializerType` used to serialize a request, response, and data into a serialized object.
|
||||||
|
public struct DownloadResponseSerializer<Value>: DownloadResponseSerializerProtocol {
|
||||||
|
/// The type of serialized object to be created by this `DownloadResponseSerializer`.
|
||||||
|
public typealias SerializedObject = Value
|
||||||
|
|
||||||
|
/// A closure used by response handlers that takes a request, response, url and error and returns a result.
|
||||||
|
public var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>
|
||||||
|
|
||||||
|
/// Initializes the `ResponseSerializer` instance with the given serialize response closure.
|
||||||
|
///
|
||||||
|
/// - parameter serializeResponse: The closure used to serialize the response.
|
||||||
|
///
|
||||||
|
/// - returns: The new generic response serializer instance.
|
||||||
|
public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>) {
|
||||||
|
self.serializeResponse = serializeResponse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Timeline
|
||||||
|
|
||||||
|
extension Request {
|
||||||
|
var timeline: Timeline {
|
||||||
|
let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent()
|
||||||
|
let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
|
||||||
|
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
|
||||||
|
|
||||||
|
return Timeline(
|
||||||
|
requestStartTime: requestStartTime,
|
||||||
|
initialResponseTime: initialResponseTime,
|
||||||
|
requestCompletedTime: requestCompletedTime,
|
||||||
|
serializationCompletedTime: CFAbsoluteTimeGetCurrent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Default
|
||||||
|
|
||||||
|
extension DataRequest {
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter queue: The queue on which the completion handler is dispatched.
|
||||||
|
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self {
|
||||||
|
delegate.queue.addOperation {
|
||||||
|
(queue ?? DispatchQueue.main).async {
|
||||||
|
var dataResponse = DefaultDataResponse(
|
||||||
|
request: self.request,
|
||||||
|
response: self.response,
|
||||||
|
data: self.delegate.data,
|
||||||
|
error: self.delegate.error,
|
||||||
|
timeline: self.timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
dataResponse.add(self.delegate.metrics)
|
||||||
|
|
||||||
|
completionHandler(dataResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter queue: The queue on which the completion handler is dispatched.
|
||||||
|
/// - parameter responseSerializer: The response serializer responsible for serializing the request, response,
|
||||||
|
/// and data.
|
||||||
|
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func response<T: DataResponseSerializerProtocol>(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
responseSerializer: T,
|
||||||
|
completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
|
||||||
|
-> Self {
|
||||||
|
delegate.queue.addOperation {
|
||||||
|
let result = responseSerializer.serializeResponse(
|
||||||
|
self.request,
|
||||||
|
self.response,
|
||||||
|
self.delegate.data,
|
||||||
|
self.delegate.error
|
||||||
|
)
|
||||||
|
|
||||||
|
var dataResponse = DataResponse<T.SerializedObject>(
|
||||||
|
request: self.request,
|
||||||
|
response: self.response,
|
||||||
|
data: self.delegate.data,
|
||||||
|
result: result,
|
||||||
|
timeline: self.timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
dataResponse.add(self.delegate.metrics)
|
||||||
|
|
||||||
|
(queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DownloadRequest {
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter queue: The queue on which the completion handler is dispatched.
|
||||||
|
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func response(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
completionHandler: @escaping (DefaultDownloadResponse) -> Void)
|
||||||
|
-> Self {
|
||||||
|
delegate.queue.addOperation {
|
||||||
|
(queue ?? DispatchQueue.main).async {
|
||||||
|
var downloadResponse = DefaultDownloadResponse(
|
||||||
|
request: self.request,
|
||||||
|
response: self.response,
|
||||||
|
temporaryURL: self.downloadDelegate.temporaryURL,
|
||||||
|
destinationURL: self.downloadDelegate.destinationURL,
|
||||||
|
resumeData: self.downloadDelegate.resumeData,
|
||||||
|
error: self.downloadDelegate.error,
|
||||||
|
timeline: self.timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
downloadResponse.add(self.delegate.metrics)
|
||||||
|
|
||||||
|
completionHandler(downloadResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter queue: The queue on which the completion handler is dispatched.
|
||||||
|
/// - parameter responseSerializer: The response serializer responsible for serializing the request, response,
|
||||||
|
/// and data contained in the destination url.
|
||||||
|
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func response<T: DownloadResponseSerializerProtocol>(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
responseSerializer: T,
|
||||||
|
completionHandler: @escaping (DownloadResponse<T.SerializedObject>) -> Void)
|
||||||
|
-> Self {
|
||||||
|
delegate.queue.addOperation {
|
||||||
|
let result = responseSerializer.serializeResponse(
|
||||||
|
self.request,
|
||||||
|
self.response,
|
||||||
|
self.downloadDelegate.fileURL,
|
||||||
|
self.downloadDelegate.error
|
||||||
|
)
|
||||||
|
|
||||||
|
var downloadResponse = DownloadResponse<T.SerializedObject>(
|
||||||
|
request: self.request,
|
||||||
|
response: self.response,
|
||||||
|
temporaryURL: self.downloadDelegate.temporaryURL,
|
||||||
|
destinationURL: self.downloadDelegate.destinationURL,
|
||||||
|
resumeData: self.downloadDelegate.resumeData,
|
||||||
|
result: result,
|
||||||
|
timeline: self.timeline
|
||||||
|
)
|
||||||
|
|
||||||
|
downloadResponse.add(self.delegate.metrics)
|
||||||
|
|
||||||
|
(queue ?? DispatchQueue.main).async { completionHandler(downloadResponse) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Data
|
||||||
|
|
||||||
|
extension Request {
|
||||||
|
/// Returns a result data type that contains the response data as-is.
|
||||||
|
///
|
||||||
|
/// - parameter response: The response from the server.
|
||||||
|
/// - parameter data: The data returned from the server.
|
||||||
|
/// - parameter error: The error already encountered if it exists.
|
||||||
|
///
|
||||||
|
/// - returns: The result data type.
|
||||||
|
public static func serializeResponseData(response: HTTPURLResponse?, data: Data?, error: Error?) -> Result<Data> {
|
||||||
|
guard error == nil else { return .failure(error!) }
|
||||||
|
|
||||||
|
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(Data()) }
|
||||||
|
|
||||||
|
guard let validData = data else {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
|
||||||
|
}
|
||||||
|
|
||||||
|
return .success(validData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DataRequest {
|
||||||
|
/// Creates a response serializer that returns the associated data as-is.
|
||||||
|
///
|
||||||
|
/// - returns: A data response serializer.
|
||||||
|
public static func dataResponseSerializer() -> DataResponseSerializer<Data> {
|
||||||
|
return DataResponseSerializer { _, response, data, error in
|
||||||
|
return Request.serializeResponseData(response: response, data: data, error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func responseData(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
completionHandler: @escaping (DataResponse<Data>) -> Void)
|
||||||
|
-> Self {
|
||||||
|
return response(
|
||||||
|
queue: queue,
|
||||||
|
responseSerializer: DataRequest.dataResponseSerializer(),
|
||||||
|
completionHandler: completionHandler
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DownloadRequest {
|
||||||
|
/// Creates a response serializer that returns the associated data as-is.
|
||||||
|
///
|
||||||
|
/// - returns: A data response serializer.
|
||||||
|
public static func dataResponseSerializer() -> DownloadResponseSerializer<Data> {
|
||||||
|
return DownloadResponseSerializer { _, response, fileURL, error in
|
||||||
|
guard error == nil else { return .failure(error!) }
|
||||||
|
|
||||||
|
guard let fileURL = fileURL else {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let data = try Data(contentsOf: fileURL)
|
||||||
|
return Request.serializeResponseData(response: response, data: data, error: error)
|
||||||
|
} catch {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func responseData(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
completionHandler: @escaping (DownloadResponse<Data>) -> Void)
|
||||||
|
-> Self {
|
||||||
|
return response(
|
||||||
|
queue: queue,
|
||||||
|
responseSerializer: DownloadRequest.dataResponseSerializer(),
|
||||||
|
completionHandler: completionHandler
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - String
|
||||||
|
|
||||||
|
extension Request {
|
||||||
|
/// Returns a result string type initialized from the response data with the specified string encoding.
|
||||||
|
///
|
||||||
|
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
|
||||||
|
/// response, falling back to the default HTTP default character set, ISO-8859-1.
|
||||||
|
/// - parameter response: The response from the server.
|
||||||
|
/// - parameter data: The data returned from the server.
|
||||||
|
/// - parameter error: The error already encountered if it exists.
|
||||||
|
///
|
||||||
|
/// - returns: The result data type.
|
||||||
|
public static func serializeResponseString(
|
||||||
|
encoding: String.Encoding?,
|
||||||
|
response: HTTPURLResponse?,
|
||||||
|
data: Data?,
|
||||||
|
error: Error?)
|
||||||
|
-> Result<String> {
|
||||||
|
guard error == nil else { return .failure(error!) }
|
||||||
|
|
||||||
|
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success("") }
|
||||||
|
|
||||||
|
guard let validData = data else {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
|
||||||
|
}
|
||||||
|
|
||||||
|
var convertedEncoding = encoding
|
||||||
|
|
||||||
|
if let encodingName = response?.textEncodingName as CFString?, convertedEncoding == nil {
|
||||||
|
convertedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(
|
||||||
|
CFStringConvertIANACharSetNameToEncoding(encodingName))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let actualEncoding = convertedEncoding ?? .isoLatin1
|
||||||
|
|
||||||
|
if let string = String(data: validData, encoding: actualEncoding) {
|
||||||
|
return .success(string)
|
||||||
|
} else {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DataRequest {
|
||||||
|
/// Creates a response serializer that returns a result string type initialized from the response data with
|
||||||
|
/// the specified string encoding.
|
||||||
|
///
|
||||||
|
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
|
||||||
|
/// response, falling back to the default HTTP default character set, ISO-8859-1.
|
||||||
|
///
|
||||||
|
/// - returns: A string response serializer.
|
||||||
|
public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DataResponseSerializer<String> {
|
||||||
|
return DataResponseSerializer { _, response, data, error in
|
||||||
|
return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the
|
||||||
|
/// server response, falling back to the default HTTP default character set,
|
||||||
|
/// ISO-8859-1.
|
||||||
|
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func responseString(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
encoding: String.Encoding? = nil,
|
||||||
|
completionHandler: @escaping (DataResponse<String>) -> Void)
|
||||||
|
-> Self {
|
||||||
|
return response(
|
||||||
|
queue: queue,
|
||||||
|
responseSerializer: DataRequest.stringResponseSerializer(encoding: encoding),
|
||||||
|
completionHandler: completionHandler
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DownloadRequest {
|
||||||
|
/// Creates a response serializer that returns a result string type initialized from the response data with
|
||||||
|
/// the specified string encoding.
|
||||||
|
///
|
||||||
|
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
|
||||||
|
/// response, falling back to the default HTTP default character set, ISO-8859-1.
|
||||||
|
///
|
||||||
|
/// - returns: A string response serializer.
|
||||||
|
public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DownloadResponseSerializer<String> {
|
||||||
|
return DownloadResponseSerializer { _, response, fileURL, error in
|
||||||
|
guard error == nil else { return .failure(error!) }
|
||||||
|
|
||||||
|
guard let fileURL = fileURL else {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let data = try Data(contentsOf: fileURL)
|
||||||
|
return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error)
|
||||||
|
} catch {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the
|
||||||
|
/// server response, falling back to the default HTTP default character set,
|
||||||
|
/// ISO-8859-1.
|
||||||
|
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func responseString(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
encoding: String.Encoding? = nil,
|
||||||
|
completionHandler: @escaping (DownloadResponse<String>) -> Void)
|
||||||
|
-> Self {
|
||||||
|
return response(
|
||||||
|
queue: queue,
|
||||||
|
responseSerializer: DownloadRequest.stringResponseSerializer(encoding: encoding),
|
||||||
|
completionHandler: completionHandler
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - JSON
|
||||||
|
|
||||||
|
extension Request {
|
||||||
|
/// Returns a JSON object contained in a result type constructed from the response data using `JSONSerialization`
|
||||||
|
/// with the specified reading options.
|
||||||
|
///
|
||||||
|
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
|
||||||
|
/// - parameter response: The response from the server.
|
||||||
|
/// - parameter data: The data returned from the server.
|
||||||
|
/// - parameter error: The error already encountered if it exists.
|
||||||
|
///
|
||||||
|
/// - returns: The result data type.
|
||||||
|
public static func serializeResponseJSON(
|
||||||
|
options: JSONSerialization.ReadingOptions,
|
||||||
|
response: HTTPURLResponse?,
|
||||||
|
data: Data?,
|
||||||
|
error: Error?)
|
||||||
|
-> Result<Any> {
|
||||||
|
guard error == nil else { return .failure(error!) }
|
||||||
|
|
||||||
|
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
|
||||||
|
|
||||||
|
guard let validData = data, validData.count > 0 else {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let json = try JSONSerialization.jsonObject(with: validData, options: options)
|
||||||
|
return .success(json)
|
||||||
|
} catch {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DataRequest {
|
||||||
|
/// Creates a response serializer that returns a JSON object result type constructed from the response data using
|
||||||
|
/// `JSONSerialization` with the specified reading options.
|
||||||
|
///
|
||||||
|
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
|
||||||
|
///
|
||||||
|
/// - returns: A JSON object response serializer.
|
||||||
|
public static func jsonResponseSerializer(
|
||||||
|
options: JSONSerialization.ReadingOptions = .allowFragments)
|
||||||
|
-> DataResponseSerializer<Any> {
|
||||||
|
return DataResponseSerializer { _, response, data, error in
|
||||||
|
return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
|
||||||
|
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func responseJSON(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
options: JSONSerialization.ReadingOptions = .allowFragments,
|
||||||
|
completionHandler: @escaping (DataResponse<Any>) -> Void)
|
||||||
|
-> Self {
|
||||||
|
return response(
|
||||||
|
queue: queue,
|
||||||
|
responseSerializer: DataRequest.jsonResponseSerializer(options: options),
|
||||||
|
completionHandler: completionHandler
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DownloadRequest {
|
||||||
|
/// Creates a response serializer that returns a JSON object result type constructed from the response data using
|
||||||
|
/// `JSONSerialization` with the specified reading options.
|
||||||
|
///
|
||||||
|
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
|
||||||
|
///
|
||||||
|
/// - returns: A JSON object response serializer.
|
||||||
|
public static func jsonResponseSerializer(
|
||||||
|
options: JSONSerialization.ReadingOptions = .allowFragments)
|
||||||
|
-> DownloadResponseSerializer<Any> {
|
||||||
|
return DownloadResponseSerializer { _, response, fileURL, error in
|
||||||
|
guard error == nil else { return .failure(error!) }
|
||||||
|
|
||||||
|
guard let fileURL = fileURL else {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let data = try Data(contentsOf: fileURL)
|
||||||
|
return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
|
||||||
|
} catch {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
|
||||||
|
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func responseJSON(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
options: JSONSerialization.ReadingOptions = .allowFragments,
|
||||||
|
completionHandler: @escaping (DownloadResponse<Any>) -> Void)
|
||||||
|
-> Self {
|
||||||
|
return response(
|
||||||
|
queue: queue,
|
||||||
|
responseSerializer: DownloadRequest.jsonResponseSerializer(options: options),
|
||||||
|
completionHandler: completionHandler
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Property List
|
||||||
|
|
||||||
|
extension Request {
|
||||||
|
/// Returns a plist object contained in a result type constructed from the response data using
|
||||||
|
/// `PropertyListSerialization` with the specified reading options.
|
||||||
|
///
|
||||||
|
/// - parameter options: The property list reading options. Defaults to `[]`.
|
||||||
|
/// - parameter response: The response from the server.
|
||||||
|
/// - parameter data: The data returned from the server.
|
||||||
|
/// - parameter error: The error already encountered if it exists.
|
||||||
|
///
|
||||||
|
/// - returns: The result data type.
|
||||||
|
public static func serializeResponsePropertyList(
|
||||||
|
options: PropertyListSerialization.ReadOptions,
|
||||||
|
response: HTTPURLResponse?,
|
||||||
|
data: Data?,
|
||||||
|
error: Error?)
|
||||||
|
-> Result<Any> {
|
||||||
|
guard error == nil else { return .failure(error!) }
|
||||||
|
|
||||||
|
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
|
||||||
|
|
||||||
|
guard let validData = data, validData.count > 0 else {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let plist = try PropertyListSerialization.propertyList(from: validData, options: options, format: nil)
|
||||||
|
return .success(plist)
|
||||||
|
} catch {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .propertyListSerializationFailed(error: error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DataRequest {
|
||||||
|
/// Creates a response serializer that returns an object constructed from the response data using
|
||||||
|
/// `PropertyListSerialization` with the specified reading options.
|
||||||
|
///
|
||||||
|
/// - parameter options: The property list reading options. Defaults to `[]`.
|
||||||
|
///
|
||||||
|
/// - returns: A property list object response serializer.
|
||||||
|
public static func propertyListResponseSerializer(
|
||||||
|
options: PropertyListSerialization.ReadOptions = [])
|
||||||
|
-> DataResponseSerializer<Any> {
|
||||||
|
return DataResponseSerializer { _, response, data, error in
|
||||||
|
return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter options: The property list reading options. Defaults to `[]`.
|
||||||
|
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func responsePropertyList(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
options: PropertyListSerialization.ReadOptions = [],
|
||||||
|
completionHandler: @escaping (DataResponse<Any>) -> Void)
|
||||||
|
-> Self {
|
||||||
|
return response(
|
||||||
|
queue: queue,
|
||||||
|
responseSerializer: DataRequest.propertyListResponseSerializer(options: options),
|
||||||
|
completionHandler: completionHandler
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DownloadRequest {
|
||||||
|
/// Creates a response serializer that returns an object constructed from the response data using
|
||||||
|
/// `PropertyListSerialization` with the specified reading options.
|
||||||
|
///
|
||||||
|
/// - parameter options: The property list reading options. Defaults to `[]`.
|
||||||
|
///
|
||||||
|
/// - returns: A property list object response serializer.
|
||||||
|
public static func propertyListResponseSerializer(
|
||||||
|
options: PropertyListSerialization.ReadOptions = [])
|
||||||
|
-> DownloadResponseSerializer<Any> {
|
||||||
|
return DownloadResponseSerializer { _, response, fileURL, error in
|
||||||
|
guard error == nil else { return .failure(error!) }
|
||||||
|
|
||||||
|
guard let fileURL = fileURL else {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let data = try Data(contentsOf: fileURL)
|
||||||
|
return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error)
|
||||||
|
} catch {
|
||||||
|
return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a handler to be called once the request has finished.
|
||||||
|
///
|
||||||
|
/// - parameter options: The property list reading options. Defaults to `[]`.
|
||||||
|
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func responsePropertyList(
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
options: PropertyListSerialization.ReadOptions = [],
|
||||||
|
completionHandler: @escaping (DownloadResponse<Any>) -> Void)
|
||||||
|
-> Self {
|
||||||
|
return response(
|
||||||
|
queue: queue,
|
||||||
|
responseSerializer: DownloadRequest.propertyListResponseSerializer(options: options),
|
||||||
|
completionHandler: completionHandler
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A set of HTTP response status code that do not contain response data.
|
||||||
|
private let emptyDataStatusCodes: Set<Int> = [204, 205]
|
||||||
306
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/ServerTrustPolicy.swift
generated
Normal file
306
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/ServerTrustPolicy.swift
generated
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
//
|
||||||
|
// ServerTrustPolicy.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Responsible for managing the mapping of `ServerTrustPolicy` objects to a given host.
|
||||||
|
open class ServerTrustPolicyManager {
|
||||||
|
/// The dictionary of policies mapped to a particular host.
|
||||||
|
public let policies: [String: ServerTrustPolicy]
|
||||||
|
|
||||||
|
/// Initializes the `ServerTrustPolicyManager` instance with the given policies.
|
||||||
|
///
|
||||||
|
/// Since different servers and web services can have different leaf certificates, intermediate and even root
|
||||||
|
/// certficates, it is important to have the flexibility to specify evaluation policies on a per host basis. This
|
||||||
|
/// allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key
|
||||||
|
/// pinning for host3 and disabling evaluation for host4.
|
||||||
|
///
|
||||||
|
/// - parameter policies: A dictionary of all policies mapped to a particular host.
|
||||||
|
///
|
||||||
|
/// - returns: The new `ServerTrustPolicyManager` instance.
|
||||||
|
public init(policies: [String: ServerTrustPolicy]) {
|
||||||
|
self.policies = policies
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `ServerTrustPolicy` for the given host if applicable.
|
||||||
|
///
|
||||||
|
/// By default, this method will return the policy that perfectly matches the given host. Subclasses could override
|
||||||
|
/// this method and implement more complex mapping implementations such as wildcards.
|
||||||
|
///
|
||||||
|
/// - parameter host: The host to use when searching for a matching policy.
|
||||||
|
///
|
||||||
|
/// - returns: The server trust policy for the given host if found.
|
||||||
|
open func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
|
||||||
|
return policies[host]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
extension URLSession {
|
||||||
|
private struct AssociatedKeys {
|
||||||
|
static var managerKey = "URLSession.ServerTrustPolicyManager"
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverTrustPolicyManager: ServerTrustPolicyManager? {
|
||||||
|
get {
|
||||||
|
return objc_getAssociatedObject(self, &AssociatedKeys.managerKey) as? ServerTrustPolicyManager
|
||||||
|
}
|
||||||
|
set (manager) {
|
||||||
|
objc_setAssociatedObject(self, &AssociatedKeys.managerKey, manager, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ServerTrustPolicy
|
||||||
|
|
||||||
|
/// The `ServerTrustPolicy` evaluates the server trust generally provided by an `NSURLAuthenticationChallenge` when
|
||||||
|
/// connecting to a server over a secure HTTPS connection. The policy configuration then evaluates the server trust
|
||||||
|
/// with a given set of criteria to determine whether the server trust is valid and the connection should be made.
|
||||||
|
///
|
||||||
|
/// Using pinned certificates or public keys for evaluation helps prevent man-in-the-middle (MITM) attacks and other
|
||||||
|
/// vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged
|
||||||
|
/// to route all communication over an HTTPS connection with pinning enabled.
|
||||||
|
///
|
||||||
|
/// - performDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to
|
||||||
|
/// validate the host provided by the challenge. Applications are encouraged to always
|
||||||
|
/// validate the host in production environments to guarantee the validity of the server's
|
||||||
|
/// certificate chain.
|
||||||
|
///
|
||||||
|
/// - performRevokedEvaluation: Uses the default and revoked server trust evaluations allowing you to control whether to
|
||||||
|
/// validate the host provided by the challenge as well as specify the revocation flags for
|
||||||
|
/// testing for revoked certificates. Apple platforms did not start testing for revoked
|
||||||
|
/// certificates automatically until iOS 10.1, macOS 10.12 and tvOS 10.1 which is
|
||||||
|
/// demonstrated in our TLS tests. Applications are encouraged to always validate the host
|
||||||
|
/// in production environments to guarantee the validity of the server's certificate chain.
|
||||||
|
///
|
||||||
|
/// - pinCertificates: Uses the pinned certificates to validate the server trust. The server trust is
|
||||||
|
/// considered valid if one of the pinned certificates match one of the server certificates.
|
||||||
|
/// By validating both the certificate chain and host, certificate pinning provides a very
|
||||||
|
/// secure form of server trust validation mitigating most, if not all, MITM attacks.
|
||||||
|
/// Applications are encouraged to always validate the host and require a valid certificate
|
||||||
|
/// chain in production environments.
|
||||||
|
///
|
||||||
|
/// - pinPublicKeys: Uses the pinned public keys to validate the server trust. The server trust is considered
|
||||||
|
/// valid if one of the pinned public keys match one of the server certificate public keys.
|
||||||
|
/// By validating both the certificate chain and host, public key pinning provides a very
|
||||||
|
/// secure form of server trust validation mitigating most, if not all, MITM attacks.
|
||||||
|
/// Applications are encouraged to always validate the host and require a valid certificate
|
||||||
|
/// chain in production environments.
|
||||||
|
///
|
||||||
|
/// - disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid.
|
||||||
|
///
|
||||||
|
/// - customEvaluation: Uses the associated closure to evaluate the validity of the server trust.
|
||||||
|
public enum ServerTrustPolicy {
|
||||||
|
case performDefaultEvaluation(validateHost: Bool)
|
||||||
|
case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags)
|
||||||
|
case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool)
|
||||||
|
case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool)
|
||||||
|
case disableEvaluation
|
||||||
|
case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool)
|
||||||
|
|
||||||
|
// MARK: - Bundle Location
|
||||||
|
|
||||||
|
/// Returns all certificates within the given bundle with a `.cer` file extension.
|
||||||
|
///
|
||||||
|
/// - parameter bundle: The bundle to search for all `.cer` files.
|
||||||
|
///
|
||||||
|
/// - returns: All certificates within the given bundle.
|
||||||
|
public static func certificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] {
|
||||||
|
var certificates: [SecCertificate] = []
|
||||||
|
|
||||||
|
let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in
|
||||||
|
bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil)
|
||||||
|
}.joined())
|
||||||
|
|
||||||
|
for path in paths {
|
||||||
|
if
|
||||||
|
let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData,
|
||||||
|
let certificate = SecCertificateCreateWithData(nil, certificateData)
|
||||||
|
{
|
||||||
|
certificates.append(certificate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificates
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all public keys within the given bundle with a `.cer` file extension.
|
||||||
|
///
|
||||||
|
/// - parameter bundle: The bundle to search for all `*.cer` files.
|
||||||
|
///
|
||||||
|
/// - returns: All public keys within the given bundle.
|
||||||
|
public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] {
|
||||||
|
var publicKeys: [SecKey] = []
|
||||||
|
|
||||||
|
for certificate in certificates(in: bundle) {
|
||||||
|
if let publicKey = publicKey(for: certificate) {
|
||||||
|
publicKeys.append(publicKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return publicKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Evaluation
|
||||||
|
|
||||||
|
/// Evaluates whether the server trust is valid for the given host.
|
||||||
|
///
|
||||||
|
/// - parameter serverTrust: The server trust to evaluate.
|
||||||
|
/// - parameter host: The host of the challenge protection space.
|
||||||
|
///
|
||||||
|
/// - returns: Whether the server trust is valid.
|
||||||
|
public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool {
|
||||||
|
var serverTrustIsValid = false
|
||||||
|
|
||||||
|
switch self {
|
||||||
|
case let .performDefaultEvaluation(validateHost):
|
||||||
|
let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
|
||||||
|
SecTrustSetPolicies(serverTrust, policy)
|
||||||
|
|
||||||
|
serverTrustIsValid = trustIsValid(serverTrust)
|
||||||
|
case let .performRevokedEvaluation(validateHost, revocationFlags):
|
||||||
|
let defaultPolicy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
|
||||||
|
let revokedPolicy = SecPolicyCreateRevocation(revocationFlags)
|
||||||
|
SecTrustSetPolicies(serverTrust, [defaultPolicy, revokedPolicy] as CFTypeRef)
|
||||||
|
|
||||||
|
serverTrustIsValid = trustIsValid(serverTrust)
|
||||||
|
case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost):
|
||||||
|
if validateCertificateChain {
|
||||||
|
let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
|
||||||
|
SecTrustSetPolicies(serverTrust, policy)
|
||||||
|
|
||||||
|
SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray)
|
||||||
|
SecTrustSetAnchorCertificatesOnly(serverTrust, true)
|
||||||
|
|
||||||
|
serverTrustIsValid = trustIsValid(serverTrust)
|
||||||
|
} else {
|
||||||
|
let serverCertificatesDataArray = certificateData(for: serverTrust)
|
||||||
|
let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates)
|
||||||
|
|
||||||
|
outerLoop: for serverCertificateData in serverCertificatesDataArray {
|
||||||
|
for pinnedCertificateData in pinnedCertificatesDataArray {
|
||||||
|
if serverCertificateData == pinnedCertificateData {
|
||||||
|
serverTrustIsValid = true
|
||||||
|
break outerLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost):
|
||||||
|
var certificateChainEvaluationPassed = true
|
||||||
|
|
||||||
|
if validateCertificateChain {
|
||||||
|
let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
|
||||||
|
SecTrustSetPolicies(serverTrust, policy)
|
||||||
|
|
||||||
|
certificateChainEvaluationPassed = trustIsValid(serverTrust)
|
||||||
|
}
|
||||||
|
|
||||||
|
if certificateChainEvaluationPassed {
|
||||||
|
outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeys(for: serverTrust) as [AnyObject] {
|
||||||
|
for pinnedPublicKey in pinnedPublicKeys as [AnyObject] {
|
||||||
|
if serverPublicKey.isEqual(pinnedPublicKey) {
|
||||||
|
serverTrustIsValid = true
|
||||||
|
break outerLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .disableEvaluation:
|
||||||
|
serverTrustIsValid = true
|
||||||
|
case let .customEvaluation(closure):
|
||||||
|
serverTrustIsValid = closure(serverTrust, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
return serverTrustIsValid
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private - Trust Validation
|
||||||
|
|
||||||
|
private func trustIsValid(_ trust: SecTrust) -> Bool {
|
||||||
|
var isValid = false
|
||||||
|
|
||||||
|
var result = SecTrustResultType.invalid
|
||||||
|
let status = SecTrustEvaluate(trust, &result)
|
||||||
|
|
||||||
|
if status == errSecSuccess {
|
||||||
|
let unspecified = SecTrustResultType.unspecified
|
||||||
|
let proceed = SecTrustResultType.proceed
|
||||||
|
|
||||||
|
isValid = result == unspecified || result == proceed
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private - Certificate Data
|
||||||
|
|
||||||
|
private func certificateData(for trust: SecTrust) -> [Data] {
|
||||||
|
var certificates: [SecCertificate] = []
|
||||||
|
|
||||||
|
for index in 0..<SecTrustGetCertificateCount(trust) {
|
||||||
|
if let certificate = SecTrustGetCertificateAtIndex(trust, index) {
|
||||||
|
certificates.append(certificate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificateData(for: certificates)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func certificateData(for certificates: [SecCertificate]) -> [Data] {
|
||||||
|
return certificates.map { SecCertificateCopyData($0) as Data }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private - Public Key Extraction
|
||||||
|
|
||||||
|
private static func publicKeys(for trust: SecTrust) -> [SecKey] {
|
||||||
|
var publicKeys: [SecKey] = []
|
||||||
|
|
||||||
|
for index in 0..<SecTrustGetCertificateCount(trust) {
|
||||||
|
if
|
||||||
|
let certificate = SecTrustGetCertificateAtIndex(trust, index),
|
||||||
|
let publicKey = publicKey(for: certificate)
|
||||||
|
{
|
||||||
|
publicKeys.append(publicKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return publicKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func publicKey(for certificate: SecCertificate) -> SecKey? {
|
||||||
|
var publicKey: SecKey?
|
||||||
|
|
||||||
|
let policy = SecPolicyCreateBasicX509()
|
||||||
|
var trust: SecTrust?
|
||||||
|
let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust)
|
||||||
|
|
||||||
|
if let trust = trust, trustCreationStatus == errSecSuccess {
|
||||||
|
publicKey = SecTrustCopyPublicKey(trust)
|
||||||
|
}
|
||||||
|
|
||||||
|
return publicKey
|
||||||
|
}
|
||||||
|
}
|
||||||
713
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/SessionDelegate.swift
generated
Normal file
713
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/SessionDelegate.swift
generated
Normal file
@@ -0,0 +1,713 @@
|
|||||||
|
//
|
||||||
|
// SessionDelegate.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Responsible for handling all delegate callbacks for the underlying session.
|
||||||
|
open class SessionDelegate: NSObject {
|
||||||
|
|
||||||
|
// MARK: URLSessionDelegate Overrides
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`.
|
||||||
|
open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
|
||||||
|
open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
|
||||||
|
|
||||||
|
/// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`.
|
||||||
|
open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`.
|
||||||
|
open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?
|
||||||
|
|
||||||
|
// MARK: URLSessionTaskDelegate Overrides
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`.
|
||||||
|
open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
|
||||||
|
|
||||||
|
/// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and
|
||||||
|
/// requires the caller to call the `completionHandler`.
|
||||||
|
open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`.
|
||||||
|
open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
|
||||||
|
|
||||||
|
/// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and
|
||||||
|
/// requires the caller to call the `completionHandler`.
|
||||||
|
open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`.
|
||||||
|
open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
|
||||||
|
|
||||||
|
/// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and
|
||||||
|
/// requires the caller to call the `completionHandler`.
|
||||||
|
open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`.
|
||||||
|
open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didCompleteWithError:)`.
|
||||||
|
open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
|
||||||
|
|
||||||
|
// MARK: URLSessionDataDelegate Overrides
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)`.
|
||||||
|
open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
|
||||||
|
|
||||||
|
/// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and
|
||||||
|
/// requires caller to call the `completionHandler`.
|
||||||
|
open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`.
|
||||||
|
open var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:)`.
|
||||||
|
open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`.
|
||||||
|
open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
|
||||||
|
|
||||||
|
/// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and
|
||||||
|
/// requires caller to call the `completionHandler`.
|
||||||
|
open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)?
|
||||||
|
|
||||||
|
// MARK: URLSessionDownloadDelegate Overrides
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didFinishDownloadingTo:)`.
|
||||||
|
open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`.
|
||||||
|
open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)`.
|
||||||
|
open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
|
||||||
|
|
||||||
|
// MARK: URLSessionStreamDelegate Overrides
|
||||||
|
|
||||||
|
#if !os(watchOS)
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:readClosedFor:)`.
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
|
||||||
|
get {
|
||||||
|
return _streamTaskReadClosed as? (URLSession, URLSessionStreamTask) -> Void
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_streamTaskReadClosed = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:writeClosedFor:)`.
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
|
||||||
|
get {
|
||||||
|
return _streamTaskWriteClosed as? (URLSession, URLSessionStreamTask) -> Void
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_streamTaskWriteClosed = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:betterRouteDiscoveredFor:)`.
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)? {
|
||||||
|
get {
|
||||||
|
return _streamTaskBetterRouteDiscovered as? (URLSession, URLSessionStreamTask) -> Void
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_streamTaskBetterRouteDiscovered = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:streamTask:didBecome:outputStream:)`.
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)? {
|
||||||
|
get {
|
||||||
|
return _streamTaskDidBecomeInputStream as? (URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_streamTaskDidBecomeInputStream = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _streamTaskReadClosed: Any?
|
||||||
|
var _streamTaskWriteClosed: Any?
|
||||||
|
var _streamTaskBetterRouteDiscovered: Any?
|
||||||
|
var _streamTaskDidBecomeInputStream: Any?
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
var retrier: RequestRetrier?
|
||||||
|
weak var sessionManager: SessionManager?
|
||||||
|
|
||||||
|
var requests: [Int: Request] = [:]
|
||||||
|
private let lock = NSLock()
|
||||||
|
|
||||||
|
/// Access the task delegate for the specified task in a thread-safe manner.
|
||||||
|
open subscript(task: URLSessionTask) -> Request? {
|
||||||
|
get {
|
||||||
|
lock.lock() ; defer { lock.unlock() }
|
||||||
|
return requests[task.taskIdentifier]
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
lock.lock() ; defer { lock.unlock() }
|
||||||
|
requests[task.taskIdentifier] = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Lifecycle
|
||||||
|
|
||||||
|
/// Initializes the `SessionDelegate` instance.
|
||||||
|
///
|
||||||
|
/// - returns: The new `SessionDelegate` instance.
|
||||||
|
public override init() {
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: NSObject Overrides
|
||||||
|
|
||||||
|
/// Returns a `Bool` indicating whether the `SessionDelegate` implements or inherits a method that can respond
|
||||||
|
/// to a specified message.
|
||||||
|
///
|
||||||
|
/// - parameter selector: A selector that identifies a message.
|
||||||
|
///
|
||||||
|
/// - returns: `true` if the receiver implements or inherits a method that can respond to selector, otherwise `false`.
|
||||||
|
open override func responds(to selector: Selector) -> Bool {
|
||||||
|
#if !os(macOS)
|
||||||
|
if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
|
||||||
|
return sessionDidFinishEventsForBackgroundURLSession != nil
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !os(watchOS)
|
||||||
|
if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) {
|
||||||
|
switch selector {
|
||||||
|
case #selector(URLSessionStreamDelegate.urlSession(_:readClosedFor:)):
|
||||||
|
return streamTaskReadClosed != nil
|
||||||
|
case #selector(URLSessionStreamDelegate.urlSession(_:writeClosedFor:)):
|
||||||
|
return streamTaskWriteClosed != nil
|
||||||
|
case #selector(URLSessionStreamDelegate.urlSession(_:betterRouteDiscoveredFor:)):
|
||||||
|
return streamTaskBetterRouteDiscovered != nil
|
||||||
|
case #selector(URLSessionStreamDelegate.urlSession(_:streamTask:didBecome:outputStream:)):
|
||||||
|
return streamTaskDidBecomeInputAndOutputStreams != nil
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch selector {
|
||||||
|
case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)):
|
||||||
|
return sessionDidBecomeInvalidWithError != nil
|
||||||
|
case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)):
|
||||||
|
return (sessionDidReceiveChallenge != nil || sessionDidReceiveChallengeWithCompletion != nil)
|
||||||
|
case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)):
|
||||||
|
return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil)
|
||||||
|
case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)):
|
||||||
|
return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil)
|
||||||
|
default:
|
||||||
|
return type(of: self).instancesRespond(to: selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - URLSessionDelegate
|
||||||
|
|
||||||
|
extension SessionDelegate: URLSessionDelegate {
|
||||||
|
/// Tells the delegate that the session has been invalidated.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session object that was invalidated.
|
||||||
|
/// - parameter error: The error that caused invalidation, or nil if the invalidation was explicit.
|
||||||
|
open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
|
||||||
|
sessionDidBecomeInvalidWithError?(session, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests credentials from the delegate in response to a session-level authentication request from the
|
||||||
|
/// remote server.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the task that requested authentication.
|
||||||
|
/// - parameter challenge: An object that contains the request for authentication.
|
||||||
|
/// - parameter completionHandler: A handler that your delegate method must call providing the disposition
|
||||||
|
/// and credential.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
didReceive challenge: URLAuthenticationChallenge,
|
||||||
|
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||||
|
guard sessionDidReceiveChallengeWithCompletion == nil else {
|
||||||
|
sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
|
||||||
|
var credential: URLCredential?
|
||||||
|
|
||||||
|
if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
|
||||||
|
(disposition, credential) = sessionDidReceiveChallenge(session, challenge)
|
||||||
|
} else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
|
||||||
|
let host = challenge.protectionSpace.host
|
||||||
|
|
||||||
|
if
|
||||||
|
let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
|
||||||
|
let serverTrust = challenge.protectionSpace.serverTrust
|
||||||
|
{
|
||||||
|
if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
|
||||||
|
disposition = .useCredential
|
||||||
|
credential = URLCredential(trust: serverTrust)
|
||||||
|
} else {
|
||||||
|
disposition = .cancelAuthenticationChallenge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(disposition, credential)
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !os(macOS)
|
||||||
|
|
||||||
|
/// Tells the delegate that all messages enqueued for a session have been delivered.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session that no longer has any outstanding requests.
|
||||||
|
open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
|
||||||
|
sessionDidFinishEventsForBackgroundURLSession?(session)
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - URLSessionTaskDelegate
|
||||||
|
|
||||||
|
extension SessionDelegate: URLSessionTaskDelegate {
|
||||||
|
/// Tells the delegate that the remote server requested an HTTP redirect.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the task whose request resulted in a redirect.
|
||||||
|
/// - parameter task: The task whose request resulted in a redirect.
|
||||||
|
/// - parameter response: An object containing the server’s response to the original request.
|
||||||
|
/// - parameter request: A URL request object filled out with the new location.
|
||||||
|
/// - parameter completionHandler: A closure that your handler should call with either the value of the request
|
||||||
|
/// parameter, a modified URL request object, or NULL to refuse the redirect and
|
||||||
|
/// return the body of the redirect response.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
task: URLSessionTask,
|
||||||
|
willPerformHTTPRedirection response: HTTPURLResponse,
|
||||||
|
newRequest request: URLRequest,
|
||||||
|
completionHandler: @escaping (URLRequest?) -> Void) {
|
||||||
|
guard taskWillPerformHTTPRedirectionWithCompletion == nil else {
|
||||||
|
taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var redirectRequest: URLRequest? = request
|
||||||
|
|
||||||
|
if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
|
||||||
|
redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(redirectRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests credentials from the delegate in response to an authentication request from the remote server.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the task whose request requires authentication.
|
||||||
|
/// - parameter task: The task whose request requires authentication.
|
||||||
|
/// - parameter challenge: An object that contains the request for authentication.
|
||||||
|
/// - parameter completionHandler: A handler that your delegate method must call providing the disposition
|
||||||
|
/// and credential.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
task: URLSessionTask,
|
||||||
|
didReceive challenge: URLAuthenticationChallenge,
|
||||||
|
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||||
|
guard taskDidReceiveChallengeWithCompletion == nil else {
|
||||||
|
taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let taskDidReceiveChallenge = taskDidReceiveChallenge {
|
||||||
|
let result = taskDidReceiveChallenge(session, task, challenge)
|
||||||
|
completionHandler(result.0, result.1)
|
||||||
|
} else if let delegate = self[task]?.delegate {
|
||||||
|
delegate.urlSession(
|
||||||
|
session,
|
||||||
|
task: task,
|
||||||
|
didReceive: challenge,
|
||||||
|
completionHandler: completionHandler
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
urlSession(session, didReceive: challenge, completionHandler: completionHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tells the delegate when a task requires a new request body stream to send to the remote server.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the task that needs a new body stream.
|
||||||
|
/// - parameter task: The task that needs a new body stream.
|
||||||
|
/// - parameter completionHandler: A completion handler that your delegate method should call with the new body stream.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
task: URLSessionTask,
|
||||||
|
needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) {
|
||||||
|
guard taskNeedNewBodyStreamWithCompletion == nil else {
|
||||||
|
taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let taskNeedNewBodyStream = taskNeedNewBodyStream {
|
||||||
|
completionHandler(taskNeedNewBodyStream(session, task))
|
||||||
|
} else if let delegate = self[task]?.delegate {
|
||||||
|
delegate.urlSession(session, task: task, needNewBodyStream: completionHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Periodically informs the delegate of the progress of sending body content to the server.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the data task.
|
||||||
|
/// - parameter task: The data task.
|
||||||
|
/// - parameter bytesSent: The number of bytes sent since the last time this delegate method was called.
|
||||||
|
/// - parameter totalBytesSent: The total number of bytes sent so far.
|
||||||
|
/// - parameter totalBytesExpectedToSend: The expected length of the body data.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
task: URLSessionTask,
|
||||||
|
didSendBodyData bytesSent: Int64,
|
||||||
|
totalBytesSent: Int64,
|
||||||
|
totalBytesExpectedToSend: Int64) {
|
||||||
|
if let taskDidSendBodyData = taskDidSendBodyData {
|
||||||
|
taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
|
||||||
|
} else if let delegate = self[task]?.delegate as? UploadTaskDelegate {
|
||||||
|
delegate.URLSession(
|
||||||
|
session,
|
||||||
|
task: task,
|
||||||
|
didSendBodyData: bytesSent,
|
||||||
|
totalBytesSent: totalBytesSent,
|
||||||
|
totalBytesExpectedToSend: totalBytesExpectedToSend
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !os(watchOS)
|
||||||
|
|
||||||
|
/// Tells the delegate that the session finished collecting metrics for the task.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session collecting the metrics.
|
||||||
|
/// - parameter task: The task whose metrics have been collected.
|
||||||
|
/// - parameter metrics: The collected metrics.
|
||||||
|
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
|
||||||
|
@objc(URLSession:task:didFinishCollectingMetrics:)
|
||||||
|
open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
|
||||||
|
self[task]?.delegate.metrics = metrics
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Tells the delegate that the task finished transferring data.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the task whose request finished transferring data.
|
||||||
|
/// - parameter task: The task whose request finished transferring data.
|
||||||
|
/// - parameter error: If an error occurred, an error object indicating how the transfer failed, otherwise nil.
|
||||||
|
open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
||||||
|
/// Executed after it is determined that the request is not going to be retried
|
||||||
|
let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
|
||||||
|
strongSelf.taskDidComplete?(session, task, error)
|
||||||
|
|
||||||
|
strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
|
||||||
|
|
||||||
|
var userInfo: [String: Any] = [Notification.Key.Task: task]
|
||||||
|
|
||||||
|
if let data = (strongSelf[task]?.delegate as? DataTaskDelegate)?.data {
|
||||||
|
userInfo[Notification.Key.ResponseData] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: Notification.Name.Task.DidComplete,
|
||||||
|
object: strongSelf,
|
||||||
|
userInfo: userInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
strongSelf[task] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let request = self[task], let sessionManager = sessionManager else {
|
||||||
|
completeTask(session, task, error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all validations on the request before checking if an error occurred
|
||||||
|
request.validations.forEach { $0() }
|
||||||
|
|
||||||
|
// Determine whether an error has occurred
|
||||||
|
var error: Error? = error
|
||||||
|
|
||||||
|
if request.delegate.error != nil {
|
||||||
|
error = request.delegate.error
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
|
||||||
|
/// should be retried. Otherwise, complete the task by notifying the task delegate.
|
||||||
|
if let retrier = retrier, let error = error {
|
||||||
|
retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
|
||||||
|
guard shouldRetry else { completeTask(session, task, error) ; return }
|
||||||
|
|
||||||
|
DispatchQueue.utility.after(timeDelay) { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
|
||||||
|
let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false
|
||||||
|
|
||||||
|
if retrySucceeded, let task = request.task {
|
||||||
|
strongSelf[task] = request
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
completeTask(session, task, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completeTask(session, task, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - URLSessionDataDelegate
|
||||||
|
|
||||||
|
extension SessionDelegate: URLSessionDataDelegate {
|
||||||
|
/// Tells the delegate that the data task received the initial reply (headers) from the server.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the data task that received an initial reply.
|
||||||
|
/// - parameter dataTask: The data task that received an initial reply.
|
||||||
|
/// - parameter response: A URL response object populated with headers.
|
||||||
|
/// - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a
|
||||||
|
/// constant to indicate whether the transfer should continue as a data task or
|
||||||
|
/// should become a download task.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
dataTask: URLSessionDataTask,
|
||||||
|
didReceive response: URLResponse,
|
||||||
|
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
|
||||||
|
guard dataTaskDidReceiveResponseWithCompletion == nil else {
|
||||||
|
dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var disposition: URLSession.ResponseDisposition = .allow
|
||||||
|
|
||||||
|
if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
|
||||||
|
disposition = dataTaskDidReceiveResponse(session, dataTask, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(disposition)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tells the delegate that the data task was changed to a download task.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the task that was replaced by a download task.
|
||||||
|
/// - parameter dataTask: The data task that was replaced by a download task.
|
||||||
|
/// - parameter downloadTask: The new download task that replaced the data task.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
dataTask: URLSessionDataTask,
|
||||||
|
didBecome downloadTask: URLSessionDownloadTask) {
|
||||||
|
if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
|
||||||
|
dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
|
||||||
|
} else {
|
||||||
|
self[downloadTask]?.delegate = DownloadTaskDelegate(task: downloadTask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tells the delegate that the data task has received some of the expected data.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the data task that provided data.
|
||||||
|
/// - parameter dataTask: The data task that provided data.
|
||||||
|
/// - parameter data: A data object containing the transferred data.
|
||||||
|
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
||||||
|
if let dataTaskDidReceiveData = dataTaskDidReceiveData {
|
||||||
|
dataTaskDidReceiveData(session, dataTask, data)
|
||||||
|
} else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
|
||||||
|
delegate.urlSession(session, dataTask: dataTask, didReceive: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asks the delegate whether the data (or upload) task should store the response in the cache.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the data (or upload) task.
|
||||||
|
/// - parameter dataTask: The data (or upload) task.
|
||||||
|
/// - parameter proposedResponse: The default caching behavior. This behavior is determined based on the current
|
||||||
|
/// caching policy and the values of certain received headers, such as the Pragma
|
||||||
|
/// and Cache-Control headers.
|
||||||
|
/// - parameter completionHandler: A block that your handler must call, providing either the original proposed
|
||||||
|
/// response, a modified version of that response, or NULL to prevent caching the
|
||||||
|
/// response. If your delegate implements this method, it must call this completion
|
||||||
|
/// handler; otherwise, your app leaks memory.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
dataTask: URLSessionDataTask,
|
||||||
|
willCacheResponse proposedResponse: CachedURLResponse,
|
||||||
|
completionHandler: @escaping (CachedURLResponse?) -> Void) {
|
||||||
|
guard dataTaskWillCacheResponseWithCompletion == nil else {
|
||||||
|
dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
|
||||||
|
completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
|
||||||
|
} else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
|
||||||
|
delegate.urlSession(
|
||||||
|
session,
|
||||||
|
dataTask: dataTask,
|
||||||
|
willCacheResponse: proposedResponse,
|
||||||
|
completionHandler: completionHandler
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
completionHandler(proposedResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - URLSessionDownloadDelegate
|
||||||
|
|
||||||
|
extension SessionDelegate: URLSessionDownloadDelegate {
|
||||||
|
/// Tells the delegate that a download task has finished downloading.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the download task that finished.
|
||||||
|
/// - parameter downloadTask: The download task that finished.
|
||||||
|
/// - parameter location: A file URL for the temporary file. Because the file is temporary, you must either
|
||||||
|
/// open the file for reading or move it to a permanent location in your app’s sandbox
|
||||||
|
/// container directory before returning from this delegate method.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
downloadTask: URLSessionDownloadTask,
|
||||||
|
didFinishDownloadingTo location: URL) {
|
||||||
|
if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
|
||||||
|
downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
|
||||||
|
} else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
|
||||||
|
delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Periodically informs the delegate about the download’s progress.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the download task.
|
||||||
|
/// - parameter downloadTask: The download task.
|
||||||
|
/// - parameter bytesWritten: The number of bytes transferred since the last time this delegate
|
||||||
|
/// method was called.
|
||||||
|
/// - parameter totalBytesWritten: The total number of bytes transferred so far.
|
||||||
|
/// - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length
|
||||||
|
/// header. If this header was not provided, the value is
|
||||||
|
/// `NSURLSessionTransferSizeUnknown`.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
downloadTask: URLSessionDownloadTask,
|
||||||
|
didWriteData bytesWritten: Int64,
|
||||||
|
totalBytesWritten: Int64,
|
||||||
|
totalBytesExpectedToWrite: Int64) {
|
||||||
|
if let downloadTaskDidWriteData = downloadTaskDidWriteData {
|
||||||
|
downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
|
||||||
|
} else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
|
||||||
|
delegate.urlSession(
|
||||||
|
session,
|
||||||
|
downloadTask: downloadTask,
|
||||||
|
didWriteData: bytesWritten,
|
||||||
|
totalBytesWritten: totalBytesWritten,
|
||||||
|
totalBytesExpectedToWrite: totalBytesExpectedToWrite
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tells the delegate that the download task has resumed downloading.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session containing the download task that finished.
|
||||||
|
/// - parameter downloadTask: The download task that resumed. See explanation in the discussion.
|
||||||
|
/// - parameter fileOffset: If the file's cache policy or last modified date prevents reuse of the
|
||||||
|
/// existing content, then this value is zero. Otherwise, this value is an
|
||||||
|
/// integer representing the number of bytes on disk that do not need to be
|
||||||
|
/// retrieved again.
|
||||||
|
/// - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header.
|
||||||
|
/// If this header was not provided, the value is NSURLSessionTransferSizeUnknown.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
downloadTask: URLSessionDownloadTask,
|
||||||
|
didResumeAtOffset fileOffset: Int64,
|
||||||
|
expectedTotalBytes: Int64) {
|
||||||
|
if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
|
||||||
|
downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
|
||||||
|
} else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
|
||||||
|
delegate.urlSession(
|
||||||
|
session,
|
||||||
|
downloadTask: downloadTask,
|
||||||
|
didResumeAtOffset: fileOffset,
|
||||||
|
expectedTotalBytes: expectedTotalBytes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - URLSessionStreamDelegate
|
||||||
|
|
||||||
|
#if !os(watchOS)
|
||||||
|
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
extension SessionDelegate: URLSessionStreamDelegate {
|
||||||
|
/// Tells the delegate that the read side of the connection has been closed.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session.
|
||||||
|
/// - parameter streamTask: The stream task.
|
||||||
|
open func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) {
|
||||||
|
streamTaskReadClosed?(session, streamTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tells the delegate that the write side of the connection has been closed.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session.
|
||||||
|
/// - parameter streamTask: The stream task.
|
||||||
|
open func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) {
|
||||||
|
streamTaskWriteClosed?(session, streamTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tells the delegate that the system has determined that a better route to the host is available.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session.
|
||||||
|
/// - parameter streamTask: The stream task.
|
||||||
|
open func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) {
|
||||||
|
streamTaskBetterRouteDiscovered?(session, streamTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tells the delegate that the stream task has been completed and provides the unopened stream objects.
|
||||||
|
///
|
||||||
|
/// - parameter session: The session.
|
||||||
|
/// - parameter streamTask: The stream task.
|
||||||
|
/// - parameter inputStream: The new input stream.
|
||||||
|
/// - parameter outputStream: The new output stream.
|
||||||
|
open func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
streamTask: URLSessionStreamTask,
|
||||||
|
didBecome inputStream: InputStream,
|
||||||
|
outputStream: OutputStream) {
|
||||||
|
streamTaskDidBecomeInputAndOutputStreams?(session, streamTask, inputStream, outputStream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
886
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/SessionManager.swift
generated
Normal file
886
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/SessionManager.swift
generated
Normal file
@@ -0,0 +1,886 @@
|
|||||||
|
//
|
||||||
|
// SessionManager.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Responsible for creating and managing `Request` objects, as well as their underlying `NSURLSession`.
|
||||||
|
open class SessionManager {
|
||||||
|
|
||||||
|
// MARK: - Helper Types
|
||||||
|
|
||||||
|
/// Defines whether the `MultipartFormData` encoding was successful and contains result of the encoding as
|
||||||
|
/// associated values.
|
||||||
|
///
|
||||||
|
/// - Success: Represents a successful `MultipartFormData` encoding and contains the new `UploadRequest` along with
|
||||||
|
/// streaming information.
|
||||||
|
/// - Failure: Used to represent a failure in the `MultipartFormData` encoding and also contains the encoding
|
||||||
|
/// error.
|
||||||
|
public enum MultipartFormDataEncodingResult {
|
||||||
|
case success(request: UploadRequest, streamingFromDisk: Bool, streamFileURL: URL?)
|
||||||
|
case failure(Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
/// A default instance of `SessionManager`, used by top-level Alamofire request methods, and suitable for use
|
||||||
|
/// directly for any ad hoc requests.
|
||||||
|
public static let `default`: SessionManager = {
|
||||||
|
let configuration = URLSessionConfiguration.default
|
||||||
|
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
|
||||||
|
|
||||||
|
return SessionManager(configuration: configuration)
|
||||||
|
}()
|
||||||
|
|
||||||
|
/// Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers.
|
||||||
|
public static let defaultHTTPHeaders: HTTPHeaders = {
|
||||||
|
// Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3
|
||||||
|
let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"
|
||||||
|
|
||||||
|
// Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5
|
||||||
|
let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
|
||||||
|
let quality = 1.0 - (Double(index) * 0.1)
|
||||||
|
return "\(languageCode);q=\(quality)"
|
||||||
|
}.joined(separator: ", ")
|
||||||
|
|
||||||
|
// User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3
|
||||||
|
// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0`
|
||||||
|
let userAgent: String = {
|
||||||
|
if let info = Bundle.main.infoDictionary {
|
||||||
|
let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
|
||||||
|
let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
|
||||||
|
let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown"
|
||||||
|
let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown"
|
||||||
|
|
||||||
|
let osNameVersion: String = {
|
||||||
|
let version = ProcessInfo.processInfo.operatingSystemVersion
|
||||||
|
let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
|
||||||
|
|
||||||
|
let osName: String = {
|
||||||
|
#if os(iOS)
|
||||||
|
return "iOS"
|
||||||
|
#elseif os(watchOS)
|
||||||
|
return "watchOS"
|
||||||
|
#elseif os(tvOS)
|
||||||
|
return "tvOS"
|
||||||
|
#elseif os(macOS)
|
||||||
|
return "OS X"
|
||||||
|
#elseif os(Linux)
|
||||||
|
return "Linux"
|
||||||
|
#else
|
||||||
|
return "Unknown"
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
|
||||||
|
return "\(osName) \(versionString)"
|
||||||
|
}()
|
||||||
|
|
||||||
|
let alamofireVersion: String = {
|
||||||
|
guard
|
||||||
|
let afInfo = Bundle(for: SessionManager.self).infoDictionary,
|
||||||
|
let build = afInfo["CFBundleShortVersionString"]
|
||||||
|
else { return "Unknown" }
|
||||||
|
|
||||||
|
return "Alamofire/\(build)"
|
||||||
|
}()
|
||||||
|
|
||||||
|
return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Alamofire"
|
||||||
|
}()
|
||||||
|
|
||||||
|
return [
|
||||||
|
"Accept-Encoding": acceptEncoding,
|
||||||
|
"Accept-Language": acceptLanguage,
|
||||||
|
"User-Agent": userAgent
|
||||||
|
]
|
||||||
|
}()
|
||||||
|
|
||||||
|
/// Default memory threshold used when encoding `MultipartFormData` in bytes.
|
||||||
|
public static let multipartFormDataEncodingMemoryThreshold: UInt64 = 10_000_000
|
||||||
|
|
||||||
|
/// The underlying session.
|
||||||
|
public let session: URLSession
|
||||||
|
|
||||||
|
/// The session delegate handling all the task and session delegate callbacks.
|
||||||
|
public let delegate: SessionDelegate
|
||||||
|
|
||||||
|
/// Whether to start requests immediately after being constructed. `true` by default.
|
||||||
|
open var startRequestsImmediately: Bool = true
|
||||||
|
|
||||||
|
/// The request adapter called each time a new request is created.
|
||||||
|
open var adapter: RequestAdapter?
|
||||||
|
|
||||||
|
/// The request retrier called each time a request encounters an error to determine whether to retry the request.
|
||||||
|
open var retrier: RequestRetrier? {
|
||||||
|
get { return delegate.retrier }
|
||||||
|
set { delegate.retrier = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The background completion handler closure provided by the UIApplicationDelegate
|
||||||
|
/// `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background
|
||||||
|
/// completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation
|
||||||
|
/// will automatically call the handler.
|
||||||
|
///
|
||||||
|
/// If you need to handle your own events before the handler is called, then you need to override the
|
||||||
|
/// SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished.
|
||||||
|
///
|
||||||
|
/// `nil` by default.
|
||||||
|
open var backgroundCompletionHandler: (() -> Void)?
|
||||||
|
|
||||||
|
let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)
|
||||||
|
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
|
/// Creates an instance with the specified `configuration`, `delegate` and `serverTrustPolicyManager`.
|
||||||
|
///
|
||||||
|
/// - parameter configuration: The configuration used to construct the managed session.
|
||||||
|
/// `URLSessionConfiguration.default` by default.
|
||||||
|
/// - parameter delegate: The delegate used when initializing the session. `SessionDelegate()` by
|
||||||
|
/// default.
|
||||||
|
/// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
|
||||||
|
/// challenges. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The new `SessionManager` instance.
|
||||||
|
public init(
|
||||||
|
configuration: URLSessionConfiguration = URLSessionConfiguration.default,
|
||||||
|
delegate: SessionDelegate = SessionDelegate(),
|
||||||
|
serverTrustPolicyManager: ServerTrustPolicyManager? = nil) {
|
||||||
|
self.delegate = delegate
|
||||||
|
self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
|
||||||
|
|
||||||
|
commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an instance with the specified `session`, `delegate` and `serverTrustPolicyManager`.
|
||||||
|
///
|
||||||
|
/// - parameter session: The URL session.
|
||||||
|
/// - parameter delegate: The delegate of the URL session. Must equal the URL session's delegate.
|
||||||
|
/// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
|
||||||
|
/// challenges. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The new `SessionManager` instance if the URL session's delegate matches; `nil` otherwise.
|
||||||
|
public init?(
|
||||||
|
session: URLSession,
|
||||||
|
delegate: SessionDelegate,
|
||||||
|
serverTrustPolicyManager: ServerTrustPolicyManager? = nil) {
|
||||||
|
guard delegate === session.delegate else { return nil }
|
||||||
|
|
||||||
|
self.delegate = delegate
|
||||||
|
self.session = session
|
||||||
|
|
||||||
|
commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
|
||||||
|
session.serverTrustPolicyManager = serverTrustPolicyManager
|
||||||
|
|
||||||
|
delegate.sessionManager = self
|
||||||
|
|
||||||
|
delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
session.invalidateAndCancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Data Request
|
||||||
|
|
||||||
|
/// Creates a `DataRequest` to retrieve the contents of the specified `url`, `method`, `parameters`, `encoding`
|
||||||
|
/// and `headers`.
|
||||||
|
///
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.get` by default.
|
||||||
|
/// - parameter parameters: The parameters. `nil` by default.
|
||||||
|
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `DataRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func request(
|
||||||
|
_ url: URLConvertible,
|
||||||
|
method: HTTPMethod = .get,
|
||||||
|
parameters: Parameters? = nil,
|
||||||
|
encoding: ParameterEncoding = URLEncoding.default,
|
||||||
|
headers: HTTPHeaders? = nil)
|
||||||
|
-> DataRequest {
|
||||||
|
var originalRequest: URLRequest?
|
||||||
|
|
||||||
|
do {
|
||||||
|
originalRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||||
|
let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
|
||||||
|
return request(encodedURLRequest)
|
||||||
|
} catch {
|
||||||
|
return request(originalRequest, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `DataRequest` to retrieve the contents of a URL based on the specified `urlRequest`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The URL request.
|
||||||
|
///
|
||||||
|
/// - returns: The created `DataRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
|
||||||
|
var originalRequest: URLRequest?
|
||||||
|
|
||||||
|
do {
|
||||||
|
originalRequest = try urlRequest.asURLRequest()
|
||||||
|
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
|
||||||
|
|
||||||
|
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
|
||||||
|
let request = DataRequest(session: session, requestTask: .data(originalTask, task))
|
||||||
|
|
||||||
|
delegate[task] = request
|
||||||
|
|
||||||
|
if startRequestsImmediately { request.resume() }
|
||||||
|
|
||||||
|
return request
|
||||||
|
} catch {
|
||||||
|
return request(originalRequest, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Private - Request Implementation
|
||||||
|
|
||||||
|
private func request(_ urlRequest: URLRequest?, failedWith error: Error) -> DataRequest {
|
||||||
|
var requestTask: Request.RequestTask = .data(nil, nil)
|
||||||
|
|
||||||
|
if let urlRequest = urlRequest {
|
||||||
|
let originalTask = DataRequest.Requestable(urlRequest: urlRequest)
|
||||||
|
requestTask = .data(originalTask, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
let underlyingError = error.underlyingAdaptError ?? error
|
||||||
|
let request = DataRequest(session: session, requestTask: requestTask, error: underlyingError)
|
||||||
|
|
||||||
|
if let retrier = retrier, error is AdaptError {
|
||||||
|
allowRetrier(retrier, toRetry: request, with: underlyingError)
|
||||||
|
} else {
|
||||||
|
if startRequestsImmediately { request.resume() }
|
||||||
|
}
|
||||||
|
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Download Request
|
||||||
|
|
||||||
|
// MARK: URL Request
|
||||||
|
|
||||||
|
/// Creates a `DownloadRequest` to retrieve the contents the specified `url`, `method`, `parameters`, `encoding`,
|
||||||
|
/// `headers` and save them to the `destination`.
|
||||||
|
///
|
||||||
|
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||||
|
/// underlying URL session.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.get` by default.
|
||||||
|
/// - parameter parameters: The parameters. `nil` by default.
|
||||||
|
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `DownloadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func download(
|
||||||
|
_ url: URLConvertible,
|
||||||
|
method: HTTPMethod = .get,
|
||||||
|
parameters: Parameters? = nil,
|
||||||
|
encoding: ParameterEncoding = URLEncoding.default,
|
||||||
|
headers: HTTPHeaders? = nil,
|
||||||
|
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||||
|
-> DownloadRequest {
|
||||||
|
do {
|
||||||
|
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||||
|
let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
|
||||||
|
return download(encodedURLRequest, to: destination)
|
||||||
|
} catch {
|
||||||
|
return download(nil, to: destination, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `DownloadRequest` to retrieve the contents of a URL based on the specified `urlRequest` and save
|
||||||
|
/// them to the `destination`.
|
||||||
|
///
|
||||||
|
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||||
|
/// underlying URL session.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter urlRequest: The URL request
|
||||||
|
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `DownloadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func download(
|
||||||
|
_ urlRequest: URLRequestConvertible,
|
||||||
|
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||||
|
-> DownloadRequest {
|
||||||
|
do {
|
||||||
|
let urlRequest = try urlRequest.asURLRequest()
|
||||||
|
return download(.request(urlRequest), to: destination)
|
||||||
|
} catch {
|
||||||
|
return download(nil, to: destination, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Resume Data
|
||||||
|
|
||||||
|
/// Creates a `DownloadRequest` from the `resumeData` produced from a previous request cancellation to retrieve
|
||||||
|
/// the contents of the original request and save them to the `destination`.
|
||||||
|
///
|
||||||
|
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||||
|
/// underlying URL session.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken
|
||||||
|
/// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the
|
||||||
|
/// data is written incorrectly and will always fail to resume the download. For more information about the bug and
|
||||||
|
/// possible workarounds, please refer to the following Stack Overflow post:
|
||||||
|
///
|
||||||
|
/// - http://stackoverflow.com/a/39347461/1342462
|
||||||
|
///
|
||||||
|
/// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask`
|
||||||
|
/// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for
|
||||||
|
/// additional information.
|
||||||
|
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `DownloadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func download(
|
||||||
|
resumingWith resumeData: Data,
|
||||||
|
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||||
|
-> DownloadRequest {
|
||||||
|
return download(.resumeData(resumeData), to: destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Private - Download Implementation
|
||||||
|
|
||||||
|
private func download(
|
||||||
|
_ downloadable: DownloadRequest.Downloadable,
|
||||||
|
to destination: DownloadRequest.DownloadFileDestination?)
|
||||||
|
-> DownloadRequest {
|
||||||
|
do {
|
||||||
|
let task = try downloadable.task(session: session, adapter: adapter, queue: queue)
|
||||||
|
let download = DownloadRequest(session: session, requestTask: .download(downloadable, task))
|
||||||
|
|
||||||
|
download.downloadDelegate.destination = destination
|
||||||
|
|
||||||
|
delegate[task] = download
|
||||||
|
|
||||||
|
if startRequestsImmediately { download.resume() }
|
||||||
|
|
||||||
|
return download
|
||||||
|
} catch {
|
||||||
|
return download(downloadable, to: destination, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func download(
|
||||||
|
_ downloadable: DownloadRequest.Downloadable?,
|
||||||
|
to destination: DownloadRequest.DownloadFileDestination?,
|
||||||
|
failedWith error: Error)
|
||||||
|
-> DownloadRequest {
|
||||||
|
var downloadTask: Request.RequestTask = .download(nil, nil)
|
||||||
|
|
||||||
|
if let downloadable = downloadable {
|
||||||
|
downloadTask = .download(downloadable, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
let underlyingError = error.underlyingAdaptError ?? error
|
||||||
|
|
||||||
|
let download = DownloadRequest(session: session, requestTask: downloadTask, error: underlyingError)
|
||||||
|
download.downloadDelegate.destination = destination
|
||||||
|
|
||||||
|
if let retrier = retrier, error is AdaptError {
|
||||||
|
allowRetrier(retrier, toRetry: download, with: underlyingError)
|
||||||
|
} else {
|
||||||
|
if startRequestsImmediately { download.resume() }
|
||||||
|
}
|
||||||
|
|
||||||
|
return download
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Upload Request
|
||||||
|
|
||||||
|
// MARK: File
|
||||||
|
|
||||||
|
/// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `file`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file to upload.
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.post` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func upload(
|
||||||
|
_ fileURL: URL,
|
||||||
|
to url: URLConvertible,
|
||||||
|
method: HTTPMethod = .post,
|
||||||
|
headers: HTTPHeaders? = nil)
|
||||||
|
-> UploadRequest {
|
||||||
|
do {
|
||||||
|
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||||
|
return upload(fileURL, with: urlRequest)
|
||||||
|
} catch {
|
||||||
|
return upload(nil, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `UploadRequest` from the specified `urlRequest` for uploading the `file`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter file: The file to upload.
|
||||||
|
/// - parameter urlRequest: The URL request.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||||
|
do {
|
||||||
|
let urlRequest = try urlRequest.asURLRequest()
|
||||||
|
return upload(.file(fileURL, urlRequest))
|
||||||
|
} catch {
|
||||||
|
return upload(nil, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Data
|
||||||
|
|
||||||
|
/// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `data`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter data: The data to upload.
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.post` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func upload(
|
||||||
|
_ data: Data,
|
||||||
|
to url: URLConvertible,
|
||||||
|
method: HTTPMethod = .post,
|
||||||
|
headers: HTTPHeaders? = nil)
|
||||||
|
-> UploadRequest {
|
||||||
|
do {
|
||||||
|
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||||
|
return upload(data, with: urlRequest)
|
||||||
|
} catch {
|
||||||
|
return upload(nil, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an `UploadRequest` from the specified `urlRequest` for uploading the `data`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter data: The data to upload.
|
||||||
|
/// - parameter urlRequest: The URL request.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||||
|
do {
|
||||||
|
let urlRequest = try urlRequest.asURLRequest()
|
||||||
|
return upload(.data(data, urlRequest))
|
||||||
|
} catch {
|
||||||
|
return upload(nil, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: InputStream
|
||||||
|
|
||||||
|
/// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `stream`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter stream: The stream to upload.
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.post` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func upload(
|
||||||
|
_ stream: InputStream,
|
||||||
|
to url: URLConvertible,
|
||||||
|
method: HTTPMethod = .post,
|
||||||
|
headers: HTTPHeaders? = nil)
|
||||||
|
-> UploadRequest {
|
||||||
|
do {
|
||||||
|
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||||
|
return upload(stream, with: urlRequest)
|
||||||
|
} catch {
|
||||||
|
return upload(nil, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an `UploadRequest` from the specified `urlRequest` for uploading the `stream`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter stream: The stream to upload.
|
||||||
|
/// - parameter urlRequest: The URL request.
|
||||||
|
///
|
||||||
|
/// - returns: The created `UploadRequest`.
|
||||||
|
@discardableResult
|
||||||
|
open func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||||
|
do {
|
||||||
|
let urlRequest = try urlRequest.asURLRequest()
|
||||||
|
return upload(.stream(stream, urlRequest))
|
||||||
|
} catch {
|
||||||
|
return upload(nil, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: MultipartFormData
|
||||||
|
|
||||||
|
/// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new
|
||||||
|
/// `UploadRequest` using the `url`, `method` and `headers`.
|
||||||
|
///
|
||||||
|
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
|
||||||
|
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
|
||||||
|
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
|
||||||
|
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
|
||||||
|
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
|
||||||
|
/// used for larger payloads such as video content.
|
||||||
|
///
|
||||||
|
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
|
||||||
|
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
|
||||||
|
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
|
||||||
|
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
|
||||||
|
/// technique was used.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
|
||||||
|
/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
|
||||||
|
/// `multipartFormDataEncodingMemoryThreshold` by default.
|
||||||
|
/// - parameter url: The URL.
|
||||||
|
/// - parameter method: The HTTP method. `.post` by default.
|
||||||
|
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||||
|
/// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
|
||||||
|
open func upload(
|
||||||
|
multipartFormData: @escaping (MultipartFormData) -> Void,
|
||||||
|
usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
|
||||||
|
to url: URLConvertible,
|
||||||
|
method: HTTPMethod = .post,
|
||||||
|
headers: HTTPHeaders? = nil,
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?) {
|
||||||
|
do {
|
||||||
|
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||||
|
|
||||||
|
return upload(
|
||||||
|
multipartFormData: multipartFormData,
|
||||||
|
usingThreshold: encodingMemoryThreshold,
|
||||||
|
with: urlRequest,
|
||||||
|
queue: queue,
|
||||||
|
encodingCompletion: encodingCompletion
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
(queue ?? DispatchQueue.main).async { encodingCompletion?(.failure(error)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new
|
||||||
|
/// `UploadRequest` using the `urlRequest`.
|
||||||
|
///
|
||||||
|
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
|
||||||
|
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
|
||||||
|
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
|
||||||
|
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
|
||||||
|
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
|
||||||
|
/// used for larger payloads such as video content.
|
||||||
|
///
|
||||||
|
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
|
||||||
|
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
|
||||||
|
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
|
||||||
|
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
|
||||||
|
/// technique was used.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
|
||||||
|
/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
|
||||||
|
/// `multipartFormDataEncodingMemoryThreshold` by default.
|
||||||
|
/// - parameter urlRequest: The URL request.
|
||||||
|
/// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
|
||||||
|
open func upload(
|
||||||
|
multipartFormData: @escaping (MultipartFormData) -> Void,
|
||||||
|
usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
|
||||||
|
with urlRequest: URLRequestConvertible,
|
||||||
|
queue: DispatchQueue? = nil,
|
||||||
|
encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?) {
|
||||||
|
DispatchQueue.global(qos: .utility).async {
|
||||||
|
let formData = MultipartFormData()
|
||||||
|
multipartFormData(formData)
|
||||||
|
|
||||||
|
var tempFileURL: URL?
|
||||||
|
|
||||||
|
do {
|
||||||
|
var urlRequestWithContentType = try urlRequest.asURLRequest()
|
||||||
|
urlRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type")
|
||||||
|
|
||||||
|
let isBackgroundSession = self.session.configuration.identifier != nil
|
||||||
|
|
||||||
|
if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession {
|
||||||
|
let data = try formData.encode()
|
||||||
|
|
||||||
|
let encodingResult = MultipartFormDataEncodingResult.success(
|
||||||
|
request: self.upload(data, with: urlRequestWithContentType),
|
||||||
|
streamingFromDisk: false,
|
||||||
|
streamFileURL: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
(queue ?? DispatchQueue.main).async { encodingCompletion?(encodingResult) }
|
||||||
|
} else {
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory())
|
||||||
|
let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data")
|
||||||
|
let fileName = UUID().uuidString
|
||||||
|
let fileURL = directoryURL.appendingPathComponent(fileName)
|
||||||
|
|
||||||
|
tempFileURL = fileURL
|
||||||
|
|
||||||
|
var directoryError: Error?
|
||||||
|
|
||||||
|
// Create directory inside serial queue to ensure two threads don't do this in parallel
|
||||||
|
self.queue.sync {
|
||||||
|
do {
|
||||||
|
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
} catch {
|
||||||
|
directoryError = error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let directoryError = directoryError { throw directoryError }
|
||||||
|
|
||||||
|
try formData.writeEncodedData(to: fileURL)
|
||||||
|
|
||||||
|
let upload = self.upload(fileURL, with: urlRequestWithContentType)
|
||||||
|
|
||||||
|
// Cleanup the temp file once the upload is complete
|
||||||
|
upload.delegate.queue.addOperation {
|
||||||
|
do {
|
||||||
|
try FileManager.default.removeItem(at: fileURL)
|
||||||
|
} catch {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(queue ?? DispatchQueue.main).async {
|
||||||
|
let encodingResult = MultipartFormDataEncodingResult.success(
|
||||||
|
request: upload,
|
||||||
|
streamingFromDisk: true,
|
||||||
|
streamFileURL: fileURL
|
||||||
|
)
|
||||||
|
|
||||||
|
encodingCompletion?(encodingResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Cleanup the temp file in the event that the multipart form data encoding failed
|
||||||
|
if let tempFileURL = tempFileURL {
|
||||||
|
do {
|
||||||
|
try FileManager.default.removeItem(at: tempFileURL)
|
||||||
|
} catch {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(queue ?? DispatchQueue.main).async { encodingCompletion?(.failure(error)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Private - Upload Implementation
|
||||||
|
|
||||||
|
private func upload(_ uploadable: UploadRequest.Uploadable) -> UploadRequest {
|
||||||
|
do {
|
||||||
|
let task = try uploadable.task(session: session, adapter: adapter, queue: queue)
|
||||||
|
let upload = UploadRequest(session: session, requestTask: .upload(uploadable, task))
|
||||||
|
|
||||||
|
if case let .stream(inputStream, _) = uploadable {
|
||||||
|
upload.delegate.taskNeedNewBodyStream = { _, _ in inputStream }
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate[task] = upload
|
||||||
|
|
||||||
|
if startRequestsImmediately { upload.resume() }
|
||||||
|
|
||||||
|
return upload
|
||||||
|
} catch {
|
||||||
|
return upload(uploadable, failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func upload(_ uploadable: UploadRequest.Uploadable?, failedWith error: Error) -> UploadRequest {
|
||||||
|
var uploadTask: Request.RequestTask = .upload(nil, nil)
|
||||||
|
|
||||||
|
if let uploadable = uploadable {
|
||||||
|
uploadTask = .upload(uploadable, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
let underlyingError = error.underlyingAdaptError ?? error
|
||||||
|
let upload = UploadRequest(session: session, requestTask: uploadTask, error: underlyingError)
|
||||||
|
|
||||||
|
if let retrier = retrier, error is AdaptError {
|
||||||
|
allowRetrier(retrier, toRetry: upload, with: underlyingError)
|
||||||
|
} else {
|
||||||
|
if startRequestsImmediately { upload.resume() }
|
||||||
|
}
|
||||||
|
|
||||||
|
return upload
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !os(watchOS)
|
||||||
|
|
||||||
|
// MARK: - Stream Request
|
||||||
|
|
||||||
|
// MARK: Hostname and Port
|
||||||
|
|
||||||
|
/// Creates a `StreamRequest` for bidirectional streaming using the `hostname` and `port`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter hostName: The hostname of the server to connect to.
|
||||||
|
/// - parameter port: The port of the server to connect to.
|
||||||
|
///
|
||||||
|
/// - returns: The created `StreamRequest`.
|
||||||
|
@discardableResult
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
open func stream(withHostName hostName: String, port: Int) -> StreamRequest {
|
||||||
|
return stream(.stream(hostName: hostName, port: port))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: NetService
|
||||||
|
|
||||||
|
/// Creates a `StreamRequest` for bidirectional streaming using the `netService`.
|
||||||
|
///
|
||||||
|
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||||
|
///
|
||||||
|
/// - parameter netService: The net service used to identify the endpoint.
|
||||||
|
///
|
||||||
|
/// - returns: The created `StreamRequest`.
|
||||||
|
@discardableResult
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
open func stream(with netService: NetService) -> StreamRequest {
|
||||||
|
return stream(.netService(netService))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Private - Stream Implementation
|
||||||
|
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
private func stream(_ streamable: StreamRequest.Streamable) -> StreamRequest {
|
||||||
|
do {
|
||||||
|
let task = try streamable.task(session: session, adapter: adapter, queue: queue)
|
||||||
|
let request = StreamRequest(session: session, requestTask: .stream(streamable, task))
|
||||||
|
|
||||||
|
delegate[task] = request
|
||||||
|
|
||||||
|
if startRequestsImmediately { request.resume() }
|
||||||
|
|
||||||
|
return request
|
||||||
|
} catch {
|
||||||
|
return stream(failedWith: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||||
|
private func stream(failedWith error: Error) -> StreamRequest {
|
||||||
|
let stream = StreamRequest(session: session, requestTask: .stream(nil, nil), error: error)
|
||||||
|
if startRequestsImmediately { stream.resume() }
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// MARK: - Internal - Retry Request
|
||||||
|
|
||||||
|
func retry(_ request: Request) -> Bool {
|
||||||
|
guard let originalTask = request.originalTask else { return false }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
|
||||||
|
|
||||||
|
if let originalTask = request.task {
|
||||||
|
delegate[originalTask] = nil // removes the old request to avoid endless growth
|
||||||
|
}
|
||||||
|
|
||||||
|
request.delegate.task = task // resets all task delegate data
|
||||||
|
|
||||||
|
request.retryCount += 1
|
||||||
|
request.startTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
request.endTime = nil
|
||||||
|
|
||||||
|
task.resume()
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
request.delegate.error = error.underlyingAdaptError ?? error
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func allowRetrier(_ retrier: RequestRetrier, toRetry request: Request, with error: Error) {
|
||||||
|
DispatchQueue.utility.async { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
|
||||||
|
retrier.should(strongSelf, retry: request, with: error) { shouldRetry, timeDelay in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
|
||||||
|
guard shouldRetry else {
|
||||||
|
if strongSelf.startRequestsImmediately { request.resume() }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.utility.after(timeDelay) {
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
|
||||||
|
let retrySucceeded = strongSelf.retry(request)
|
||||||
|
|
||||||
|
if retrySucceeded, let task = request.task {
|
||||||
|
strongSelf.delegate[task] = request
|
||||||
|
} else {
|
||||||
|
if strongSelf.startRequestsImmediately { request.resume() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
456
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/TaskDelegate.swift
generated
Normal file
456
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/TaskDelegate.swift
generated
Normal file
@@ -0,0 +1,456 @@
|
|||||||
|
//
|
||||||
|
// TaskDelegate.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// The task delegate is responsible for handling all delegate callbacks for the underlying task as well as
|
||||||
|
/// executing all operations attached to the serial operation queue upon task completion.
|
||||||
|
open class TaskDelegate: NSObject {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
/// The serial operation queue used to execute all operations after the task completes.
|
||||||
|
public let queue: OperationQueue
|
||||||
|
|
||||||
|
/// The data returned by the server.
|
||||||
|
public var data: Data? { return nil }
|
||||||
|
|
||||||
|
/// The error generated throughout the lifecyle of the task.
|
||||||
|
public var error: Error?
|
||||||
|
|
||||||
|
var task: URLSessionTask? {
|
||||||
|
set {
|
||||||
|
taskLock.lock(); defer { taskLock.unlock() }
|
||||||
|
_task = newValue
|
||||||
|
}
|
||||||
|
get {
|
||||||
|
taskLock.lock(); defer { taskLock.unlock() }
|
||||||
|
return _task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var initialResponseTime: CFAbsoluteTime?
|
||||||
|
var credential: URLCredential?
|
||||||
|
var metrics: AnyObject? // URLSessionTaskMetrics
|
||||||
|
|
||||||
|
private var _task: URLSessionTask? {
|
||||||
|
didSet { reset() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private let taskLock = NSLock()
|
||||||
|
|
||||||
|
// MARK: Lifecycle
|
||||||
|
|
||||||
|
init(task: URLSessionTask?) {
|
||||||
|
_task = task
|
||||||
|
|
||||||
|
self.queue = {
|
||||||
|
let operationQueue = OperationQueue()
|
||||||
|
|
||||||
|
operationQueue.maxConcurrentOperationCount = 1
|
||||||
|
operationQueue.isSuspended = true
|
||||||
|
operationQueue.qualityOfService = .utility
|
||||||
|
|
||||||
|
return operationQueue
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset() {
|
||||||
|
error = nil
|
||||||
|
initialResponseTime = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: URLSessionTaskDelegate
|
||||||
|
|
||||||
|
var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
|
||||||
|
var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
|
||||||
|
var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
|
||||||
|
var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)?
|
||||||
|
|
||||||
|
@objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)
|
||||||
|
func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
task: URLSessionTask,
|
||||||
|
willPerformHTTPRedirection response: HTTPURLResponse,
|
||||||
|
newRequest request: URLRequest,
|
||||||
|
completionHandler: @escaping (URLRequest?) -> Void) {
|
||||||
|
var redirectRequest: URLRequest? = request
|
||||||
|
|
||||||
|
if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
|
||||||
|
redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(redirectRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(URLSession:task:didReceiveChallenge:completionHandler:)
|
||||||
|
func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
task: URLSessionTask,
|
||||||
|
didReceive challenge: URLAuthenticationChallenge,
|
||||||
|
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||||
|
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
|
||||||
|
var credential: URLCredential?
|
||||||
|
|
||||||
|
if let taskDidReceiveChallenge = taskDidReceiveChallenge {
|
||||||
|
(disposition, credential) = taskDidReceiveChallenge(session, task, challenge)
|
||||||
|
} else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
|
||||||
|
let host = challenge.protectionSpace.host
|
||||||
|
|
||||||
|
if
|
||||||
|
let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
|
||||||
|
let serverTrust = challenge.protectionSpace.serverTrust
|
||||||
|
{
|
||||||
|
if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
|
||||||
|
disposition = .useCredential
|
||||||
|
credential = URLCredential(trust: serverTrust)
|
||||||
|
} else {
|
||||||
|
disposition = .cancelAuthenticationChallenge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if challenge.previousFailureCount > 0 {
|
||||||
|
disposition = .rejectProtectionSpace
|
||||||
|
} else {
|
||||||
|
credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
|
||||||
|
|
||||||
|
if credential != nil {
|
||||||
|
disposition = .useCredential
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(disposition, credential)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(URLSession:task:needNewBodyStream:)
|
||||||
|
func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
task: URLSessionTask,
|
||||||
|
needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) {
|
||||||
|
var bodyStream: InputStream?
|
||||||
|
|
||||||
|
if let taskNeedNewBodyStream = taskNeedNewBodyStream {
|
||||||
|
bodyStream = taskNeedNewBodyStream(session, task)
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(bodyStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(URLSession:task:didCompleteWithError:)
|
||||||
|
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
||||||
|
if let taskDidCompleteWithError = taskDidCompleteWithError {
|
||||||
|
taskDidCompleteWithError(session, task, error)
|
||||||
|
} else {
|
||||||
|
if let error = error {
|
||||||
|
if self.error == nil { self.error = error }
|
||||||
|
|
||||||
|
if
|
||||||
|
let downloadDelegate = self as? DownloadTaskDelegate,
|
||||||
|
let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
|
||||||
|
{
|
||||||
|
downloadDelegate.resumeData = resumeData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.isSuspended = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
var dataTask: URLSessionDataTask { return task as! URLSessionDataTask }
|
||||||
|
|
||||||
|
override var data: Data? {
|
||||||
|
if dataStream != nil {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return mutableData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var progress: Progress
|
||||||
|
var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
|
||||||
|
|
||||||
|
var dataStream: ((_ data: Data) -> Void)?
|
||||||
|
|
||||||
|
private var totalBytesReceived: Int64 = 0
|
||||||
|
private var mutableData: Data
|
||||||
|
|
||||||
|
private var expectedContentLength: Int64?
|
||||||
|
|
||||||
|
// MARK: Lifecycle
|
||||||
|
|
||||||
|
override init(task: URLSessionTask?) {
|
||||||
|
mutableData = Data()
|
||||||
|
progress = Progress(totalUnitCount: 0)
|
||||||
|
|
||||||
|
super.init(task: task)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func reset() {
|
||||||
|
super.reset()
|
||||||
|
|
||||||
|
progress = Progress(totalUnitCount: 0)
|
||||||
|
totalBytesReceived = 0
|
||||||
|
mutableData = Data()
|
||||||
|
expectedContentLength = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: URLSessionDataDelegate
|
||||||
|
|
||||||
|
var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
|
||||||
|
var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
|
||||||
|
var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
|
||||||
|
var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
|
||||||
|
|
||||||
|
func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
dataTask: URLSessionDataTask,
|
||||||
|
didReceive response: URLResponse,
|
||||||
|
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
|
||||||
|
var disposition: URLSession.ResponseDisposition = .allow
|
||||||
|
|
||||||
|
expectedContentLength = response.expectedContentLength
|
||||||
|
|
||||||
|
if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
|
||||||
|
disposition = dataTaskDidReceiveResponse(session, dataTask, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(disposition)
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
dataTask: URLSessionDataTask,
|
||||||
|
didBecome downloadTask: URLSessionDownloadTask) {
|
||||||
|
dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
||||||
|
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
|
||||||
|
|
||||||
|
if let dataTaskDidReceiveData = dataTaskDidReceiveData {
|
||||||
|
dataTaskDidReceiveData(session, dataTask, data)
|
||||||
|
} else {
|
||||||
|
if let dataStream = dataStream {
|
||||||
|
dataStream(data)
|
||||||
|
} else {
|
||||||
|
mutableData.append(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytesReceived = Int64(data.count)
|
||||||
|
totalBytesReceived += bytesReceived
|
||||||
|
let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
|
||||||
|
|
||||||
|
progress.totalUnitCount = totalBytesExpected
|
||||||
|
progress.completedUnitCount = totalBytesReceived
|
||||||
|
|
||||||
|
if let progressHandler = progressHandler {
|
||||||
|
progressHandler.queue.async { progressHandler.closure(self.progress) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
dataTask: URLSessionDataTask,
|
||||||
|
willCacheResponse proposedResponse: CachedURLResponse,
|
||||||
|
completionHandler: @escaping (CachedURLResponse?) -> Void) {
|
||||||
|
var cachedResponse: CachedURLResponse? = proposedResponse
|
||||||
|
|
||||||
|
if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
|
||||||
|
cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(cachedResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask }
|
||||||
|
|
||||||
|
var progress: Progress
|
||||||
|
var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
|
||||||
|
|
||||||
|
var resumeData: Data?
|
||||||
|
override var data: Data? { return resumeData }
|
||||||
|
|
||||||
|
var destination: DownloadRequest.DownloadFileDestination?
|
||||||
|
|
||||||
|
var temporaryURL: URL?
|
||||||
|
var destinationURL: URL?
|
||||||
|
|
||||||
|
var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL }
|
||||||
|
|
||||||
|
// MARK: Lifecycle
|
||||||
|
|
||||||
|
override init(task: URLSessionTask?) {
|
||||||
|
progress = Progress(totalUnitCount: 0)
|
||||||
|
super.init(task: task)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func reset() {
|
||||||
|
super.reset()
|
||||||
|
|
||||||
|
progress = Progress(totalUnitCount: 0)
|
||||||
|
resumeData = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: URLSessionDownloadDelegate
|
||||||
|
|
||||||
|
var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)?
|
||||||
|
var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
|
||||||
|
var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
|
||||||
|
|
||||||
|
func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
downloadTask: URLSessionDownloadTask,
|
||||||
|
didFinishDownloadingTo location: URL) {
|
||||||
|
temporaryURL = location
|
||||||
|
|
||||||
|
guard
|
||||||
|
let destination = destination,
|
||||||
|
let response = downloadTask.response as? HTTPURLResponse
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
let result = destination(location, response)
|
||||||
|
let destinationURL = result.destinationURL
|
||||||
|
let options = result.options
|
||||||
|
|
||||||
|
self.destinationURL = destinationURL
|
||||||
|
|
||||||
|
do {
|
||||||
|
if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) {
|
||||||
|
try FileManager.default.removeItem(at: destinationURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.contains(.createIntermediateDirectories) {
|
||||||
|
let directory = destinationURL.deletingLastPathComponent()
|
||||||
|
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
try FileManager.default.moveItem(at: location, to: destinationURL)
|
||||||
|
} catch {
|
||||||
|
self.error = error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
downloadTask: URLSessionDownloadTask,
|
||||||
|
didWriteData bytesWritten: Int64,
|
||||||
|
totalBytesWritten: Int64,
|
||||||
|
totalBytesExpectedToWrite: Int64) {
|
||||||
|
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
|
||||||
|
|
||||||
|
if let downloadTaskDidWriteData = downloadTaskDidWriteData {
|
||||||
|
downloadTaskDidWriteData(
|
||||||
|
session,
|
||||||
|
downloadTask,
|
||||||
|
bytesWritten,
|
||||||
|
totalBytesWritten,
|
||||||
|
totalBytesExpectedToWrite
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
progress.totalUnitCount = totalBytesExpectedToWrite
|
||||||
|
progress.completedUnitCount = totalBytesWritten
|
||||||
|
|
||||||
|
if let progressHandler = progressHandler {
|
||||||
|
progressHandler.queue.async { progressHandler.closure(self.progress) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
downloadTask: URLSessionDownloadTask,
|
||||||
|
didResumeAtOffset fileOffset: Int64,
|
||||||
|
expectedTotalBytes: Int64) {
|
||||||
|
if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
|
||||||
|
downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
|
||||||
|
} else {
|
||||||
|
progress.totalUnitCount = expectedTotalBytes
|
||||||
|
progress.completedUnitCount = fileOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
class UploadTaskDelegate: DataTaskDelegate {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask }
|
||||||
|
|
||||||
|
var uploadProgress: Progress
|
||||||
|
var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
|
||||||
|
|
||||||
|
// MARK: Lifecycle
|
||||||
|
|
||||||
|
override init(task: URLSessionTask?) {
|
||||||
|
uploadProgress = Progress(totalUnitCount: 0)
|
||||||
|
super.init(task: task)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func reset() {
|
||||||
|
super.reset()
|
||||||
|
uploadProgress = Progress(totalUnitCount: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: URLSessionTaskDelegate
|
||||||
|
|
||||||
|
var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
|
||||||
|
|
||||||
|
func URLSession(
|
||||||
|
_ session: URLSession,
|
||||||
|
task: URLSessionTask,
|
||||||
|
didSendBodyData bytesSent: Int64,
|
||||||
|
totalBytesSent: Int64,
|
||||||
|
totalBytesExpectedToSend: Int64) {
|
||||||
|
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
|
||||||
|
|
||||||
|
if let taskDidSendBodyData = taskDidSendBodyData {
|
||||||
|
taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
|
||||||
|
} else {
|
||||||
|
uploadProgress.totalUnitCount = totalBytesExpectedToSend
|
||||||
|
uploadProgress.completedUnitCount = totalBytesSent
|
||||||
|
|
||||||
|
if let uploadProgressHandler = uploadProgressHandler {
|
||||||
|
uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
135
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/Timeline.swift
generated
Normal file
135
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/Timeline.swift
generated
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
//
|
||||||
|
// Timeline.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Responsible for computing the timing metrics for the complete lifecycle of a `Request`.
|
||||||
|
public struct Timeline {
|
||||||
|
/// The time the request was initialized.
|
||||||
|
public let requestStartTime: CFAbsoluteTime
|
||||||
|
|
||||||
|
/// The time the first bytes were received from or sent to the server.
|
||||||
|
public let initialResponseTime: CFAbsoluteTime
|
||||||
|
|
||||||
|
/// The time when the request was completed.
|
||||||
|
public let requestCompletedTime: CFAbsoluteTime
|
||||||
|
|
||||||
|
/// The time when the response serialization was completed.
|
||||||
|
public let serializationCompletedTime: CFAbsoluteTime
|
||||||
|
|
||||||
|
/// The time interval in seconds from the time the request started to the initial response from the server.
|
||||||
|
public let latency: TimeInterval
|
||||||
|
|
||||||
|
/// The time interval in seconds from the time the request started to the time the request completed.
|
||||||
|
public let requestDuration: TimeInterval
|
||||||
|
|
||||||
|
/// The time interval in seconds from the time the request completed to the time response serialization completed.
|
||||||
|
public let serializationDuration: TimeInterval
|
||||||
|
|
||||||
|
/// The time interval in seconds from the time the request started to the time response serialization completed.
|
||||||
|
public let totalDuration: TimeInterval
|
||||||
|
|
||||||
|
/// Creates a new `Timeline` instance with the specified request times.
|
||||||
|
///
|
||||||
|
/// - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`.
|
||||||
|
/// - parameter initialResponseTime: The time the first bytes were received from or sent to the server.
|
||||||
|
/// Defaults to `0.0`.
|
||||||
|
/// - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`.
|
||||||
|
/// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults
|
||||||
|
/// to `0.0`.
|
||||||
|
///
|
||||||
|
/// - returns: The new `Timeline` instance.
|
||||||
|
public init(
|
||||||
|
requestStartTime: CFAbsoluteTime = 0.0,
|
||||||
|
initialResponseTime: CFAbsoluteTime = 0.0,
|
||||||
|
requestCompletedTime: CFAbsoluteTime = 0.0,
|
||||||
|
serializationCompletedTime: CFAbsoluteTime = 0.0) {
|
||||||
|
self.requestStartTime = requestStartTime
|
||||||
|
self.initialResponseTime = initialResponseTime
|
||||||
|
self.requestCompletedTime = requestCompletedTime
|
||||||
|
self.serializationCompletedTime = serializationCompletedTime
|
||||||
|
|
||||||
|
self.latency = initialResponseTime - requestStartTime
|
||||||
|
self.requestDuration = requestCompletedTime - requestStartTime
|
||||||
|
self.serializationDuration = serializationCompletedTime - requestCompletedTime
|
||||||
|
self.totalDuration = serializationCompletedTime - requestStartTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - CustomStringConvertible
|
||||||
|
|
||||||
|
extension Timeline: CustomStringConvertible {
|
||||||
|
/// The textual representation used when written to an output stream, which includes the latency, the request
|
||||||
|
/// duration and the total duration.
|
||||||
|
public var description: String {
|
||||||
|
let latency = String(format: "%.3f", self.latency)
|
||||||
|
let requestDuration = String(format: "%.3f", self.requestDuration)
|
||||||
|
let serializationDuration = String(format: "%.3f", self.serializationDuration)
|
||||||
|
let totalDuration = String(format: "%.3f", self.totalDuration)
|
||||||
|
|
||||||
|
// NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
|
||||||
|
// fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
|
||||||
|
let timings = [
|
||||||
|
"\"Latency\": " + latency + " secs",
|
||||||
|
"\"Request Duration\": " + requestDuration + " secs",
|
||||||
|
"\"Serialization Duration\": " + serializationDuration + " secs",
|
||||||
|
"\"Total Duration\": " + totalDuration + " secs"
|
||||||
|
]
|
||||||
|
|
||||||
|
return "Timeline: { " + timings.joined(separator: ", ") + " }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - CustomDebugStringConvertible
|
||||||
|
|
||||||
|
extension Timeline: CustomDebugStringConvertible {
|
||||||
|
/// The textual representation used when written to an output stream, which includes the request start time, the
|
||||||
|
/// initial response time, the request completed time, the serialization completed time, the latency, the request
|
||||||
|
/// duration and the total duration.
|
||||||
|
public var debugDescription: String {
|
||||||
|
let requestStartTime = String(format: "%.3f", self.requestStartTime)
|
||||||
|
let initialResponseTime = String(format: "%.3f", self.initialResponseTime)
|
||||||
|
let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime)
|
||||||
|
let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime)
|
||||||
|
let latency = String(format: "%.3f", self.latency)
|
||||||
|
let requestDuration = String(format: "%.3f", self.requestDuration)
|
||||||
|
let serializationDuration = String(format: "%.3f", self.serializationDuration)
|
||||||
|
let totalDuration = String(format: "%.3f", self.totalDuration)
|
||||||
|
|
||||||
|
// NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
|
||||||
|
// fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
|
||||||
|
let timings = [
|
||||||
|
"\"Request Start Time\": " + requestStartTime,
|
||||||
|
"\"Initial Response Time\": " + initialResponseTime,
|
||||||
|
"\"Request Completed Time\": " + requestCompletedTime,
|
||||||
|
"\"Serialization Completed Time\": " + serializationCompletedTime,
|
||||||
|
"\"Latency\": " + latency + " secs",
|
||||||
|
"\"Request Duration\": " + requestDuration + " secs",
|
||||||
|
"\"Serialization Duration\": " + serializationDuration + " secs",
|
||||||
|
"\"Total Duration\": " + totalDuration + " secs"
|
||||||
|
]
|
||||||
|
|
||||||
|
return "Timeline: { " + timings.joined(separator: ", ") + " }"
|
||||||
|
}
|
||||||
|
}
|
||||||
319
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/Validation.swift
generated
Normal file
319
samples/client/petstore/swift4/default/SwaggerClientTests/Pods/Alamofire/Source/Validation.swift
generated
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
//
|
||||||
|
// Validation.swift
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014 Alamofire Software Foundation (http://alamofire.org/)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Request {
|
||||||
|
|
||||||
|
// MARK: Helper Types
|
||||||
|
|
||||||
|
fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason
|
||||||
|
|
||||||
|
/// Used to represent whether validation was successful or encountered an error resulting in a failure.
|
||||||
|
///
|
||||||
|
/// - success: The validation was successful.
|
||||||
|
/// - failure: The validation failed encountering the provided error.
|
||||||
|
public enum ValidationResult {
|
||||||
|
case success
|
||||||
|
case failure(Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate struct MIMEType {
|
||||||
|
let type: String
|
||||||
|
let subtype: String
|
||||||
|
|
||||||
|
var isWildcard: Bool { return type == "*" && subtype == "*" }
|
||||||
|
|
||||||
|
init?(_ string: String) {
|
||||||
|
let components: [String] = {
|
||||||
|
let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
||||||
|
#if swift(>=3.2)
|
||||||
|
let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)]
|
||||||
|
#else
|
||||||
|
let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return split.components(separatedBy: "/")
|
||||||
|
}()
|
||||||
|
|
||||||
|
if let type = components.first, let subtype = components.last {
|
||||||
|
self.type = type
|
||||||
|
self.subtype = subtype
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func matches(_ mime: MIMEType) -> Bool {
|
||||||
|
switch (type, subtype) {
|
||||||
|
case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"):
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
fileprivate var acceptableStatusCodes: [Int] { return Array(200..<300) }
|
||||||
|
|
||||||
|
fileprivate var acceptableContentTypes: [String] {
|
||||||
|
if let accept = request?.value(forHTTPHeaderField: "Accept") {
|
||||||
|
return accept.components(separatedBy: ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ["*/*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Status Code
|
||||||
|
|
||||||
|
fileprivate func validate<S: Sequence>(
|
||||||
|
statusCode acceptableStatusCodes: S,
|
||||||
|
response: HTTPURLResponse)
|
||||||
|
-> ValidationResult
|
||||||
|
where S.Iterator.Element == Int {
|
||||||
|
if acceptableStatusCodes.contains(response.statusCode) {
|
||||||
|
return .success
|
||||||
|
} else {
|
||||||
|
let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode)
|
||||||
|
return .failure(AFError.responseValidationFailed(reason: reason))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Content Type
|
||||||
|
|
||||||
|
fileprivate func validate<S: Sequence>(
|
||||||
|
contentType acceptableContentTypes: S,
|
||||||
|
response: HTTPURLResponse,
|
||||||
|
data: Data?)
|
||||||
|
-> ValidationResult
|
||||||
|
where S.Iterator.Element == String {
|
||||||
|
guard let data = data, data.count > 0 else { return .success }
|
||||||
|
|
||||||
|
guard
|
||||||
|
let responseContentType = response.mimeType,
|
||||||
|
let responseMIMEType = MIMEType(responseContentType)
|
||||||
|
else {
|
||||||
|
for contentType in acceptableContentTypes {
|
||||||
|
if let mimeType = MIMEType(contentType), mimeType.isWildcard {
|
||||||
|
return .success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let error: AFError = {
|
||||||
|
let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes))
|
||||||
|
return AFError.responseValidationFailed(reason: reason)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return .failure(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
for contentType in acceptableContentTypes {
|
||||||
|
if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) {
|
||||||
|
return .success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let error: AFError = {
|
||||||
|
let reason: ErrorReason = .unacceptableContentType(
|
||||||
|
acceptableContentTypes: Array(acceptableContentTypes),
|
||||||
|
responseContentType: responseContentType
|
||||||
|
)
|
||||||
|
|
||||||
|
return AFError.responseValidationFailed(reason: reason)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return .failure(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
extension DataRequest {
|
||||||
|
/// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the
|
||||||
|
/// request was valid.
|
||||||
|
public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult
|
||||||
|
|
||||||
|
/// Validates the request, using the specified closure.
|
||||||
|
///
|
||||||
|
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||||
|
///
|
||||||
|
/// - parameter validation: A closure to validate the request.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func validate(_ validation: @escaping Validation) -> Self {
|
||||||
|
let validationExecution: () -> Void = { [unowned self] in
|
||||||
|
if
|
||||||
|
let response = self.response,
|
||||||
|
self.delegate.error == nil,
|
||||||
|
case let .failure(error) = validation(self.request, response, self.delegate.data)
|
||||||
|
{
|
||||||
|
self.delegate.error = error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validations.append(validationExecution)
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates that the response has a status code in the specified sequence.
|
||||||
|
///
|
||||||
|
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||||
|
///
|
||||||
|
/// - parameter range: The range of acceptable status codes.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
|
||||||
|
return validate { [unowned self] _, response, _ in
|
||||||
|
return self.validate(statusCode: acceptableStatusCodes, response: response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates that the response has a content type in the specified sequence.
|
||||||
|
///
|
||||||
|
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||||
|
///
|
||||||
|
/// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func validate<S: Sequence>(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String {
|
||||||
|
return validate { [unowned self] _, response, data in
|
||||||
|
return self.validate(contentType: acceptableContentTypes, response: response, data: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
|
||||||
|
/// type matches any specified in the Accept HTTP header field.
|
||||||
|
///
|
||||||
|
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func validate() -> Self {
|
||||||
|
let contentTypes = { [unowned self] in
|
||||||
|
self.acceptableContentTypes
|
||||||
|
}
|
||||||
|
return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
extension DownloadRequest {
|
||||||
|
/// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a
|
||||||
|
/// destination URL, and returns whether the request was valid.
|
||||||
|
public typealias Validation = (
|
||||||
|
_ request: URLRequest?,
|
||||||
|
_ response: HTTPURLResponse,
|
||||||
|
_ temporaryURL: URL?,
|
||||||
|
_ destinationURL: URL?)
|
||||||
|
-> ValidationResult
|
||||||
|
|
||||||
|
/// Validates the request, using the specified closure.
|
||||||
|
///
|
||||||
|
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||||
|
///
|
||||||
|
/// - parameter validation: A closure to validate the request.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func validate(_ validation: @escaping Validation) -> Self {
|
||||||
|
let validationExecution: () -> Void = { [unowned self] in
|
||||||
|
let request = self.request
|
||||||
|
let temporaryURL = self.downloadDelegate.temporaryURL
|
||||||
|
let destinationURL = self.downloadDelegate.destinationURL
|
||||||
|
|
||||||
|
if
|
||||||
|
let response = self.response,
|
||||||
|
self.delegate.error == nil,
|
||||||
|
case let .failure(error) = validation(request, response, temporaryURL, destinationURL)
|
||||||
|
{
|
||||||
|
self.delegate.error = error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validations.append(validationExecution)
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates that the response has a status code in the specified sequence.
|
||||||
|
///
|
||||||
|
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||||
|
///
|
||||||
|
/// - parameter range: The range of acceptable status codes.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
|
||||||
|
return validate { [unowned self] _, response, _, _ in
|
||||||
|
return self.validate(statusCode: acceptableStatusCodes, response: response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates that the response has a content type in the specified sequence.
|
||||||
|
///
|
||||||
|
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||||
|
///
|
||||||
|
/// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func validate<S: Sequence>(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String {
|
||||||
|
return validate { [unowned self] _, response, _, _ in
|
||||||
|
let fileURL = self.downloadDelegate.fileURL
|
||||||
|
|
||||||
|
guard let validFileURL = fileURL else {
|
||||||
|
return .failure(AFError.responseValidationFailed(reason: .dataFileNil))
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let data = try Data(contentsOf: validFileURL)
|
||||||
|
return self.validate(contentType: acceptableContentTypes, response: response, data: data)
|
||||||
|
} catch {
|
||||||
|
return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
|
||||||
|
/// type matches any specified in the Accept HTTP header field.
|
||||||
|
///
|
||||||
|
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||||
|
///
|
||||||
|
/// - returns: The request.
|
||||||
|
@discardableResult
|
||||||
|
public func validate() -> Self {
|
||||||
|
let contentTypes = { [unowned self] in
|
||||||
|
self.acceptableContentTypes
|
||||||
|
}
|
||||||
|
return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,6 +76,8 @@ class DateFormatTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testCodableAlwaysResultsInUTCEncodedDate() {
|
func testCodableAlwaysResultsInUTCEncodedDate() {
|
||||||
|
CodableHelper.jsonEncoder.outputFormatting.remove(.prettyPrinted)
|
||||||
|
|
||||||
let jsonData = "{\"date\":\"1970-01-01T00:00:00.000Z\"}".data(using: .utf8)!
|
let jsonData = "{\"date\":\"1970-01-01T00:00:00.000Z\"}".data(using: .utf8)!
|
||||||
let decodeResult = CodableHelper.decode(DateTest.self, from: jsonData)
|
let decodeResult = CodableHelper.decode(DateTest.self, from: jsonData)
|
||||||
XCTAssert(decodeResult.decodableObj != nil && decodeResult.error == nil)
|
XCTAssert(decodeResult.decodableObj != nil && decodeResult.error == nil)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
4.2.2-SNAPSHOT
|
4.2.3-SNAPSHOT
|
||||||
@@ -9,11 +9,11 @@ let package = Package(
|
|||||||
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
// Products define the executables and libraries produced by a package, and make them visible to other packages.
|
||||||
.library(
|
.library(
|
||||||
name: "PetstoreClient",
|
name: "PetstoreClient",
|
||||||
targets: ["PetstoreClient"])
|
targets: ["PetstoreClient"]),
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
// Dependencies declare other packages that this package depends on.
|
// Dependencies declare other packages that this package depends on.
|
||||||
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.9.0")
|
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.9.0"),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||||
@@ -22,6 +22,6 @@ let package = Package(
|
|||||||
name: "PetstoreClient",
|
name: "PetstoreClient",
|
||||||
dependencies: ["Alamofire"],
|
dependencies: ["Alamofire"],
|
||||||
path: "PetstoreClient/Classes"
|
path: "PetstoreClient/Classes"
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ internal struct APIHelper {
|
|||||||
|
|
||||||
internal static func rejectNilHeaders(_ source: [String:Any?]) -> [String:String] {
|
internal static func rejectNilHeaders(_ source: [String:Any?]) -> [String:String] {
|
||||||
return source.reduce(into: [String: String]()) { (result, item) in
|
return source.reduce(into: [String: String]()) { (result, item) in
|
||||||
if let collection = item.value as? [Any?] {
|
if let collection = item.value as? Array<Any?> {
|
||||||
result[item.key] = collection.filter({ $0 != nil }).map{ "\($0!)" }.joined(separator: ",")
|
result[item.key] = collection.filter({ $0 != nil }).map{ "\($0!)" }.joined(separator: ",")
|
||||||
} else if let value: Any = item.value {
|
} else if let value: Any = item.value {
|
||||||
result[item.key] = "\(value)"
|
result[item.key] = "\(value)"
|
||||||
@@ -46,7 +46,7 @@ internal struct APIHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal static func mapValueToPathItem(_ source: Any) -> Any {
|
internal static func mapValueToPathItem(_ source: Any) -> Any {
|
||||||
if let collection = source as? [Any?] {
|
if let collection = source as? Array<Any?> {
|
||||||
return collection.filter({ $0 != nil }).map({"\($0!)"}).joined(separator: ",")
|
return collection.filter({ $0 != nil }).map({"\($0!)"}).joined(separator: ",")
|
||||||
}
|
}
|
||||||
return source
|
return source
|
||||||
@@ -54,7 +54,7 @@ internal struct APIHelper {
|
|||||||
|
|
||||||
internal static func mapValuesToQueryItems(_ source: [String:Any?]) -> [URLQueryItem]? {
|
internal static func mapValuesToQueryItems(_ source: [String:Any?]) -> [URLQueryItem]? {
|
||||||
let destination = source.filter({ $0.value != nil}).reduce(into: [URLQueryItem]()) { (result, item) in
|
let destination = source.filter({ $0.value != nil}).reduce(into: [URLQueryItem]()) { (result, item) in
|
||||||
if let collection = item.value as? [Any?] {
|
if let collection = item.value as? Array<Any?> {
|
||||||
let value = collection.filter({ $0 != nil }).map({"\($0!)"}).joined(separator: ",")
|
let value = collection.filter({ $0 != nil }).map({"\($0!)"}).joined(separator: ",")
|
||||||
result.append(URLQueryItem(name: item.key, value: value))
|
result.append(URLQueryItem(name: item.key, value: value))
|
||||||
} else if let value = item.value {
|
} else if let value = item.value {
|
||||||
@@ -68,3 +68,4 @@ internal struct APIHelper {
|
|||||||
return destination
|
return destination
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ internal class PetstoreClientAPI {
|
|||||||
internal static var credential: URLCredential?
|
internal static var credential: URLCredential?
|
||||||
internal static var customHeaders: [String:String] = [:]
|
internal static var customHeaders: [String:String] = [:]
|
||||||
internal static var requestBuilderFactory: RequestBuilderFactory = AlamofireRequestBuilderFactory()
|
internal static var requestBuilderFactory: RequestBuilderFactory = AlamofireRequestBuilderFactory()
|
||||||
|
internal static var apiResponseQueue: DispatchQueue = .main
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class RequestBuilder<T> {
|
internal class RequestBuilder<T> {
|
||||||
@@ -22,7 +23,7 @@ internal class RequestBuilder<T> {
|
|||||||
internal let URLString: String
|
internal let URLString: String
|
||||||
|
|
||||||
/// Optional block to obtain a reference to the request's progress instance when available.
|
/// Optional block to obtain a reference to the request's progress instance when available.
|
||||||
internal var onProgressReady: ((Progress) -> Void)?
|
internal var onProgressReady: ((Progress) -> ())?
|
||||||
|
|
||||||
required internal init(method: String, URLString: String, parameters: [String:Any]?, isBody: Bool, headers: [String:String] = [:]) {
|
required internal init(method: String, URLString: String, parameters: [String:Any]?, isBody: Bool, headers: [String:String] = [:]) {
|
||||||
self.method = method
|
self.method = method
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
internal class AnotherFakeAPI {
|
internal class AnotherFakeAPI {
|
||||||
/**
|
/**
|
||||||
To test special tags
|
To test special tags
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
internal class FakeAPI {
|
internal class FakeAPI {
|
||||||
/**
|
/**
|
||||||
|
|
||||||
@@ -130,7 +132,7 @@ internal class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func testBodyWithFileSchema(body: FileSchemaTestClass, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func testBodyWithFileSchema(body: FileSchemaTestClass, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testBodyWithFileSchemaWithRequestBuilder(body: body).execute { (_, error) -> Void in
|
testBodyWithFileSchemaWithRequestBuilder(body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -164,7 +166,7 @@ internal class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func testBodyWithQueryParams(query: String, body: User, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func testBodyWithQueryParams(query: String, body: User, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testBodyWithQueryParamsWithRequestBuilder(query: query, body: body).execute { (_, error) -> Void in
|
testBodyWithQueryParamsWithRequestBuilder(query: query, body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -186,7 +188,7 @@ internal class FakeAPI {
|
|||||||
|
|
||||||
var url = URLComponents(string: URLString)
|
var url = URLComponents(string: URLString)
|
||||||
url?.queryItems = APIHelper.mapValuesToQueryItems([
|
url?.queryItems = APIHelper.mapValuesToQueryItems([
|
||||||
"query": query
|
"query": query.encodeToJSON()
|
||||||
])
|
])
|
||||||
|
|
||||||
let requestBuilder: RequestBuilder<Void>.Type = PetstoreClientAPI.requestBuilderFactory.getNonDecodableBuilder()
|
let requestBuilder: RequestBuilder<Void>.Type = PetstoreClientAPI.requestBuilderFactory.getNonDecodableBuilder()
|
||||||
@@ -245,7 +247,7 @@ internal class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func testEndpointParameters(number: Double, double: Double, patternWithoutDelimiter: String, byte: Data, integer: Int? = nil, int32: Int? = nil, int64: Int64? = nil, float: Float? = nil, string: String? = nil, binary: URL? = nil, date: Date? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func testEndpointParameters(number: Double, double: Double, patternWithoutDelimiter: String, byte: Data, integer: Int? = nil, int32: Int? = nil, int64: Int64? = nil, float: Float? = nil, string: String? = nil, binary: URL? = nil, date: Date? = nil, dateTime: Date? = nil, password: String? = nil, callback: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testEndpointParametersWithRequestBuilder(number: number, double: double, patternWithoutDelimiter: patternWithoutDelimiter, byte: byte, integer: integer, int32: int32, int64: int64, float: float, string: string, binary: binary, date: date, dateTime: dateTime, password: password, callback: callback).execute { (_, error) -> Void in
|
testEndpointParametersWithRequestBuilder(number: number, double: double, patternWithoutDelimiter: patternWithoutDelimiter, byte: byte, integer: integer, int32: int32, int64: int64, float: float, string: string, binary: binary, date: date, dateTime: dateTime, password: password, callback: callback).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -284,17 +286,17 @@ internal class FakeAPI {
|
|||||||
"integer": integer?.encodeToJSON(),
|
"integer": integer?.encodeToJSON(),
|
||||||
"int32": int32?.encodeToJSON(),
|
"int32": int32?.encodeToJSON(),
|
||||||
"int64": int64?.encodeToJSON(),
|
"int64": int64?.encodeToJSON(),
|
||||||
"number": number,
|
"number": number.encodeToJSON(),
|
||||||
"float": float,
|
"float": float?.encodeToJSON(),
|
||||||
"double": double,
|
"double": double.encodeToJSON(),
|
||||||
"string": string,
|
"string": string?.encodeToJSON(),
|
||||||
"pattern_without_delimiter": patternWithoutDelimiter,
|
"pattern_without_delimiter": patternWithoutDelimiter.encodeToJSON(),
|
||||||
"byte": byte,
|
"byte": byte.encodeToJSON(),
|
||||||
"binary": binary,
|
"binary": binary?.encodeToJSON(),
|
||||||
"date": date?.encodeToJSON(),
|
"date": date?.encodeToJSON(),
|
||||||
"dateTime": dateTime?.encodeToJSON(),
|
"dateTime": dateTime?.encodeToJSON(),
|
||||||
"password": password,
|
"password": password?.encodeToJSON(),
|
||||||
"callback": callback
|
"callback": callback?.encodeToJSON()
|
||||||
]
|
]
|
||||||
|
|
||||||
let nonNullParameters = APIHelper.rejectNil(formParams)
|
let nonNullParameters = APIHelper.rejectNil(formParams)
|
||||||
@@ -388,7 +390,7 @@ internal class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func testEnumParameters(enumHeaderStringArray: [String]? = nil, enumHeaderString: EnumHeaderString_testEnumParameters? = nil, enumQueryStringArray: [String]? = nil, enumQueryString: EnumQueryString_testEnumParameters? = nil, enumQueryInteger: EnumQueryInteger_testEnumParameters? = nil, enumQueryDouble: EnumQueryDouble_testEnumParameters? = nil, enumFormStringArray: [String]? = nil, enumFormString: EnumFormString_testEnumParameters? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func testEnumParameters(enumHeaderStringArray: [String]? = nil, enumHeaderString: EnumHeaderString_testEnumParameters? = nil, enumQueryStringArray: [String]? = nil, enumQueryString: EnumQueryString_testEnumParameters? = nil, enumQueryInteger: EnumQueryInteger_testEnumParameters? = nil, enumQueryDouble: EnumQueryDouble_testEnumParameters? = nil, enumFormStringArray: [String]? = nil, enumFormString: EnumFormString_testEnumParameters? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testEnumParametersWithRequestBuilder(enumHeaderStringArray: enumHeaderStringArray, enumHeaderString: enumHeaderString, enumQueryStringArray: enumQueryStringArray, enumQueryString: enumQueryString, enumQueryInteger: enumQueryInteger, enumQueryDouble: enumQueryDouble, enumFormStringArray: enumFormStringArray, enumFormString: enumFormString).execute { (_, error) -> Void in
|
testEnumParametersWithRequestBuilder(enumHeaderStringArray: enumHeaderStringArray, enumHeaderString: enumHeaderString, enumQueryStringArray: enumQueryStringArray, enumQueryString: enumQueryString, enumQueryInteger: enumQueryInteger, enumQueryDouble: enumQueryDouble, enumFormStringArray: enumFormStringArray, enumFormString: enumFormString).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -415,8 +417,8 @@ internal class FakeAPI {
|
|||||||
let path = "/fake"
|
let path = "/fake"
|
||||||
let URLString = PetstoreClientAPI.basePath + path
|
let URLString = PetstoreClientAPI.basePath + path
|
||||||
let formParams: [String:Any?] = [
|
let formParams: [String:Any?] = [
|
||||||
"enum_form_string_array": enumFormStringArray,
|
"enum_form_string_array": enumFormStringArray?.encodeToJSON(),
|
||||||
"enum_form_string": enumFormString?.rawValue
|
"enum_form_string": enumFormString?.encodeToJSON()
|
||||||
]
|
]
|
||||||
|
|
||||||
let nonNullParameters = APIHelper.rejectNil(formParams)
|
let nonNullParameters = APIHelper.rejectNil(formParams)
|
||||||
@@ -424,14 +426,14 @@ internal class FakeAPI {
|
|||||||
|
|
||||||
var url = URLComponents(string: URLString)
|
var url = URLComponents(string: URLString)
|
||||||
url?.queryItems = APIHelper.mapValuesToQueryItems([
|
url?.queryItems = APIHelper.mapValuesToQueryItems([
|
||||||
"enum_query_string_array": enumQueryStringArray,
|
"enum_query_string_array": enumQueryStringArray?.encodeToJSON(),
|
||||||
"enum_query_string": enumQueryString?.rawValue,
|
"enum_query_string": enumQueryString?.encodeToJSON(),
|
||||||
"enum_query_integer": enumQueryInteger?.rawValue,
|
"enum_query_integer": enumQueryInteger?.encodeToJSON(),
|
||||||
"enum_query_double": enumQueryDouble?.rawValue
|
"enum_query_double": enumQueryDouble?.encodeToJSON()
|
||||||
])
|
])
|
||||||
let nillableHeaders: [String: Any?] = [
|
let nillableHeaders: [String: Any?] = [
|
||||||
"enum_header_string_array": enumHeaderStringArray,
|
"enum_header_string_array": enumHeaderStringArray?.encodeToJSON(),
|
||||||
"enum_header_string": enumHeaderString?.rawValue
|
"enum_header_string": enumHeaderString?.encodeToJSON()
|
||||||
]
|
]
|
||||||
let headerParameters = APIHelper.rejectNilHeaders(nillableHeaders)
|
let headerParameters = APIHelper.rejectNilHeaders(nillableHeaders)
|
||||||
|
|
||||||
@@ -452,7 +454,7 @@ internal class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func testGroupParameters(requiredStringGroup: Int, requiredBooleanGroup: Bool, requiredInt64Group: Int64, stringGroup: Int? = nil, booleanGroup: Bool? = nil, int64Group: Int64? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func testGroupParameters(requiredStringGroup: Int, requiredBooleanGroup: Bool, requiredInt64Group: Int64, stringGroup: Int? = nil, booleanGroup: Bool? = nil, int64Group: Int64? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testGroupParametersWithRequestBuilder(requiredStringGroup: requiredStringGroup, requiredBooleanGroup: requiredBooleanGroup, requiredInt64Group: requiredInt64Group, stringGroup: stringGroup, booleanGroup: booleanGroup, int64Group: int64Group).execute { (_, error) -> Void in
|
testGroupParametersWithRequestBuilder(requiredStringGroup: requiredStringGroup, requiredBooleanGroup: requiredBooleanGroup, requiredInt64Group: requiredInt64Group, stringGroup: stringGroup, booleanGroup: booleanGroup, int64Group: int64Group).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -486,8 +488,8 @@ internal class FakeAPI {
|
|||||||
"int64_group": int64Group?.encodeToJSON()
|
"int64_group": int64Group?.encodeToJSON()
|
||||||
])
|
])
|
||||||
let nillableHeaders: [String: Any?] = [
|
let nillableHeaders: [String: Any?] = [
|
||||||
"required_boolean_group": requiredBooleanGroup,
|
"required_boolean_group": requiredBooleanGroup.encodeToJSON(),
|
||||||
"boolean_group": booleanGroup
|
"boolean_group": booleanGroup?.encodeToJSON()
|
||||||
]
|
]
|
||||||
let headerParameters = APIHelper.rejectNilHeaders(nillableHeaders)
|
let headerParameters = APIHelper.rejectNilHeaders(nillableHeaders)
|
||||||
|
|
||||||
@@ -503,7 +505,7 @@ internal class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func testInlineAdditionalProperties(param: [String:String], completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func testInlineAdditionalProperties(param: [String:String], completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testInlineAdditionalPropertiesWithRequestBuilder(param: param).execute { (_, error) -> Void in
|
testInlineAdditionalPropertiesWithRequestBuilder(param: param).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -538,7 +540,7 @@ internal class FakeAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func testJsonFormData(param: String, param2: String, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func testJsonFormData(param: String, param2: String, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
testJsonFormDataWithRequestBuilder(param: param, param2: param2).execute { (_, error) -> Void in
|
testJsonFormDataWithRequestBuilder(param: param, param2: param2).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -558,8 +560,8 @@ internal class FakeAPI {
|
|||||||
let path = "/fake/jsonFormData"
|
let path = "/fake/jsonFormData"
|
||||||
let URLString = PetstoreClientAPI.basePath + path
|
let URLString = PetstoreClientAPI.basePath + path
|
||||||
let formParams: [String:Any?] = [
|
let formParams: [String:Any?] = [
|
||||||
"param": param,
|
"param": param.encodeToJSON(),
|
||||||
"param2": param2
|
"param2": param2.encodeToJSON()
|
||||||
]
|
]
|
||||||
|
|
||||||
let nonNullParameters = APIHelper.rejectNil(formParams)
|
let nonNullParameters = APIHelper.rejectNil(formParams)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
internal class FakeClassnameTags123API {
|
internal class FakeClassnameTags123API {
|
||||||
/**
|
/**
|
||||||
To test class name in snake case
|
To test class name in snake case
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
internal class PetAPI {
|
internal class PetAPI {
|
||||||
/**
|
/**
|
||||||
Add a new pet to the store
|
Add a new pet to the store
|
||||||
@@ -15,7 +17,7 @@ internal class PetAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func addPet(body: Pet, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func addPet(body: Pet, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
addPetWithRequestBuilder(body: body).execute { (_, error) -> Void in
|
addPetWithRequestBuilder(body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -53,7 +55,7 @@ internal class PetAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func deletePet(petId: Int64, apiKey: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func deletePet(petId: Int64, apiKey: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
deletePetWithRequestBuilder(petId: petId, apiKey: apiKey).execute { (_, error) -> Void in
|
deletePetWithRequestBuilder(petId: petId, apiKey: apiKey).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -82,7 +84,7 @@ internal class PetAPI {
|
|||||||
|
|
||||||
let url = URLComponents(string: URLString)
|
let url = URLComponents(string: URLString)
|
||||||
let nillableHeaders: [String: Any?] = [
|
let nillableHeaders: [String: Any?] = [
|
||||||
"api_key": apiKey
|
"api_key": apiKey?.encodeToJSON()
|
||||||
]
|
]
|
||||||
let headerParameters = APIHelper.rejectNilHeaders(nillableHeaders)
|
let headerParameters = APIHelper.rejectNilHeaders(nillableHeaders)
|
||||||
|
|
||||||
@@ -129,7 +131,7 @@ internal class PetAPI {
|
|||||||
|
|
||||||
var url = URLComponents(string: URLString)
|
var url = URLComponents(string: URLString)
|
||||||
url?.queryItems = APIHelper.mapValuesToQueryItems([
|
url?.queryItems = APIHelper.mapValuesToQueryItems([
|
||||||
"status": status
|
"status": status.encodeToJSON()
|
||||||
])
|
])
|
||||||
|
|
||||||
let requestBuilder: RequestBuilder<[Pet]>.Type = PetstoreClientAPI.requestBuilderFactory.getBuilder()
|
let requestBuilder: RequestBuilder<[Pet]>.Type = PetstoreClientAPI.requestBuilderFactory.getBuilder()
|
||||||
@@ -166,7 +168,7 @@ internal class PetAPI {
|
|||||||
|
|
||||||
var url = URLComponents(string: URLString)
|
var url = URLComponents(string: URLString)
|
||||||
url?.queryItems = APIHelper.mapValuesToQueryItems([
|
url?.queryItems = APIHelper.mapValuesToQueryItems([
|
||||||
"tags": tags
|
"tags": tags.encodeToJSON()
|
||||||
])
|
])
|
||||||
|
|
||||||
let requestBuilder: RequestBuilder<[Pet]>.Type = PetstoreClientAPI.requestBuilderFactory.getBuilder()
|
let requestBuilder: RequestBuilder<[Pet]>.Type = PetstoreClientAPI.requestBuilderFactory.getBuilder()
|
||||||
@@ -218,7 +220,7 @@ internal class PetAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func updatePet(body: Pet, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func updatePet(body: Pet, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
updatePetWithRequestBuilder(body: body).execute { (_, error) -> Void in
|
updatePetWithRequestBuilder(body: body).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -257,7 +259,7 @@ internal class PetAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func updatePetWithForm(petId: Int64, name: String? = nil, status: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func updatePetWithForm(petId: Int64, name: String? = nil, status: String? = nil, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
updatePetWithFormWithRequestBuilder(petId: petId, name: name, status: status).execute { (_, error) -> Void in
|
updatePetWithFormWithRequestBuilder(petId: petId, name: name, status: status).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
@@ -284,8 +286,8 @@ internal class PetAPI {
|
|||||||
path = path.replacingOccurrences(of: "{petId}", with: petIdPostEscape, options: .literal, range: nil)
|
path = path.replacingOccurrences(of: "{petId}", with: petIdPostEscape, options: .literal, range: nil)
|
||||||
let URLString = PetstoreClientAPI.basePath + path
|
let URLString = PetstoreClientAPI.basePath + path
|
||||||
let formParams: [String:Any?] = [
|
let formParams: [String:Any?] = [
|
||||||
"name": name,
|
"name": name?.encodeToJSON(),
|
||||||
"status": status
|
"status": status?.encodeToJSON()
|
||||||
]
|
]
|
||||||
|
|
||||||
let nonNullParameters = APIHelper.rejectNil(formParams)
|
let nonNullParameters = APIHelper.rejectNil(formParams)
|
||||||
@@ -330,8 +332,8 @@ internal class PetAPI {
|
|||||||
path = path.replacingOccurrences(of: "{petId}", with: petIdPostEscape, options: .literal, range: nil)
|
path = path.replacingOccurrences(of: "{petId}", with: petIdPostEscape, options: .literal, range: nil)
|
||||||
let URLString = PetstoreClientAPI.basePath + path
|
let URLString = PetstoreClientAPI.basePath + path
|
||||||
let formParams: [String:Any?] = [
|
let formParams: [String:Any?] = [
|
||||||
"additionalMetadata": additionalMetadata,
|
"additionalMetadata": additionalMetadata?.encodeToJSON(),
|
||||||
"file": file
|
"file": file?.encodeToJSON()
|
||||||
]
|
]
|
||||||
|
|
||||||
let nonNullParameters = APIHelper.rejectNil(formParams)
|
let nonNullParameters = APIHelper.rejectNil(formParams)
|
||||||
@@ -376,8 +378,8 @@ internal class PetAPI {
|
|||||||
path = path.replacingOccurrences(of: "{petId}", with: petIdPostEscape, options: .literal, range: nil)
|
path = path.replacingOccurrences(of: "{petId}", with: petIdPostEscape, options: .literal, range: nil)
|
||||||
let URLString = PetstoreClientAPI.basePath + path
|
let URLString = PetstoreClientAPI.basePath + path
|
||||||
let formParams: [String:Any?] = [
|
let formParams: [String:Any?] = [
|
||||||
"additionalMetadata": additionalMetadata,
|
"additionalMetadata": additionalMetadata?.encodeToJSON(),
|
||||||
"requiredFile": requiredFile
|
"requiredFile": requiredFile.encodeToJSON()
|
||||||
]
|
]
|
||||||
|
|
||||||
let nonNullParameters = APIHelper.rejectNil(formParams)
|
let nonNullParameters = APIHelper.rejectNil(formParams)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
internal class StoreAPI {
|
internal class StoreAPI {
|
||||||
/**
|
/**
|
||||||
Delete purchase order by ID
|
Delete purchase order by ID
|
||||||
@@ -15,7 +17,7 @@ internal class StoreAPI {
|
|||||||
- parameter completion: completion handler to receive the data and the error objects
|
- parameter completion: completion handler to receive the data and the error objects
|
||||||
*/
|
*/
|
||||||
internal class func deleteOrder(orderId: String, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
internal class func deleteOrder(orderId: String, completion: @escaping ((_ data: Void?,_ error: Error?) -> Void)) {
|
||||||
deleteOrderWithRequestBuilder(orderId: orderId).execute { (_, error) -> Void in
|
deleteOrderWithRequestBuilder(orderId: orderId).execute { (response, error) -> Void in
|
||||||
if error == nil {
|
if error == nil {
|
||||||
completion((), error)
|
completion((), error)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user