mirror of
https://github.com/OpenAPITools/openapi-generator.git
synced 2025-12-18 19:57:06 +00:00
[Swift] Adding Swift3 generator
* This address #3788 * Contains all changes made in #3823 * Also contains many changes made by @ewanmellor, thanks!
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
use_frameworks!
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
|
||||
target 'SwaggerClient' do
|
||||
pod "PetstoreClient", :path => "../"
|
||||
|
||||
target 'SwaggerClientTests' do
|
||||
inherit! :search_paths
|
||||
end
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
target.build_configurations.each do |configuration|
|
||||
configuration.build_settings['SWIFT_VERSION'] = "3.0"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
PODS:
|
||||
- Alamofire (4.0.0)
|
||||
- PetstoreClient (0.0.1):
|
||||
- Alamofire (~> 4.0)
|
||||
- RxSwift (~> 3.0.0-beta.1)
|
||||
- RxSwift (3.0.0-beta.1)
|
||||
|
||||
DEPENDENCIES:
|
||||
- PetstoreClient (from `../`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
PetstoreClient:
|
||||
:path: ../
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: fef59f00388f267e52d9b432aa5d93dc97190f14
|
||||
PetstoreClient: a58edc9541bf0e2a0a7f8464907f26c9b7bafe74
|
||||
RxSwift: 0823e8d7969c23bfa9ddfb2afa4881e424a1a710
|
||||
|
||||
PODFILE CHECKSUM: da9f5a7ad6086f2c7abb73cf2c35cefce04a9a30
|
||||
|
||||
COCOAPODS: 1.0.1
|
||||
19
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/LICENSE
generated
Normal file
19
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/LICENSE
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2014-2016 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.
|
||||
1744
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/README.md
generated
Normal file
1744
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/README.md
generated
Normal file
File diff suppressed because it is too large
Load Diff
450
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/AFError.swift
generated
Normal file
450
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/AFError.swift
generated
Normal file
@@ -0,0 +1,450 @@
|
||||
//
|
||||
// AFError.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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
|
||||
|
||||
/// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with
|
||||
/// their own associated reasons.
|
||||
///
|
||||
/// - invalidURL: Returned when a `URLConvertible` type fails to create a valid `URL`.
|
||||
/// - parameterEncodingFailed: Returned when a parameter encoding object throws an error during the encoding process.
|
||||
/// - multipartEncodingFailed: Returned when some step in the multipart encoding process fails.
|
||||
/// - responseValidationFailed: Returned when a `validate()` call fails.
|
||||
/// - responseSerializationFailed: Returned when a response serializer encounters an error in the serialization process.
|
||||
public enum AFError: Error {
|
||||
/// The underlying reason the parameter encoding error occurred.
|
||||
///
|
||||
/// - missingURL: The URL request did not have a URL to encode.
|
||||
/// - jsonEncodingFailed: JSON serialization failed with an underlying system error during the
|
||||
/// encoding process.
|
||||
/// - propertyListEncodingFailed: Property list serialization failed with an underlying system error during
|
||||
/// encoding process.
|
||||
public enum ParameterEncodingFailureReason {
|
||||
case missingURL
|
||||
case jsonEncodingFailed(error: Error)
|
||||
case propertyListEncodingFailed(error: Error)
|
||||
}
|
||||
|
||||
/// The underlying reason the multipart encoding error occurred.
|
||||
///
|
||||
/// - bodyPartURLInvalid: The `fileURL` provided for reading an encodable body part isn't a
|
||||
/// file URL.
|
||||
/// - bodyPartFilenameInvalid: The filename of the `fileURL` provided has either an empty
|
||||
/// `lastPathComponent` or `pathExtension.
|
||||
/// - bodyPartFileNotReachable: The file at the `fileURL` provided was not reachable.
|
||||
/// - bodyPartFileNotReachableWithError: Attempting to check the reachability of the `fileURL` provided threw
|
||||
/// an error.
|
||||
/// - bodyPartFileIsDirectory: The file at the `fileURL` provided is actually a directory.
|
||||
/// - bodyPartFileSizeNotAvailable: The size of the file at the `fileURL` provided was not returned by
|
||||
/// the system.
|
||||
/// - bodyPartFileSizeQueryFailedWithError: The attempt to find the size of the file at the `fileURL` provided
|
||||
/// threw an error.
|
||||
/// - bodyPartInputStreamCreationFailed: An `InputStream` could not be created for the provided `fileURL`.
|
||||
/// - outputStreamCreationFailed: An `OutputStream` could not be created when attempting to write the
|
||||
/// encoded data to disk.
|
||||
/// - outputStreamFileAlreadyExists: The encoded body data could not be writtent disk because a file
|
||||
/// already exists at the provided `fileURL`.
|
||||
/// - outputStreamURLInvalid: The `fileURL` provided for writing the encoded body data to disk is
|
||||
/// not a file URL.
|
||||
/// - outputStreamWriteFailed: The attempt to write the encoded body data to disk failed with an
|
||||
/// underlying error.
|
||||
/// - inputStreamReadFailed: The attempt to read an encoded body part `InputStream` failed with
|
||||
/// underlying system error.
|
||||
public enum MultipartEncodingFailureReason {
|
||||
case bodyPartURLInvalid(url: URL)
|
||||
case bodyPartFilenameInvalid(in: URL)
|
||||
case bodyPartFileNotReachable(at: URL)
|
||||
case bodyPartFileNotReachableWithError(atURL: URL, error: Error)
|
||||
case bodyPartFileIsDirectory(at: URL)
|
||||
case bodyPartFileSizeNotAvailable(at: URL)
|
||||
case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error)
|
||||
case bodyPartInputStreamCreationFailed(for: URL)
|
||||
|
||||
case outputStreamCreationFailed(for: URL)
|
||||
case outputStreamFileAlreadyExists(at: URL)
|
||||
case outputStreamURLInvalid(url: URL)
|
||||
case outputStreamWriteFailed(error: Error)
|
||||
|
||||
case inputStreamReadFailed(error: Error)
|
||||
}
|
||||
|
||||
/// The underlying reason the response validation error occurred.
|
||||
///
|
||||
/// - dataFileNil: The data file containing the server response did not exist.
|
||||
/// - dataFileReadFailed: The data file containing the server response could not be read.
|
||||
/// - missingContentType: The response did not contain a `Content-Type` and the `acceptableContentTypes`
|
||||
/// provided did not contain wildcard type.
|
||||
/// - unacceptableContentType: The response `Content-Type` did not match any type in the provided
|
||||
/// `acceptableContentTypes`.
|
||||
/// - unacceptableStatusCode: The response status code was not acceptable.
|
||||
public enum ResponseValidationFailureReason {
|
||||
case dataFileNil
|
||||
case dataFileReadFailed(at: URL)
|
||||
case missingContentType(acceptableContentTypes: [String])
|
||||
case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String)
|
||||
case unacceptableStatusCode(code: Int)
|
||||
}
|
||||
|
||||
/// The underlying reason the response serialization error occurred.
|
||||
///
|
||||
/// - inputDataNil: The server response contained no data.
|
||||
/// - inputDataNilOrZeroLength: The server response contained no data or the data was zero length.
|
||||
/// - inputFileNil: The file containing the server response did not exist.
|
||||
/// - inputFileReadFailed: The file containing the server response could not be read.
|
||||
/// - stringSerializationFailed: String serialization failed using the provided `String.Encoding`.
|
||||
/// - jsonSerializationFailed: JSON serialization failed with an underlying system error.
|
||||
/// - propertyListSerializationFailed: Property list serialization failed with an underlying system error.
|
||||
public enum ResponseSerializationFailureReason {
|
||||
case inputDataNil
|
||||
case inputDataNilOrZeroLength
|
||||
case inputFileNil
|
||||
case inputFileReadFailed(at: URL)
|
||||
case stringSerializationFailed(encoding: String.Encoding)
|
||||
case jsonSerializationFailed(error: Error)
|
||||
case propertyListSerializationFailed(error: Error)
|
||||
}
|
||||
|
||||
case invalidURL(url: URLConvertible)
|
||||
case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
|
||||
case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
|
||||
case responseValidationFailed(reason: ResponseValidationFailureReason)
|
||||
case responseSerializationFailed(reason: ResponseSerializationFailureReason)
|
||||
}
|
||||
|
||||
// MARK: - Error Booleans
|
||||
|
||||
extension AFError {
|
||||
/// Returns whether the AFError is an invalid URL error.
|
||||
public var isInvalidURLError: Bool {
|
||||
if case .invalidURL = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
/// Returns whether the AFError is a parameter encoding error. When `true`, the `underlyingError` property will
|
||||
/// contain the associated value.
|
||||
public var isParameterEncodingError: Bool {
|
||||
if case .multipartEncodingFailed = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
/// Returns whether the AFError is a multipart encoding error. When `true`, the `url` and `underlyingError` properties
|
||||
/// will contain the associated values.
|
||||
public var isMultipartEncodingError: Bool {
|
||||
if case .multipartEncodingFailed = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
/// Returns whether the `AFError` is a response validation error. When `true`, the `acceptableContentTypes`,
|
||||
/// `responseContentType`, and `responseCode` properties will contain the associated values.
|
||||
public var isResponseValidationError: Bool {
|
||||
if case .responseValidationFailed = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
/// Returns whether the `AFError` is a response serialization error. When `true`, the `failedStringEncoding` and
|
||||
/// `underlyingError` properties will contain the associated values.
|
||||
public var isResponseSerializationError: Bool {
|
||||
if case .responseSerializationFailed = self { return true }
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Convenience Properties
|
||||
|
||||
extension AFError {
|
||||
/// The `URLConvertible` associated with the error.
|
||||
public var urlConvertible: URLConvertible? {
|
||||
switch self {
|
||||
case .invalidURL(let url):
|
||||
return url
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The `URL` associated with the error.
|
||||
public var url: URL? {
|
||||
switch self {
|
||||
case .multipartEncodingFailed(let reason):
|
||||
return reason.url
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Error` returned by a system framework associated with a `.parameterEncodingFailed`,
|
||||
/// `.multipartEncodingFailed` or `.responseSerializationFailed` error.
|
||||
public var underlyingError: Error? {
|
||||
switch self {
|
||||
case .parameterEncodingFailed(let reason):
|
||||
return reason.underlyingError
|
||||
case .multipartEncodingFailed(let reason):
|
||||
return reason.underlyingError
|
||||
case .responseSerializationFailed(let reason):
|
||||
return reason.underlyingError
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The acceptable `Content-Type`s of a `.responseValidationFailed` error.
|
||||
public var acceptableContentTypes: [String]? {
|
||||
switch self {
|
||||
case .responseValidationFailed(let reason):
|
||||
return reason.acceptableContentTypes
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The response `Content-Type` of a `.responseValidationFailed` error.
|
||||
public var responseContentType: String? {
|
||||
switch self {
|
||||
case .responseValidationFailed(let reason):
|
||||
return reason.responseContentType
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The response code of a `.responseValidationFailed` error.
|
||||
public var responseCode: Int? {
|
||||
switch self {
|
||||
case .responseValidationFailed(let reason):
|
||||
return reason.responseCode
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The `String.Encoding` associated with a failed `.stringResponse()` call.
|
||||
public var failedStringEncoding: String.Encoding? {
|
||||
switch self {
|
||||
case .responseSerializationFailed(let reason):
|
||||
return reason.failedStringEncoding
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ParameterEncodingFailureReason {
|
||||
var underlyingError: Error? {
|
||||
switch self {
|
||||
case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error):
|
||||
return error
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.MultipartEncodingFailureReason {
|
||||
var url: URL? {
|
||||
switch self {
|
||||
case .bodyPartURLInvalid(let url), .bodyPartFilenameInvalid(let url), .bodyPartFileNotReachable(let url),
|
||||
.bodyPartFileIsDirectory(let url), .bodyPartFileSizeNotAvailable(let url),
|
||||
.bodyPartInputStreamCreationFailed(let url), .outputStreamCreationFailed(let url),
|
||||
.outputStreamFileAlreadyExists(let url), .outputStreamURLInvalid(let url),
|
||||
.bodyPartFileNotReachableWithError(let url, _), .bodyPartFileSizeQueryFailedWithError(let url, _):
|
||||
return url
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var underlyingError: Error? {
|
||||
switch self {
|
||||
case .bodyPartFileNotReachableWithError(_, let error), .bodyPartFileSizeQueryFailedWithError(_, let error),
|
||||
.outputStreamWriteFailed(let error), .inputStreamReadFailed(let error):
|
||||
return error
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ResponseValidationFailureReason {
|
||||
var acceptableContentTypes: [String]? {
|
||||
switch self {
|
||||
case .missingContentType(let types), .unacceptableContentType(let types, _):
|
||||
return types
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var responseContentType: String? {
|
||||
switch self {
|
||||
case .unacceptableContentType(_, let reponseType):
|
||||
return reponseType
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var responseCode: Int? {
|
||||
switch self {
|
||||
case .unacceptableStatusCode(let code):
|
||||
return code
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ResponseSerializationFailureReason {
|
||||
var failedStringEncoding: String.Encoding? {
|
||||
switch self {
|
||||
case .stringSerializationFailed(let encoding):
|
||||
return encoding
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var underlyingError: Error? {
|
||||
switch self {
|
||||
case .jsonSerializationFailed(let error), .propertyListSerializationFailed(let error):
|
||||
return error
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Error Descriptions
|
||||
|
||||
extension AFError: LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .invalidURL(let url):
|
||||
return "URL is not valid: \(url)"
|
||||
case .parameterEncodingFailed(let reason):
|
||||
return reason.localizedDescription
|
||||
case .multipartEncodingFailed(let reason):
|
||||
return reason.localizedDescription
|
||||
case .responseValidationFailed(let reason):
|
||||
return reason.localizedDescription
|
||||
case .responseSerializationFailed(let reason):
|
||||
return reason.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ParameterEncodingFailureReason {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .missingURL:
|
||||
return "URL request to encode was missing a URL"
|
||||
case .jsonEncodingFailed(let error):
|
||||
return "JSON could not be encoded because of error:\n\(error.localizedDescription)"
|
||||
case .propertyListEncodingFailed(let error):
|
||||
return "PropertyList could not be encoded because of error:\n\(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.MultipartEncodingFailureReason {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .bodyPartURLInvalid(let url):
|
||||
return "The URL provided is not a file URL: \(url)"
|
||||
case .bodyPartFilenameInvalid(let url):
|
||||
return "The URL provided does not have a valid filename: \(url)"
|
||||
case .bodyPartFileNotReachable(let url):
|
||||
return "The URL provided is not reachable: \(url)"
|
||||
case .bodyPartFileNotReachableWithError(let url, let error):
|
||||
return (
|
||||
"The system returned an error while checking the provided URL for " +
|
||||
"reachability.\nURL: \(url)\nError: \(error)"
|
||||
)
|
||||
case .bodyPartFileIsDirectory(let url):
|
||||
return "The URL provided is a directory: \(url)"
|
||||
case .bodyPartFileSizeNotAvailable(let url):
|
||||
return "Could not fetch the file size from the provided URL: \(url)"
|
||||
case .bodyPartFileSizeQueryFailedWithError(let url, let error):
|
||||
return (
|
||||
"The system returned an error while attempting to fetch the file size from the " +
|
||||
"provided URL.\nURL: \(url)\nError: \(error)"
|
||||
)
|
||||
case .bodyPartInputStreamCreationFailed(let url):
|
||||
return "Failed to create an InputStream for the provided URL: \(url)"
|
||||
case .outputStreamCreationFailed(let url):
|
||||
return "Failed to create an OutputStream for URL: \(url)"
|
||||
case .outputStreamFileAlreadyExists(let url):
|
||||
return "A file already exists at the provided URL: \(url)"
|
||||
case .outputStreamURLInvalid(let url):
|
||||
return "The provided OutputStream URL is invalid: \(url)"
|
||||
case .outputStreamWriteFailed(let error):
|
||||
return "OutputStream write failed with error: \(error)"
|
||||
case .inputStreamReadFailed(let error):
|
||||
return "InputStream read failed with error: \(error)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ResponseSerializationFailureReason {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .inputDataNil:
|
||||
return "Response could not be serialized, input data was nil."
|
||||
case .inputDataNilOrZeroLength:
|
||||
return "Response could not be serialized, input data was nil or zero length."
|
||||
case .inputFileNil:
|
||||
return "Response could not be serialized, input file was nil."
|
||||
case .inputFileReadFailed(let url):
|
||||
return "Response could not be serialized, input file could not be read: \(url)."
|
||||
case .stringSerializationFailed(let encoding):
|
||||
return "String could not be serialized with encoding: \(encoding)."
|
||||
case .jsonSerializationFailed(let error):
|
||||
return "JSON could not be serialized because of error:\n\(error.localizedDescription)"
|
||||
case .propertyListSerializationFailed(let error):
|
||||
return "PropertyList could not be serialized because of error:\n\(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ResponseValidationFailureReason {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .dataFileNil:
|
||||
return "Response could not be validated, data file was nil."
|
||||
case .dataFileReadFailed(let url):
|
||||
return "Response could not be validated, data file could not be read: \(url)."
|
||||
case .missingContentType(let types):
|
||||
return (
|
||||
"Response Content-Type was missing and acceptable content types " +
|
||||
"(\(types.joined(separator: ","))) do not match \"*/*\"."
|
||||
)
|
||||
case .unacceptableContentType(let acceptableTypes, let responseType):
|
||||
return (
|
||||
"Response Content-Type \"\(responseType)\" does not match any acceptable types: " +
|
||||
"\(acceptableTypes.joined(separator: ","))."
|
||||
)
|
||||
case .unacceptableStatusCode(let code):
|
||||
return "Response status code was unacceptable: \(code)."
|
||||
}
|
||||
}
|
||||
}
|
||||
456
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Alamofire.swift
generated
Normal file
456
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Alamofire.swift
generated
Normal file
@@ -0,0 +1,456 @@
|
||||
//
|
||||
// Alamofire.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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, otherise 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.
|
||||
///
|
||||
/// - 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
|
||||
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
|
||||
public func stream(with netService: NetService) -> StreamRequest {
|
||||
return SessionManager.default.stream(with: netService)
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// DispatchQueue+Alamofire.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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 Dispatch
|
||||
|
||||
extension DispatchQueue {
|
||||
static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) }
|
||||
static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) }
|
||||
static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) }
|
||||
static var background: DispatchQueue { return DispatchQueue.global(qos: .background) }
|
||||
|
||||
func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
|
||||
asyncAfter(deadline: .now() + delay, execute: closure)
|
||||
}
|
||||
|
||||
func syncResult<T>(_ closure: () -> T) -> T {
|
||||
var result: T!
|
||||
sync { result = closure() }
|
||||
return result
|
||||
}
|
||||
}
|
||||
581
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/MultipartFormData.swift
generated
Normal file
581
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/MultipartFormData.swift
generated
Normal file
@@ -0,0 +1,581 @@
|
||||
//
|
||||
// MultipartFormData.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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(OSX)
|
||||
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 var contentType: String { return "multipart/form-data; boundary=\(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 let 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,240 @@
|
||||
//
|
||||
// NetworkReachabilityManager.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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.
|
||||
- ReachableOnWWAN: The network is reachable over the WWAN connection.
|
||||
- ReachableOnWiFi: The network is reachable over the WiFi connection.
|
||||
*/
|
||||
|
||||
|
||||
/// 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?
|
||||
|
||||
private var flags: SCNetworkReachabilityFlags? {
|
||||
var flags = SCNetworkReachabilityFlags()
|
||||
|
||||
if SCNetworkReachabilityGetFlags(reachability, &flags) {
|
||||
return flags
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private let reachability: SCNetworkReachability
|
||||
private 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
|
||||
self.previousFlags = SCNetworkReachabilityFlags()
|
||||
}
|
||||
|
||||
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()
|
||||
self.notifyListener(self.flags ?? SCNetworkReachabilityFlags())
|
||||
}
|
||||
|
||||
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 flags.contains(.reachable) else { return .notReachable }
|
||||
|
||||
var networkStatus: NetworkReachabilityStatus = .notReachable
|
||||
|
||||
if !flags.contains(.connectionRequired) { networkStatus = .reachable(.ethernetOrWiFi) }
|
||||
|
||||
if flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) {
|
||||
if !flags.contains(.interventionRequired) { networkStatus = .reachable(.ethernetOrWiFi) }
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) }
|
||||
#endif
|
||||
|
||||
return networkStatus
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
52
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Notifications.swift
generated
Normal file
52
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Notifications.swift
generated
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Notifications.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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 Notification.Name {
|
||||
/// Used as a namespace for all `URLSessionTask` related notifications.
|
||||
public struct Task {
|
||||
/// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`.
|
||||
public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume")
|
||||
|
||||
/// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`.
|
||||
public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend")
|
||||
|
||||
/// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`.
|
||||
public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel")
|
||||
|
||||
/// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`.
|
||||
public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension Notification {
|
||||
/// Used as a namespace for all `Notification` user info dictionary keys.
|
||||
public struct Key {
|
||||
/// User info dictionary key representing the `URLSessionTask` associated with the notification.
|
||||
public static let Task = "org.alamofire.notification.key.task"
|
||||
}
|
||||
}
|
||||
373
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/ParameterEncoding.swift
generated
Normal file
373
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/ParameterEncoding.swift
generated
Normal file
@@ -0,0 +1,373 @@
|
||||
//
|
||||
// ParameterEncoding.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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`. Since there is no published specification for how to encode
|
||||
/// collection types, 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`).
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// MARK: Initialization
|
||||
|
||||
/// Creates a `URLEncoding` instance using the specified destination.
|
||||
///
|
||||
/// - parameter destination: The destination defining where the encoded query string is to be applied.
|
||||
///
|
||||
/// - returns: The new `URLEncoding` instance.
|
||||
public init(destination: Destination = .methodDependent) {
|
||||
self.destination = destination
|
||||
}
|
||||
|
||||
// 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: "\(key)[]", value: value)
|
||||
}
|
||||
} else if let value = value as? NSNumber {
|
||||
if value.isBool {
|
||||
components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
|
||||
} else {
|
||||
components.append((escape(key), escape("\(value)")))
|
||||
}
|
||||
} else if let bool = value as? Bool {
|
||||
components.append((escape(key), escape((bool ? "1" : "0"))))
|
||||
} 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)")
|
||||
|
||||
return string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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) }
|
||||
}
|
||||
600
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Request.swift
generated
Normal file
600
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Request.swift
generated
Normal file
@@ -0,0 +1,600 @@
|
||||
//
|
||||
// Request.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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 asychronous. 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.
|
||||
open 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 }
|
||||
|
||||
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 static 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 -i"]
|
||||
|
||||
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 {
|
||||
components.append("-u \(credential.user!):\(credential.password!)")
|
||||
}
|
||||
} else {
|
||||
if let credential = delegate.credential {
|
||||
components.append("-u \(credential.user!):\(credential.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);" }
|
||||
components.append("-b \"\(string.substring(to: string.characters.index(before: string.endIndex)))\"")
|
||||
}
|
||||
}
|
||||
|
||||
var headers: [AnyHashable: Any] = [:]
|
||||
|
||||
if let additionalHeaders = session.configuration.httpAdditionalHeaders {
|
||||
for (field, value) in additionalHeaders where field != AnyHashable("Cookie") {
|
||||
headers[field] = value
|
||||
}
|
||||
}
|
||||
|
||||
if let headerFields = request.allHTTPHeaderFields {
|
||||
for (field, value) in headerFields where field != "Cookie" {
|
||||
headers[field] = value
|
||||
}
|
||||
}
|
||||
|
||||
for (field, value) in headers {
|
||||
components.append("-H \"\(field): \(value)\"")
|
||||
}
|
||||
|
||||
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 {
|
||||
let urlRequest = try self.urlRequest.adapt(using: adapter)
|
||||
return queue.syncResult { session.dataTask(with: urlRequest) }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// 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 {
|
||||
let task: URLSessionTask
|
||||
|
||||
switch self {
|
||||
case let .request(urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.downloadTask(with: urlRequest) }
|
||||
case let .resumeData(resumeData):
|
||||
task = queue.syncResult { session.downloadTask(withResumeData: resumeData) }
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// 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.
|
||||
open override func cancel() {
|
||||
downloadDelegate.downloadTask.cancel { self.downloadDelegate.resumeData = $0 }
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: Notification.Name.Task.DidCancel,
|
||||
object: self,
|
||||
userInfo: [Notification.Key.Task: task]
|
||||
)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
let task: URLSessionTask
|
||||
|
||||
switch self {
|
||||
case let .data(data, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.uploadTask(with: urlRequest, from: data) }
|
||||
case let .file(url, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.uploadTask(with: urlRequest, fromFile: url) }
|
||||
case let .stream(_, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.uploadTask(withStreamedRequest: urlRequest) }
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// 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`.
|
||||
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.syncResult { session.streamTask(withHostName: hostName, port: port) }
|
||||
case let .netService(netService):
|
||||
task = queue.syncResult { session.streamTask(with: netService) }
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
296
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Response.swift
generated
Normal file
296
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Response.swift
generated
Normal file
@@ -0,0 +1,296 @@
|
||||
//
|
||||
// Response.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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?
|
||||
|
||||
var _metrics: AnyObject?
|
||||
|
||||
init(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) {
|
||||
self.request = request
|
||||
self.response = response
|
||||
self.data = data
|
||||
self.error = error
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
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!)" : "[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: -
|
||||
|
||||
/// 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?
|
||||
|
||||
var _metrics: AnyObject?
|
||||
|
||||
init(
|
||||
request: URLRequest?,
|
||||
response: HTTPURLResponse?,
|
||||
temporaryURL: URL?,
|
||||
destinationURL: URL?,
|
||||
resumeData: Data?,
|
||||
error: Error?)
|
||||
{
|
||||
self.request = request
|
||||
self.response = response
|
||||
self.temporaryURL = temporaryURL
|
||||
self.destinationURL = destinationURL
|
||||
self.resumeData = resumeData
|
||||
self.error = error
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
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!)" : "[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: -
|
||||
|
||||
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,716 @@
|
||||
//
|
||||
// ResponseSerialization.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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: - 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
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
|
||||
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
|
||||
|
||||
let timeline = Timeline(
|
||||
requestStartTime: self.startTime ?? CFAbsoluteTimeGetCurrent(),
|
||||
initialResponseTime: initialResponseTime,
|
||||
requestCompletedTime: requestCompletedTime,
|
||||
serializationCompletedTime: CFAbsoluteTimeGetCurrent()
|
||||
)
|
||||
|
||||
var dataResponse = DataResponse<T.SerializedObject>(
|
||||
request: self.request,
|
||||
response: self.response,
|
||||
data: self.delegate.data,
|
||||
result: result,
|
||||
timeline: 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
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
|
||||
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
|
||||
|
||||
let timeline = Timeline(
|
||||
requestStartTime: self.startTime ?? CFAbsoluteTimeGetCurrent(),
|
||||
initialResponseTime: initialResponseTime,
|
||||
requestCompletedTime: requestCompletedTime,
|
||||
serializationCompletedTime: CFAbsoluteTimeGetCurrent()
|
||||
)
|
||||
|
||||
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: 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 ?? String.Encoding.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]
|
||||
102
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Result.swift
generated
Normal file
102
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Result.swift
generated
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// Result.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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 represent whether a request was successful or encountered an error.
|
||||
///
|
||||
/// - success: The request and all post processing operations were successful resulting in the serialization of the
|
||||
/// provided associated value.
|
||||
///
|
||||
/// - failure: The request encountered an error resulting in a failure. The associated values are the original data
|
||||
/// provided by the server as well as the error that caused the failure.
|
||||
public enum Result<Value> {
|
||||
case success(Value)
|
||||
case failure(Error)
|
||||
|
||||
/// Returns `true` if the result is a success, `false` otherwise.
|
||||
public var isSuccess: Bool {
|
||||
switch self {
|
||||
case .success:
|
||||
return true
|
||||
case .failure:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the result is a failure, `false` otherwise.
|
||||
public var isFailure: Bool {
|
||||
return !isSuccess
|
||||
}
|
||||
|
||||
/// Returns the associated value if the result is a success, `nil` otherwise.
|
||||
public var value: Value? {
|
||||
switch self {
|
||||
case .success(let value):
|
||||
return value
|
||||
case .failure:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the associated error value if the result is a failure, `nil` otherwise.
|
||||
public var error: Error? {
|
||||
switch self {
|
||||
case .success:
|
||||
return nil
|
||||
case .failure(let error):
|
||||
return error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CustomStringConvertible
|
||||
|
||||
extension Result: CustomStringConvertible {
|
||||
/// The textual representation used when written to an output stream, which includes whether the result was a
|
||||
/// success or failure.
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .success:
|
||||
return "SUCCESS"
|
||||
case .failure:
|
||||
return "FAILURE"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CustomDebugStringConvertible
|
||||
|
||||
extension Result: CustomDebugStringConvertible {
|
||||
/// The debug textual representation used when written to an output stream, which includes whether the result was a
|
||||
/// success or failure in addition to the value or error.
|
||||
public var debugDescription: String {
|
||||
switch self {
|
||||
case .success(let value):
|
||||
return "SUCCESS: \(value)"
|
||||
case .failure(let error):
|
||||
return "FAILURE: \(error)"
|
||||
}
|
||||
}
|
||||
}
|
||||
293
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/ServerTrustPolicy.swift
generated
Normal file
293
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/ServerTrustPolicy.swift
generated
Normal file
@@ -0,0 +1,293 @@
|
||||
//
|
||||
// ServerTrustPolicy.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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.
|
||||
open 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.
|
||||
///
|
||||
/// - 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 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 .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
|
||||
}
|
||||
}
|
||||
681
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/SessionDelegate.swift
generated
Normal file
681
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/SessionDelegate.swift
generated
Normal file
@@ -0,0 +1,681 @@
|
||||
//
|
||||
// SessionDelegate.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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, (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, (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, (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, (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, (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, (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:)`.
|
||||
open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:writeClosedFor:)`.
|
||||
open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:betterRouteDiscoveredFor:)`.
|
||||
open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:streamTask:didBecome:outputStream:)`.
|
||||
open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)?
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
var retrier: RequestRetrier?
|
||||
weak var sessionManager: SessionManager?
|
||||
|
||||
private 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(OSX)
|
||||
if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
|
||||
return sessionDidFinishEventsForBackgroundURLSession != nil
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !os(watchOS)
|
||||
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(OSX)
|
||||
|
||||
/// 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 }
|
||||
|
||||
if let taskDidComplete = strongSelf.taskDidComplete {
|
||||
taskDidComplete(session, task, error)
|
||||
} else if let delegate = strongSelf[task]?.delegate {
|
||||
delegate.urlSession(session, task: task, didCompleteWithError: error)
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: Notification.Name.Task.DidComplete,
|
||||
object: strongSelf,
|
||||
userInfo: [Notification.Key.Task: task]
|
||||
)
|
||||
|
||||
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 let taskDelegate = self[task]?.delegate, taskDelegate.error != nil {
|
||||
error = taskDelegate.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, delay in
|
||||
guard shouldRetry else { completeTask(session, task, error) ; return }
|
||||
|
||||
DispatchQueue.utility.after(delay) { [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)
|
||||
|
||||
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
|
||||
776
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/SessionManager.swift
generated
Normal file
776
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/SessionManager.swift
generated
Normal file
@@ -0,0 +1,776 @@
|
||||
//
|
||||
// SessionManager.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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.
|
||||
open 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.
|
||||
open 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 (com.alamofire.iOS-Example; build:1; iOS 9.3.0) Alamofire/3.4.2`
|
||||
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(OSX)
|
||||
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.
|
||||
open static let multipartFormDataEncodingMemoryThreshold: UInt64 = 10_000_000
|
||||
|
||||
/// The underlying session.
|
||||
open let session: URLSession
|
||||
|
||||
/// The session delegate handling all the task and session delegate callbacks.
|
||||
open 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
|
||||
{
|
||||
do {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
|
||||
return request(encodedURLRequest)
|
||||
} catch {
|
||||
return request(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`.
|
||||
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
|
||||
do {
|
||||
let 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(failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private - Request Implementation
|
||||
|
||||
private func request(failedWith error: Error) -> DataRequest {
|
||||
let request = DataRequest(session: session, requestTask: .data(nil, nil), error: error)
|
||||
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(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(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.
|
||||
///
|
||||
/// - 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 request = DownloadRequest(session: session, requestTask: .download(downloadable, task))
|
||||
|
||||
request.downloadDelegate.destination = destination
|
||||
|
||||
delegate[task] = request
|
||||
|
||||
if startRequestsImmediately { request.resume() }
|
||||
|
||||
return request
|
||||
} catch {
|
||||
return download(failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func download(failedWith error: Error) -> DownloadRequest {
|
||||
let download = DownloadRequest(session: session, requestTask: .download(nil, nil), error: error)
|
||||
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(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(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(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(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(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(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,
|
||||
encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
|
||||
{
|
||||
do {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
|
||||
return upload(
|
||||
multipartFormData: multipartFormData,
|
||||
usingThreshold: encodingMemoryThreshold,
|
||||
with: urlRequest,
|
||||
encodingCompletion: encodingCompletion
|
||||
)
|
||||
} catch {
|
||||
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,
|
||||
encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
|
||||
{
|
||||
DispatchQueue.global(qos: .utility).async {
|
||||
let formData = MultipartFormData()
|
||||
multipartFormData(formData)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let encodingResult = MultipartFormDataEncodingResult.success(
|
||||
request: self.upload(fileURL, with: urlRequestWithContentType),
|
||||
streamingFromDisk: true,
|
||||
streamFileURL: fileURL
|
||||
)
|
||||
encodingCompletion?(encodingResult)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
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(failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func upload(failedWith error: Error) -> UploadRequest {
|
||||
let upload = UploadRequest(session: session, requestTask: .upload(nil, nil), error: error)
|
||||
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
|
||||
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
|
||||
open func stream(with netService: NetService) -> StreamRequest {
|
||||
return stream(.netService(netService))
|
||||
}
|
||||
|
||||
// MARK: Private - Stream Implementation
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
request.delegate.task = task // resets all task delegate data
|
||||
|
||||
request.startTime = CFAbsoluteTimeGetCurrent()
|
||||
request.endTime = nil
|
||||
|
||||
task.resume()
|
||||
|
||||
return true
|
||||
} catch {
|
||||
request.delegate.error = error
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
448
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/TaskDelegate.swift
generated
Normal file
448
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/TaskDelegate.swift
generated
Normal file
@@ -0,0 +1,448 @@
|
||||
//
|
||||
// TaskDelegate.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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.
|
||||
open let queue: OperationQueue
|
||||
|
||||
var task: URLSessionTask? {
|
||||
didSet { reset() }
|
||||
}
|
||||
|
||||
var data: Data? { return nil }
|
||||
var error: Error?
|
||||
|
||||
var initialResponseTime: CFAbsoluteTime?
|
||||
var credential: URLCredential?
|
||||
var metrics: AnyObject? // URLSessionTaskMetrics
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
init(task: URLSessionTask?) {
|
||||
self.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
|
||||
|
||||
if let destination = destination {
|
||||
let result = destination(location, downloadTask.response as! HTTPURLResponse)
|
||||
let destination = result.destinationURL
|
||||
let options = result.options
|
||||
|
||||
do {
|
||||
destinationURL = destination
|
||||
|
||||
if options.contains(.removePreviousFile) {
|
||||
if FileManager.default.fileExists(atPath: destination.path) {
|
||||
try FileManager.default.removeItem(at: destination)
|
||||
}
|
||||
}
|
||||
|
||||
if options.contains(.createIntermediateDirectories) {
|
||||
let directory = destination.deletingLastPathComponent()
|
||||
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
|
||||
try FileManager.default.moveItem(at: location, to: destination)
|
||||
} 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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
136
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Timeline.swift
generated
Normal file
136
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Timeline.swift
generated
Normal file
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// Timeline.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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: ", ") + " }"
|
||||
}
|
||||
}
|
||||
309
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Validation.swift
generated
Normal file
309
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Alamofire/Source/Validation.swift
generated
Normal file
@@ -0,0 +1,309 @@
|
||||
//
|
||||
// Validation.swift
|
||||
//
|
||||
// Copyright (c) 2014-2016 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)
|
||||
let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)
|
||||
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 = {
|
||||
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 { _, 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 { _, 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 {
|
||||
return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = {
|
||||
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 { _, 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 { _, 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 {
|
||||
return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "PetstoreClient",
|
||||
"platforms": {
|
||||
"ios": "9.0",
|
||||
"osx": "10.11"
|
||||
},
|
||||
"version": "0.0.1",
|
||||
"source": {
|
||||
"git": "git@github.com:swagger-api/swagger-mustache.git",
|
||||
"tag": "v1.0.0"
|
||||
},
|
||||
"authors": "",
|
||||
"license": "Apache License, Version 2.0",
|
||||
"homepage": "https://github.com/swagger-api/swagger-codegen",
|
||||
"summary": "PetstoreClient",
|
||||
"source_files": "PetstoreClient/Classes/Swaggers/**/*.swift",
|
||||
"dependencies": {
|
||||
"RxSwift": [
|
||||
"~> 3.0.0-beta.1"
|
||||
],
|
||||
"Alamofire": [
|
||||
"~> 4.0"
|
||||
]
|
||||
}
|
||||
}
|
||||
22
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Manifest.lock
generated
Normal file
22
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Manifest.lock
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
PODS:
|
||||
- Alamofire (4.0.0)
|
||||
- PetstoreClient (0.0.1):
|
||||
- Alamofire (~> 4.0)
|
||||
- RxSwift (~> 3.0.0-beta.1)
|
||||
- RxSwift (3.0.0-beta.1)
|
||||
|
||||
DEPENDENCIES:
|
||||
- PetstoreClient (from `../`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
PetstoreClient:
|
||||
:path: ../
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: fef59f00388f267e52d9b432aa5d93dc97190f14
|
||||
PetstoreClient: a58edc9541bf0e2a0a7f8464907f26c9b7bafe74
|
||||
RxSwift: 0823e8d7969c23bfa9ddfb2afa4881e424a1a710
|
||||
|
||||
PODFILE CHECKSUM: da9f5a7ad6086f2c7abb73cf2c35cefce04a9a30
|
||||
|
||||
COCOAPODS: 1.0.1
|
||||
1865
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Pods.xcodeproj/project.pbxproj
generated
Normal file
1865
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/Pods.xcodeproj/project.pbxproj
generated
Normal file
File diff suppressed because it is too large
Load Diff
9
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/LICENSE.md
generated
Normal file
9
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/LICENSE.md
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
**The MIT License**
|
||||
**Copyright © 2015 Krunoslav Zaher**
|
||||
**All rights reserved.**
|
||||
|
||||
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.
|
||||
203
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/README.md
generated
Normal file
203
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/README.md
generated
Normal file
@@ -0,0 +1,203 @@
|
||||
<img src="assets/Rx_Logo_M.png" alt="Miss Electric Eel 2016" width="36" height="36"> RxSwift: ReactiveX for Swift
|
||||
======================================
|
||||
|
||||
[](https://travis-ci.org/ReactiveX/RxSwift)   [](https://github.com/Carthage/Carthage)
|
||||
|
||||
## About Rx
|
||||
|
||||
**:warning: This readme describes RxSwift 3.0 version that requires Swift 3.0:warning:**
|
||||
|
||||
**:warning: If you are looking for Swift 2.3 compatible version, please take a look at RxSwift ~> 2.0 versions and [swift-2.3](https://github.com/ReactiveX/RxSwift/tree/rxswift-2.0) branch :warning:**
|
||||
|
||||
Rx is a [generic abstraction of computation](https://youtu.be/looJcaeboBY) expressed through `Observable<Element>` interface.
|
||||
|
||||
This is a Swift version of [Rx](https://github.com/Reactive-Extensions/Rx.NET).
|
||||
|
||||
It tries to port as many concepts from the original version as possible, but some concepts were adapted for more pleasant and performant integration with iOS/OSX environment.
|
||||
|
||||
Cross platform documentation can be found on [ReactiveX.io](http://reactivex.io/).
|
||||
|
||||
Like the original Rx, its intention is to enable easy composition of asynchronous operations and event/data streams.
|
||||
|
||||
KVO observing, async operations and streams are all unified under [abstraction of sequence](Documentation/GettingStarted.md#observables-aka-sequences). This is the reason why Rx is so simple, elegant and powerful.
|
||||
|
||||
## I came here because I want to ...
|
||||
|
||||
###### ... understand
|
||||
|
||||
* [why use rx?](Documentation/Why.md)
|
||||
* [the basics, getting started with RxSwift](Documentation/GettingStarted.md)
|
||||
* [units](Documentation/Units.md) - what is `Driver`, `ControlProperty`, and `Variable` ... and why do they exist?
|
||||
* [testing](Documentation/UnitTests.md)
|
||||
* [tips and common errors](Documentation/Tips.md)
|
||||
* [debugging](Documentation/GettingStarted.md#debugging)
|
||||
* [the math behind Rx](Documentation/MathBehindRx.md)
|
||||
* [what are hot and cold observable sequences?](Documentation/HotAndColdObservables.md)
|
||||
* [what does the the public API look like?](Documentation/API.md)
|
||||
|
||||
|
||||
###### ... install
|
||||
|
||||
* Integrate RxSwift/RxCocoa with my app. [Installation Guide](Documentation/Installation.md)
|
||||
|
||||
###### ... hack around
|
||||
|
||||
* with the example app. [Running Example App](Documentation/ExampleApp.md)
|
||||
* with operators in playgrounds. [Playgrounds](Documentation/Playgrounds.md)
|
||||
|
||||
###### ... interact
|
||||
|
||||
* All of this is great, but it would be nice to talk with other people using RxSwift and exchange experiences. <br />[](http://slack.rxswift.org) [Join Slack Channel](http://rxswift-slack.herokuapp.com)
|
||||
* Report a problem using the library. [Open an Issue With Bug Template](ISSUE_TEMPLATE.md)
|
||||
* Request a new feature. [Open an Issue With Feature Request Template](Documentation/NewFeatureRequestTemplate.md)
|
||||
|
||||
|
||||
###### ... compare
|
||||
|
||||
* [with other libraries](Documentation/ComparisonWithOtherLibraries.md).
|
||||
|
||||
|
||||
###### ... find compatible
|
||||
|
||||
* libraries from [RxSwiftCommunity](https://github.com/RxSwiftCommunity).
|
||||
* [Pods using RxSwift](https://cocoapods.org/?q=uses%3Arxswift).
|
||||
|
||||
###### ... see the broader vision
|
||||
|
||||
* Does this exist for Android? [RxJava](https://github.com/ReactiveX/RxJava)
|
||||
* Where is all of this going, what is the future, what about reactive architectures, how do you design entire apps this way? [Cycle.js](https://github.com/cyclejs/cycle-core) - this is javascript, but [RxJS](https://github.com/Reactive-Extensions/RxJS) is javascript version of Rx.
|
||||
|
||||
## Usage
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th width="30%">Here's an example</th>
|
||||
<th width="30%">In Action</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Define search for GitHub repositories ...</td>
|
||||
<th rowspan="9"><img src="https://raw.githubusercontent.com/kzaher/rxswiftcontent/master/GithubSearch.gif"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="highlight highlight-source-swift"><pre>
|
||||
let searchResults = searchBar.rx.text
|
||||
.throttle(0.3, scheduler: MainScheduler.instance)
|
||||
.distinctUntilChanged()
|
||||
.flatMapLatest { query -> Observable<[Repository]> in
|
||||
if query.isEmpty {
|
||||
return Observable.just([])
|
||||
}
|
||||
|
||||
return searchGitHub(query)
|
||||
.catchErrorJustReturn([])
|
||||
}
|
||||
.observeOn(MainScheduler.instance)</pre></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>... then bind the results to your tableview</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><div class="highlight highlight-source-swift"><pre>
|
||||
searchResults
|
||||
.bindTo(tableView.rx.items(cellIdentifier: "Cell")) {
|
||||
(index, repository: Repository, cell) in
|
||||
cell.textLabel?.text = repository.name
|
||||
cell.detailTextLabel?.text = repository.url
|
||||
}
|
||||
.addDisposableTo(disposeBag)</pre></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
* Xcode 8.0 GM (8A218a)
|
||||
* Swift 3.0
|
||||
|
||||
* iOS 8.0+
|
||||
* Mac OS X 10.10+
|
||||
* tvOS 9.0+
|
||||
* watchOS 2.0+
|
||||
|
||||
## Installation
|
||||
|
||||
Rx doesn't contain any external dependencies.
|
||||
|
||||
These are currently the supported options:
|
||||
|
||||
### Manual
|
||||
|
||||
Open Rx.xcworkspace, choose `RxExample` and hit run. This method will build everything and run the sample app
|
||||
|
||||
### [CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html)
|
||||
|
||||
```
|
||||
# Podfile
|
||||
use_frameworks!
|
||||
|
||||
target 'YOUR_TARGET_NAME' do
|
||||
pod 'RxSwift', '~> 3.0.0-beta.1'
|
||||
pod 'RxCocoa', '~> 3.0.0-beta.1'
|
||||
end
|
||||
|
||||
# RxTests and RxBlocking make the most sense in the context of unit/integration tests
|
||||
target 'YOUR_TESTING_TARGET' do
|
||||
pod 'RxBlocking', '~> 3.0.0-beta.1'
|
||||
pod 'RxTests', '~> 3.0.0-beta.1'
|
||||
end
|
||||
```
|
||||
|
||||
Replace `YOUR_TARGET_NAME` and then, in the `Podfile` directory, type:
|
||||
|
||||
**:warning: If you want to use CocoaPods with Xcode 8.0 beta and Swift 3.0, you might need to add the following
|
||||
lines to your podfile: :warning:**
|
||||
|
||||
```
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['SWIFT_VERSION'] = '3.0'
|
||||
config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.10'
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
```
|
||||
$ pod install
|
||||
```
|
||||
|
||||
### [Carthage](https://github.com/Carthage/Carthage)
|
||||
|
||||
Add this to `Cartfile`
|
||||
|
||||
```
|
||||
github "ReactiveX/RxSwift" "3.0.0-beta.1"
|
||||
```
|
||||
|
||||
```
|
||||
$ carthage update
|
||||
```
|
||||
|
||||
### Manually using git submodules
|
||||
|
||||
* Add RxSwift as a submodule
|
||||
|
||||
```
|
||||
$ git submodule add git@github.com:ReactiveX/RxSwift.git
|
||||
```
|
||||
|
||||
* Drag `Rx.xcodeproj` into Project Navigator
|
||||
* Go to `Project > Targets > Build Phases > Link Binary With Libraries`, click `+` and select `RxSwift-[Platform]` and `RxCocoa-[Platform]` targets
|
||||
|
||||
|
||||
## References
|
||||
|
||||
* [http://reactivex.io/](http://reactivex.io/)
|
||||
* [Reactive Extensions GitHub (GitHub)](https://github.com/Reactive-Extensions)
|
||||
* [Erik Meijer (Wikipedia)](http://en.wikipedia.org/wiki/Erik_Meijer_%28computer_scientist%29)
|
||||
* [Expert to Expert: Brian Beckman and Erik Meijer - Inside the .NET Reactive Framework (Rx) (video)](https://youtu.be/looJcaeboBY)
|
||||
* [Reactive Programming Overview (Jafar Husain from Netflix)](https://www.youtube.com/watch?v=dwP1TNXE6fc)
|
||||
* [Subject/Observer is Dual to Iterator (paper)](http://csl.stanford.edu/~christos/pldi2010.fit/meijer.duality.pdf)
|
||||
* [Rx standard sequence operators visualized (visualization tool)](http://rxmarbles.com/)
|
||||
* [Haskell](https://www.haskell.org/)
|
||||
75
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/AnyObserver.swift
generated
Normal file
75
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/AnyObserver.swift
generated
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// AnyObserver.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/28/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
A type-erased `ObserverType`.
|
||||
|
||||
Forwards operations to an arbitrary underlying observer with the same `Element` type, hiding the specifics of the underlying observer type.
|
||||
*/
|
||||
public struct AnyObserver<Element> : ObserverType {
|
||||
/**
|
||||
The type of elements in sequence that observer can observe.
|
||||
*/
|
||||
public typealias E = Element
|
||||
|
||||
/**
|
||||
Anonymous event handler type.
|
||||
*/
|
||||
public typealias EventHandler = (Event<Element>) -> Void
|
||||
|
||||
public let observer: EventHandler
|
||||
|
||||
/**
|
||||
Construct an instance whose `on(event)` calls `eventHandler(event)`
|
||||
|
||||
- parameter eventHandler: Event handler that observes sequences events.
|
||||
*/
|
||||
public init(eventHandler: @escaping EventHandler) {
|
||||
self.observer = eventHandler
|
||||
}
|
||||
|
||||
/**
|
||||
Construct an instance whose `on(event)` calls `observer.on(event)`
|
||||
|
||||
- parameter observer: Observer that receives sequence events.
|
||||
*/
|
||||
public init<O : ObserverType>(_ observer: O) where O.E == Element {
|
||||
self.observer = observer.on
|
||||
}
|
||||
|
||||
/**
|
||||
Send `event` to this observer.
|
||||
|
||||
- parameter event: Event instance.
|
||||
*/
|
||||
public func on(_ event: Event<Element>) {
|
||||
return self.observer(event)
|
||||
}
|
||||
|
||||
/**
|
||||
Erases type of observer and returns canonical observer.
|
||||
|
||||
- returns: type erased observer.
|
||||
*/
|
||||
public func asObserver() -> AnyObserver<E> {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
extension ObserverType {
|
||||
/**
|
||||
Erases type of observer and returns canonical observer.
|
||||
|
||||
- returns: type erased observer.
|
||||
*/
|
||||
public func asObserver() -> AnyObserver<E> {
|
||||
return AnyObserver(self)
|
||||
}
|
||||
}
|
||||
28
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Cancelable.swift
generated
Normal file
28
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Cancelable.swift
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Cancelable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/12/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents disposable resource with state tracking.
|
||||
*/
|
||||
public protocol Cancelable : Disposable {
|
||||
/**
|
||||
- returns: Was resource disposed.
|
||||
*/
|
||||
var isDisposed: Bool { get }
|
||||
}
|
||||
|
||||
public extension Cancelable {
|
||||
|
||||
@available(*, deprecated, renamed: "isDisposed")
|
||||
var disposed: Bool {
|
||||
return isDisposed
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// AsyncLock.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/21/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
In case nobody holds this lock, the work will be queued and executed immediately
|
||||
on thread that is requesting lock.
|
||||
|
||||
In case there is somebody currently holding that lock, action will be enqueued.
|
||||
When owned of the lock finishes with it's processing, it will also execute
|
||||
and pending work.
|
||||
|
||||
That means that enqueued work could possibly be executed later on a different thread.
|
||||
*/
|
||||
class AsyncLock<I: InvocableType>
|
||||
: Disposable
|
||||
, Lock
|
||||
, SynchronizedDisposeType {
|
||||
typealias Action = () -> Void
|
||||
|
||||
var _lock = SpinLock()
|
||||
|
||||
private var _queue: Queue<I> = Queue(capacity: 0)
|
||||
|
||||
private var _isExecuting: Bool = false
|
||||
private var _hasFaulted: Bool = false
|
||||
|
||||
// lock {
|
||||
func lock() {
|
||||
_lock.lock()
|
||||
}
|
||||
|
||||
func unlock() {
|
||||
_lock.unlock()
|
||||
}
|
||||
// }
|
||||
|
||||
private func enqueue(_ action: I) -> I? {
|
||||
_lock.lock(); defer { _lock.unlock() } // {
|
||||
if _hasFaulted {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _isExecuting {
|
||||
_queue.enqueue(action)
|
||||
return nil
|
||||
}
|
||||
|
||||
_isExecuting = true
|
||||
|
||||
return action
|
||||
// }
|
||||
}
|
||||
|
||||
private func dequeue() -> I? {
|
||||
_lock.lock(); defer { _lock.unlock() } // {
|
||||
if _queue.count > 0 {
|
||||
return _queue.dequeue()
|
||||
}
|
||||
else {
|
||||
_isExecuting = false
|
||||
return nil
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
func invoke(_ action: I) {
|
||||
let firstEnqueuedAction = enqueue(action)
|
||||
|
||||
if let firstEnqueuedAction = firstEnqueuedAction {
|
||||
firstEnqueuedAction.invoke()
|
||||
}
|
||||
else {
|
||||
// action is enqueued, it's somebody else's concern now
|
||||
return
|
||||
}
|
||||
|
||||
while true {
|
||||
let nextAction = dequeue()
|
||||
|
||||
if let nextAction = nextAction {
|
||||
nextAction.invoke()
|
||||
}
|
||||
else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dispose() {
|
||||
synchronizedDispose()
|
||||
}
|
||||
|
||||
func _synchronized_dispose() {
|
||||
_queue = Queue(capacity: 0)
|
||||
_hasFaulted = true
|
||||
}
|
||||
}
|
||||
107
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Concurrency/Lock.swift
generated
Normal file
107
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Concurrency/Lock.swift
generated
Normal file
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// Lock.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/31/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol Lock {
|
||||
func lock()
|
||||
func unlock()
|
||||
}
|
||||
|
||||
#if os(Linux)
|
||||
import Glibc
|
||||
|
||||
/**
|
||||
Simple wrapper for spin lock.
|
||||
*/
|
||||
class SpinLock {
|
||||
private var _lock: pthread_spinlock_t = 0
|
||||
|
||||
init() {
|
||||
if (pthread_spin_init(&_lock, 0) != 0) {
|
||||
fatalError("Spin lock initialization failed")
|
||||
}
|
||||
}
|
||||
|
||||
func lock() {
|
||||
pthread_spin_lock(&_lock)
|
||||
}
|
||||
|
||||
func unlock() {
|
||||
pthread_spin_unlock(&_lock)
|
||||
}
|
||||
|
||||
func performLocked(@noescape action: () -> Void) {
|
||||
lock(); defer { unlock() }
|
||||
action()
|
||||
}
|
||||
|
||||
func calculateLocked<T>(@noescape action: () -> T) -> T {
|
||||
lock(); defer { unlock() }
|
||||
return action()
|
||||
}
|
||||
|
||||
func calculateLockedOrFail<T>(@noescape action: () throws -> T) throws -> T {
|
||||
lock(); defer { unlock() }
|
||||
let result = try action()
|
||||
return result
|
||||
}
|
||||
|
||||
deinit {
|
||||
pthread_spin_destroy(&_lock)
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
// https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000321.html
|
||||
typealias SpinLock = NSRecursiveLock
|
||||
#endif
|
||||
|
||||
extension NSRecursiveLock : Lock {
|
||||
func performLocked(_ action: () -> Void) {
|
||||
lock(); defer { unlock() }
|
||||
action()
|
||||
}
|
||||
|
||||
func calculateLocked<T>(_ action: () -> T) -> T {
|
||||
lock(); defer { unlock() }
|
||||
return action()
|
||||
}
|
||||
|
||||
func calculateLockedOrFail<T>(_ action: () throws -> T) throws -> T {
|
||||
lock(); defer { unlock() }
|
||||
let result = try action()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
let RECURSIVE_MUTEX = _initializeRecursiveMutex()
|
||||
|
||||
func _initializeRecursiveMutex() -> pthread_mutex_t {
|
||||
var mutex: pthread_mutex_t = pthread_mutex_t()
|
||||
var mta: pthread_mutexattr_t = pthread_mutexattr_t()
|
||||
|
||||
pthread_mutex_init(&mutex, nil)
|
||||
pthread_mutexattr_init(&mta)
|
||||
pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE)
|
||||
pthread_mutex_init(&mutex, &mta)
|
||||
|
||||
return mutex
|
||||
}
|
||||
|
||||
extension pthread_mutex_t {
|
||||
mutating func lock() {
|
||||
pthread_mutex_lock(&self)
|
||||
}
|
||||
|
||||
mutating func unlock() {
|
||||
pthread_mutex_unlock(&self)
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// LockOwnerType.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 10/25/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol LockOwnerType : class, Lock {
|
||||
var _lock: NSRecursiveLock { get }
|
||||
}
|
||||
|
||||
extension LockOwnerType {
|
||||
func lock() {
|
||||
_lock.lock()
|
||||
}
|
||||
|
||||
func unlock() {
|
||||
_lock.unlock()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// SynchronizedDisposeType.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 10/25/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SynchronizedDisposeType : class, Disposable, Lock {
|
||||
func _synchronized_dispose()
|
||||
}
|
||||
|
||||
extension SynchronizedDisposeType {
|
||||
func synchronizedDispose() {
|
||||
lock(); defer { unlock() }
|
||||
_synchronized_dispose()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// SynchronizedOnType.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 10/25/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SynchronizedOnType : class, ObserverType, Lock {
|
||||
func _synchronized_on(_ event: Event<E>)
|
||||
}
|
||||
|
||||
extension SynchronizedOnType {
|
||||
func synchronizedOn(_ event: Event<E>) {
|
||||
lock(); defer { unlock() }
|
||||
_synchronized_on(event)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// SynchronizedSubscribeType.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 10/25/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SynchronizedSubscribeType : class, ObservableType, Lock {
|
||||
func _synchronized_subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E
|
||||
}
|
||||
|
||||
extension SynchronizedSubscribeType {
|
||||
func synchronizedSubscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E {
|
||||
lock(); defer { unlock() }
|
||||
return _synchronized_subscribe(observer)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// SynchronizedUnsubscribeType.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 10/25/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SynchronizedUnsubscribeType : class {
|
||||
associatedtype DisposeKey
|
||||
|
||||
func synchronizedUnsubscribe(_ disposeKey: DisposeKey)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// ConnectableObservableType.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/1/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents an observable sequence wrapper that can be connected and disconnected from its underlying observable sequence.
|
||||
*/
|
||||
public protocol ConnectableObservableType : ObservableType {
|
||||
/**
|
||||
Connects the observable wrapper to its source. All subscribed observers will receive values from the underlying observable sequence as long as the connection is established.
|
||||
|
||||
- returns: Disposable used to disconnect the observable wrapper from its source, causing subscribed observer to stop receiving values from the underlying observable sequence.
|
||||
*/
|
||||
func connect() -> Disposable
|
||||
}
|
||||
336
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/DataStructures/Bag.swift
generated
Normal file
336
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/DataStructures/Bag.swift
generated
Normal file
@@ -0,0 +1,336 @@
|
||||
//
|
||||
// Bag.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/28/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Swift
|
||||
|
||||
let arrayDictionaryMaxSize = 30
|
||||
|
||||
/**
|
||||
Class that enables using memory allocations as a means to uniquely identify objects.
|
||||
*/
|
||||
class Identity {
|
||||
// weird things have known to happen with Swift
|
||||
var _forceAllocation: Int32 = 0
|
||||
}
|
||||
|
||||
func hash(_ _x: Int) -> Int {
|
||||
var x = _x
|
||||
x = ((x >> 16) ^ x) &* 0x45d9f3b
|
||||
x = ((x >> 16) ^ x) &* 0x45d9f3b
|
||||
x = ((x >> 16) ^ x)
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
Unique identifier for object added to `Bag`.
|
||||
*/
|
||||
public struct BagKey : Hashable {
|
||||
let uniqueIdentity: Identity?
|
||||
let key: Int
|
||||
|
||||
public var hashValue: Int {
|
||||
if let uniqueIdentity = uniqueIdentity {
|
||||
return hash(key) ^ (ObjectIdentifier(uniqueIdentity).hashValue)
|
||||
}
|
||||
else {
|
||||
return hash(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Compares two `BagKey`s.
|
||||
*/
|
||||
public func == (lhs: BagKey, rhs: BagKey) -> Bool {
|
||||
return lhs.key == rhs.key && lhs.uniqueIdentity === rhs.uniqueIdentity
|
||||
}
|
||||
|
||||
/**
|
||||
Data structure that represents a bag of elements typed `T`.
|
||||
|
||||
Single element can be stored multiple times.
|
||||
|
||||
Time and space complexity of insertion an deletion is O(n).
|
||||
|
||||
It is suitable for storing small number of elements.
|
||||
*/
|
||||
public struct Bag<T> : CustomDebugStringConvertible {
|
||||
/**
|
||||
Type of identifier for inserted elements.
|
||||
*/
|
||||
public typealias KeyType = BagKey
|
||||
|
||||
fileprivate typealias ScopeUniqueTokenType = Int
|
||||
|
||||
typealias Entry = (key: BagKey, value: T)
|
||||
|
||||
fileprivate var _uniqueIdentity: Identity?
|
||||
fileprivate var _nextKey: ScopeUniqueTokenType = 0
|
||||
|
||||
// data
|
||||
|
||||
// first fill inline variables
|
||||
fileprivate var _key0: BagKey? = nil
|
||||
fileprivate var _value0: T? = nil
|
||||
|
||||
fileprivate var _key1: BagKey? = nil
|
||||
fileprivate var _value1: T? = nil
|
||||
|
||||
// then fill "array dictionary"
|
||||
fileprivate var _pairs = ContiguousArray<Entry>()
|
||||
|
||||
// last is sparse dictionary
|
||||
fileprivate var _dictionary: [BagKey : T]? = nil
|
||||
|
||||
fileprivate var _onlyFastPath = true
|
||||
|
||||
/**
|
||||
Creates new empty `Bag`.
|
||||
*/
|
||||
public init() {
|
||||
}
|
||||
|
||||
/**
|
||||
Inserts `value` into bag.
|
||||
|
||||
- parameter element: Element to insert.
|
||||
- returns: Key that can be used to remove element from bag.
|
||||
*/
|
||||
public mutating func insert(_ element: T) -> BagKey {
|
||||
_nextKey = _nextKey &+ 1
|
||||
|
||||
#if DEBUG
|
||||
_nextKey = _nextKey % 20
|
||||
#endif
|
||||
|
||||
if _nextKey == 0 {
|
||||
_uniqueIdentity = Identity()
|
||||
}
|
||||
|
||||
let key = BagKey(uniqueIdentity: _uniqueIdentity, key: _nextKey)
|
||||
|
||||
if _key0 == nil {
|
||||
_key0 = key
|
||||
_value0 = element
|
||||
return key
|
||||
}
|
||||
|
||||
_onlyFastPath = false
|
||||
|
||||
if _key1 == nil {
|
||||
_key1 = key
|
||||
_value1 = element
|
||||
return key
|
||||
}
|
||||
|
||||
if _dictionary != nil {
|
||||
_dictionary![key] = element
|
||||
return key
|
||||
}
|
||||
|
||||
if _pairs.count < arrayDictionaryMaxSize {
|
||||
_pairs.append(key: key, value: element)
|
||||
return key
|
||||
}
|
||||
|
||||
if _dictionary == nil {
|
||||
_dictionary = [:]
|
||||
}
|
||||
|
||||
_dictionary![key] = element
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
/**
|
||||
- returns: Number of elements in bag.
|
||||
*/
|
||||
public var count: Int {
|
||||
let dictionaryCount: Int = _dictionary?.count ?? 0
|
||||
return _pairs.count + (_value0 != nil ? 1 : 0) + (_value1 != nil ? 1 : 0) + dictionaryCount
|
||||
}
|
||||
|
||||
/**
|
||||
Removes all elements from bag and clears capacity.
|
||||
*/
|
||||
public mutating func removeAll() {
|
||||
_key0 = nil
|
||||
_value0 = nil
|
||||
_key1 = nil
|
||||
_value1 = nil
|
||||
|
||||
_pairs.removeAll(keepingCapacity: false)
|
||||
_dictionary?.removeAll(keepingCapacity: false)
|
||||
}
|
||||
|
||||
/**
|
||||
Removes element with a specific `key` from bag.
|
||||
|
||||
- parameter key: Key that identifies element to remove from bag.
|
||||
- returns: Element that bag contained, or nil in case element was already removed.
|
||||
*/
|
||||
public mutating func removeKey(_ key: BagKey) -> T? {
|
||||
if _key0 == key {
|
||||
_key0 = nil
|
||||
let value = _value0!
|
||||
_value0 = nil
|
||||
return value
|
||||
}
|
||||
|
||||
if _key1 == key {
|
||||
_key1 = nil
|
||||
let value = _value1!
|
||||
_value1 = nil
|
||||
return value
|
||||
}
|
||||
|
||||
if let existingObject = _dictionary?.removeValue(forKey: key) {
|
||||
return existingObject
|
||||
}
|
||||
|
||||
for i in 0 ..< _pairs.count {
|
||||
if _pairs[i].key == key {
|
||||
let value = _pairs[i].value
|
||||
_pairs.remove(at: i)
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension Bag {
|
||||
/**
|
||||
A textual representation of `self`, suitable for debugging.
|
||||
*/
|
||||
public var debugDescription : String {
|
||||
return "\(self.count) elements in Bag"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: forEach
|
||||
|
||||
extension Bag {
|
||||
/**
|
||||
Enumerates elements inside the bag.
|
||||
|
||||
- parameter action: Enumeration closure.
|
||||
*/
|
||||
public func forEach(_ action: (T) -> Void) {
|
||||
if _onlyFastPath {
|
||||
if let value0 = _value0 {
|
||||
action(value0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let pairs = _pairs
|
||||
let value0 = _value0
|
||||
let value1 = _value1
|
||||
let dictionary = _dictionary
|
||||
|
||||
if let value0 = value0 {
|
||||
action(value0)
|
||||
}
|
||||
|
||||
if let value1 = value1 {
|
||||
action(value1)
|
||||
}
|
||||
|
||||
for i in 0 ..< pairs.count {
|
||||
action(pairs[i].value)
|
||||
}
|
||||
|
||||
if dictionary?.count ?? 0 > 0 {
|
||||
for element in dictionary!.values {
|
||||
action(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Bag where T: ObserverType {
|
||||
/**
|
||||
Dispatches `event` to app observers contained inside bag.
|
||||
|
||||
- parameter action: Enumeration closure.
|
||||
*/
|
||||
public func on(_ event: Event<T.E>) {
|
||||
if _onlyFastPath {
|
||||
_value0?.on(event)
|
||||
return
|
||||
}
|
||||
|
||||
let pairs = _pairs
|
||||
let value0 = _value0
|
||||
let value1 = _value1
|
||||
let dictionary = _dictionary
|
||||
|
||||
if let value0 = value0 {
|
||||
value0.on(event)
|
||||
}
|
||||
|
||||
if let value1 = value1 {
|
||||
value1.on(event)
|
||||
}
|
||||
|
||||
for i in 0 ..< pairs.count {
|
||||
pairs[i].value.on(event)
|
||||
}
|
||||
|
||||
if dictionary?.count ?? 0 > 0 {
|
||||
for element in dictionary!.values {
|
||||
element.on(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Dispatches `dispose` to all disposables contained inside bag.
|
||||
*/
|
||||
@available(*, deprecated, renamed: "disposeAll(in:)")
|
||||
public func disposeAllIn(_ bag: Bag<Disposable>) {
|
||||
disposeAll(in: bag)
|
||||
}
|
||||
|
||||
/**
|
||||
Dispatches `dispose` to all disposables contained inside bag.
|
||||
*/
|
||||
public func disposeAll(in bag: Bag<Disposable>) {
|
||||
if bag._onlyFastPath {
|
||||
bag._value0?.dispose()
|
||||
return
|
||||
}
|
||||
|
||||
let pairs = bag._pairs
|
||||
let value0 = bag._value0
|
||||
let value1 = bag._value1
|
||||
let dictionary = bag._dictionary
|
||||
|
||||
if let value0 = value0 {
|
||||
value0.dispose()
|
||||
}
|
||||
|
||||
if let value1 = value1 {
|
||||
value1.dispose()
|
||||
}
|
||||
|
||||
for i in 0 ..< pairs.count {
|
||||
pairs[i].value.dispose()
|
||||
}
|
||||
|
||||
if dictionary?.count ?? 0 > 0 {
|
||||
for element in dictionary!.values {
|
||||
element.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// InfiniteSequence.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 6/13/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Sequence that repeats `repeatedValue` infinite number of times.
|
||||
*/
|
||||
struct InfiniteSequence<E> : Sequence {
|
||||
typealias Element = E
|
||||
typealias Iterator = AnyIterator<E>
|
||||
|
||||
private let _repeatedValue: E
|
||||
|
||||
init(repeatedValue: E) {
|
||||
_repeatedValue = repeatedValue
|
||||
}
|
||||
|
||||
func makeIterator() -> Iterator {
|
||||
let repeatedValue = _repeatedValue
|
||||
return AnyIterator {
|
||||
return repeatedValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
//
|
||||
// PriorityQueue.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/27/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct PriorityQueue<Element: AnyObject> {
|
||||
private let _hasHigherPriority: (Element, Element) -> Bool
|
||||
fileprivate var _elements = [Element]()
|
||||
|
||||
init(hasHigherPriority: @escaping (Element, Element) -> Bool) {
|
||||
_hasHigherPriority = hasHigherPriority
|
||||
}
|
||||
|
||||
mutating func enqueue(_ element: Element) {
|
||||
_elements.append(element)
|
||||
bubbleToHigherPriority(_elements.count - 1)
|
||||
}
|
||||
|
||||
func peek() -> Element? {
|
||||
return _elements.first
|
||||
}
|
||||
|
||||
var isEmpty: Bool {
|
||||
return _elements.count == 0
|
||||
}
|
||||
|
||||
mutating func dequeue() -> Element? {
|
||||
guard let front = peek() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
removeAt(0)
|
||||
|
||||
return front
|
||||
}
|
||||
|
||||
mutating func remove(_ element: Element) {
|
||||
for i in 0 ..< _elements.count {
|
||||
if _elements[i] === element {
|
||||
removeAt(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private mutating func removeAt(_ index: Int) {
|
||||
let removingLast = index == _elements.count - 1
|
||||
if !removingLast {
|
||||
swap(&_elements[index], &_elements[_elements.count - 1])
|
||||
}
|
||||
|
||||
_ = _elements.popLast()
|
||||
|
||||
if !removingLast {
|
||||
bubbleToHigherPriority(index)
|
||||
bubbleToLowerPriority(index)
|
||||
}
|
||||
}
|
||||
|
||||
private mutating func bubbleToHigherPriority(_ initialUnbalancedIndex: Int) {
|
||||
precondition(initialUnbalancedIndex >= 0)
|
||||
precondition(initialUnbalancedIndex < _elements.count)
|
||||
|
||||
var unbalancedIndex = initialUnbalancedIndex
|
||||
|
||||
while unbalancedIndex > 0 {
|
||||
let parentIndex = (unbalancedIndex - 1) / 2
|
||||
|
||||
if _hasHigherPriority(_elements[unbalancedIndex], _elements[parentIndex]) {
|
||||
swap(&_elements[unbalancedIndex], &_elements[parentIndex])
|
||||
|
||||
unbalancedIndex = parentIndex
|
||||
}
|
||||
else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private mutating func bubbleToLowerPriority(_ initialUnbalancedIndex: Int) {
|
||||
precondition(initialUnbalancedIndex >= 0)
|
||||
precondition(initialUnbalancedIndex < _elements.count)
|
||||
|
||||
var unbalancedIndex = initialUnbalancedIndex
|
||||
repeat {
|
||||
let leftChildIndex = unbalancedIndex * 2 + 1
|
||||
let rightChildIndex = unbalancedIndex * 2 + 2
|
||||
|
||||
var highestPriorityIndex = unbalancedIndex
|
||||
|
||||
if leftChildIndex < _elements.count && _hasHigherPriority(_elements[leftChildIndex], _elements[highestPriorityIndex]) {
|
||||
highestPriorityIndex = leftChildIndex
|
||||
}
|
||||
|
||||
if rightChildIndex < _elements.count && _hasHigherPriority(_elements[rightChildIndex], _elements[highestPriorityIndex]) {
|
||||
highestPriorityIndex = rightChildIndex
|
||||
}
|
||||
|
||||
if highestPriorityIndex != unbalancedIndex {
|
||||
swap(&_elements[highestPriorityIndex], &_elements[unbalancedIndex])
|
||||
|
||||
unbalancedIndex = highestPriorityIndex
|
||||
}
|
||||
else {
|
||||
break
|
||||
}
|
||||
} while true
|
||||
}
|
||||
}
|
||||
|
||||
extension PriorityQueue : CustomDebugStringConvertible {
|
||||
var debugDescription: String {
|
||||
return _elements.debugDescription
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// Queue.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/21/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Data structure that represents queue.
|
||||
|
||||
Complexity of `enqueue`, `dequeue` is O(1) when number of operations is
|
||||
averaged over N operations.
|
||||
|
||||
Complexity of `peek` is O(1).
|
||||
*/
|
||||
public struct Queue<T>: Sequence {
|
||||
/**
|
||||
Type of generator.
|
||||
*/
|
||||
public typealias Generator = AnyIterator<T>
|
||||
|
||||
private let _resizeFactor = 2
|
||||
|
||||
private var _storage: ContiguousArray<T?>
|
||||
private var _count = 0
|
||||
private var _pushNextIndex = 0
|
||||
private var _initialCapacity: Int
|
||||
|
||||
/**
|
||||
Creates new queue.
|
||||
|
||||
- parameter capacity: Capacity of newly created queue.
|
||||
*/
|
||||
public init(capacity: Int) {
|
||||
_initialCapacity = capacity
|
||||
|
||||
_storage = ContiguousArray<T?>(repeating: nil, count: capacity)
|
||||
}
|
||||
|
||||
private var dequeueIndex: Int {
|
||||
let index = _pushNextIndex - count
|
||||
return index < 0 ? index + _storage.count : index
|
||||
}
|
||||
|
||||
/**
|
||||
- returns: Is queue empty.
|
||||
*/
|
||||
public var isEmpty: Bool {
|
||||
return count == 0
|
||||
}
|
||||
|
||||
/**
|
||||
- returns: Number of elements inside queue.
|
||||
*/
|
||||
public var count: Int {
|
||||
return _count
|
||||
}
|
||||
|
||||
/**
|
||||
- returns: Element in front of a list of elements to `dequeue`.
|
||||
*/
|
||||
public func peek() -> T {
|
||||
precondition(count > 0)
|
||||
|
||||
return _storage[dequeueIndex]!
|
||||
}
|
||||
|
||||
mutating private func resizeTo(_ size: Int) {
|
||||
var newStorage = ContiguousArray<T?>(repeating: nil, count: size)
|
||||
|
||||
let count = _count
|
||||
|
||||
let dequeueIndex = self.dequeueIndex
|
||||
let spaceToEndOfQueue = _storage.count - dequeueIndex
|
||||
|
||||
// first batch is from dequeue index to end of array
|
||||
let countElementsInFirstBatch = Swift.min(count, spaceToEndOfQueue)
|
||||
// second batch is wrapped from start of array to end of queue
|
||||
let numberOfElementsInSecondBatch = count - countElementsInFirstBatch
|
||||
|
||||
newStorage[0 ..< countElementsInFirstBatch] = _storage[dequeueIndex ..< (dequeueIndex + countElementsInFirstBatch)]
|
||||
newStorage[countElementsInFirstBatch ..< (countElementsInFirstBatch + numberOfElementsInSecondBatch)] = _storage[0 ..< numberOfElementsInSecondBatch]
|
||||
|
||||
_count = count
|
||||
_pushNextIndex = count
|
||||
_storage = newStorage
|
||||
}
|
||||
|
||||
/**
|
||||
Enqueues `element`.
|
||||
|
||||
- parameter element: Element to enqueue.
|
||||
*/
|
||||
public mutating func enqueue(_ element: T) {
|
||||
if count == _storage.count {
|
||||
resizeTo(Swift.max(_storage.count, 1) * _resizeFactor)
|
||||
}
|
||||
|
||||
_storage[_pushNextIndex] = element
|
||||
_pushNextIndex += 1
|
||||
_count += 1
|
||||
|
||||
if _pushNextIndex >= _storage.count {
|
||||
_pushNextIndex -= _storage.count
|
||||
}
|
||||
}
|
||||
|
||||
private mutating func dequeueElementOnly() -> T {
|
||||
precondition(count > 0)
|
||||
|
||||
let index = dequeueIndex
|
||||
|
||||
defer {
|
||||
_storage[index] = nil
|
||||
_count -= 1
|
||||
}
|
||||
|
||||
return _storage[index]!
|
||||
}
|
||||
|
||||
/**
|
||||
Dequeues element or throws an exception in case queue is empty.
|
||||
|
||||
- returns: Dequeued element.
|
||||
*/
|
||||
public mutating func dequeue() -> T? {
|
||||
if self.count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer {
|
||||
let downsizeLimit = _storage.count / (_resizeFactor * _resizeFactor)
|
||||
if _count < downsizeLimit && downsizeLimit >= _initialCapacity {
|
||||
resizeTo(_storage.count / _resizeFactor)
|
||||
}
|
||||
}
|
||||
|
||||
return dequeueElementOnly()
|
||||
}
|
||||
|
||||
/**
|
||||
- returns: Generator of contained elements.
|
||||
*/
|
||||
public func makeIterator() -> AnyIterator<T> {
|
||||
var i = dequeueIndex
|
||||
var count = _count
|
||||
|
||||
return AnyIterator {
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer {
|
||||
count -= 1
|
||||
i += 1
|
||||
}
|
||||
|
||||
if i >= self._storage.count {
|
||||
i -= self._storage.count
|
||||
}
|
||||
|
||||
return self._storage[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
15
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Disposable.swift
generated
Normal file
15
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Disposable.swift
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Disposable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/8/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Respresents a disposable resource.
|
||||
public protocol Disposable {
|
||||
/// Dispose resource.
|
||||
func dispose()
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// AnonymousDisposable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/15/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents an Action-based disposable.
|
||||
|
||||
When dispose method is called, disposal action will be dereferenced.
|
||||
*/
|
||||
public final class AnonymousDisposable : DisposeBase, Cancelable {
|
||||
public typealias DisposeAction = () -> Void
|
||||
|
||||
private var _isDisposed: AtomicInt = 0
|
||||
private var _disposeAction: DisposeAction?
|
||||
|
||||
/**
|
||||
- returns: Was resource disposed.
|
||||
*/
|
||||
public var isDisposed: Bool {
|
||||
return _isDisposed == 1
|
||||
}
|
||||
|
||||
/**
|
||||
Constructs a new disposable with the given action used for disposal.
|
||||
|
||||
- parameter disposeAction: Disposal action which will be run upon calling `dispose`.
|
||||
*/
|
||||
@available(*, deprecated, renamed: "Disposables.create")
|
||||
public init(_ disposeAction: @escaping DisposeAction) {
|
||||
_disposeAction = disposeAction
|
||||
super.init()
|
||||
}
|
||||
|
||||
// Non-deprecated version of the constructor, used by `Disposables.create(with:)`
|
||||
fileprivate init(disposeAction: @escaping DisposeAction) {
|
||||
_disposeAction = disposeAction
|
||||
super.init()
|
||||
}
|
||||
|
||||
/**
|
||||
Calls the disposal action if and only if the current instance hasn't been disposed yet.
|
||||
|
||||
After invoking disposal action, disposal action will be dereferenced.
|
||||
*/
|
||||
public func dispose() {
|
||||
if AtomicCompareAndSwap(0, 1, &_isDisposed) {
|
||||
assert(_isDisposed == 1)
|
||||
|
||||
if let action = _disposeAction {
|
||||
_disposeAction = nil
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Disposables {
|
||||
|
||||
/**
|
||||
Constructs a new disposable with the given action used for disposal.
|
||||
|
||||
- parameter dispose: Disposal action which will be run upon calling `dispose`.
|
||||
*/
|
||||
static func create(with dispose: @escaping () -> ()) -> Cancelable {
|
||||
return AnonymousDisposable(disposeAction: dispose)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// BinaryDisposable.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 6/12/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents two disposable resources that are disposed together.
|
||||
*/
|
||||
private final class BinaryDisposable : DisposeBase, Cancelable {
|
||||
|
||||
private var _isDisposed: AtomicInt = 0
|
||||
|
||||
// state
|
||||
private var _disposable1: Disposable?
|
||||
private var _disposable2: Disposable?
|
||||
|
||||
/**
|
||||
- returns: Was resource disposed.
|
||||
*/
|
||||
var isDisposed: Bool {
|
||||
return _isDisposed > 0
|
||||
}
|
||||
|
||||
/**
|
||||
Constructs new binary disposable from two disposables.
|
||||
|
||||
- parameter disposable1: First disposable
|
||||
- parameter disposable2: Second disposable
|
||||
*/
|
||||
init(_ disposable1: Disposable, _ disposable2: Disposable) {
|
||||
_disposable1 = disposable1
|
||||
_disposable2 = disposable2
|
||||
super.init()
|
||||
}
|
||||
|
||||
/**
|
||||
Calls the disposal action if and only if the current instance hasn't been disposed yet.
|
||||
|
||||
After invoking disposal action, disposal action will be dereferenced.
|
||||
*/
|
||||
func dispose() {
|
||||
if AtomicCompareAndSwap(0, 1, &_isDisposed) {
|
||||
_disposable1?.dispose()
|
||||
_disposable2?.dispose()
|
||||
_disposable1 = nil
|
||||
_disposable2 = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Disposables {
|
||||
|
||||
/**
|
||||
Creates a disposable with the given disposables.
|
||||
*/
|
||||
static func create(_ disposable1: Disposable, _ disposable2: Disposable) -> Cancelable {
|
||||
return BinaryDisposable(disposable1, disposable2)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// BooleanDisposable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Junior B. on 10/29/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents a disposable resource that can be checked for disposal status.
|
||||
*/
|
||||
public final class BooleanDisposable : Disposable, Cancelable {
|
||||
|
||||
internal static let BooleanDisposableTrue = BooleanDisposable(isDisposed: true)
|
||||
private var _isDisposed = false
|
||||
|
||||
/**
|
||||
Initializes a new instance of the `BooleanDisposable` class
|
||||
*/
|
||||
public init() {
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a new instance of the `BooleanDisposable` class with given value
|
||||
*/
|
||||
public init(isDisposed: Bool) {
|
||||
self._isDisposed = isDisposed
|
||||
}
|
||||
|
||||
/**
|
||||
- returns: Was resource disposed.
|
||||
*/
|
||||
public var isDisposed: Bool {
|
||||
return _isDisposed
|
||||
}
|
||||
|
||||
/**
|
||||
Sets the status to disposed, which can be observer through the `isDisposed` property.
|
||||
*/
|
||||
public func dispose() {
|
||||
_isDisposed = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// CompositeDisposable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/20/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents a group of disposable resources that are disposed together.
|
||||
*/
|
||||
public final class CompositeDisposable : DisposeBase, Disposable, Cancelable {
|
||||
public typealias DisposeKey = Bag<Disposable>.KeyType
|
||||
|
||||
private var _lock = SpinLock()
|
||||
|
||||
// state
|
||||
private var _disposables: Bag<Disposable>? = Bag()
|
||||
|
||||
public var isDisposed: Bool {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
return _disposables == nil
|
||||
}
|
||||
|
||||
public override init() {
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a new instance of composite disposable with the specified number of disposables.
|
||||
*/
|
||||
public init(_ disposable1: Disposable, _ disposable2: Disposable) {
|
||||
// This overload is here to make sure we are using optimized version up to 4 arguments.
|
||||
let _ = _disposables!.insert(disposable1)
|
||||
let _ = _disposables!.insert(disposable2)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a new instance of composite disposable with the specified number of disposables.
|
||||
*/
|
||||
public init(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable) {
|
||||
// This overload is here to make sure we are using optimized version up to 4 arguments.
|
||||
let _ = _disposables!.insert(disposable1)
|
||||
let _ = _disposables!.insert(disposable2)
|
||||
let _ = _disposables!.insert(disposable3)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a new instance of composite disposable with the specified number of disposables.
|
||||
*/
|
||||
public init(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable, _ disposable4: Disposable, _ disposables: Disposable...) {
|
||||
// This overload is here to make sure we are using optimized version up to 4 arguments.
|
||||
let _ = _disposables!.insert(disposable1)
|
||||
let _ = _disposables!.insert(disposable2)
|
||||
let _ = _disposables!.insert(disposable3)
|
||||
let _ = _disposables!.insert(disposable4)
|
||||
|
||||
for disposable in disposables {
|
||||
let _ = _disposables!.insert(disposable)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a new instance of composite disposable with the specified number of disposables.
|
||||
*/
|
||||
public init(disposables: [Disposable]) {
|
||||
for disposable in disposables {
|
||||
let _ = _disposables!.insert(disposable)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Adds a disposable to the CompositeDisposable or disposes the disposable if the CompositeDisposable is disposed.
|
||||
|
||||
- parameter disposable: Disposable to add.
|
||||
- returns: Key that can be used to remove disposable from composite disposable. In case dispose bag was already
|
||||
disposed `nil` will be returned.
|
||||
*/
|
||||
@available(*, deprecated, renamed: "insert(_:)")
|
||||
public func addDisposable(_ disposable: Disposable) -> DisposeKey? {
|
||||
return insert(disposable)
|
||||
}
|
||||
|
||||
/**
|
||||
Adds a disposable to the CompositeDisposable or disposes the disposable if the CompositeDisposable is disposed.
|
||||
|
||||
- parameter disposable: Disposable to add.
|
||||
- returns: Key that can be used to remove disposable from composite disposable. In case dispose bag was already
|
||||
disposed `nil` will be returned.
|
||||
*/
|
||||
public func insert(_ disposable: Disposable) -> DisposeKey? {
|
||||
let key = _insert(disposable)
|
||||
|
||||
if key == nil {
|
||||
disposable.dispose()
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
private func _insert(_ disposable: Disposable) -> DisposeKey? {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
|
||||
return _disposables?.insert(disposable)
|
||||
}
|
||||
|
||||
/**
|
||||
- returns: Gets the number of disposables contained in the `CompositeDisposable`.
|
||||
*/
|
||||
public var count: Int {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
return _disposables?.count ?? 0
|
||||
}
|
||||
|
||||
/**
|
||||
Removes and disposes the disposable identified by `disposeKey` from the CompositeDisposable.
|
||||
|
||||
- parameter disposeKey: Key used to identify disposable to be removed.
|
||||
*/
|
||||
@available(*, deprecated, renamed: "remove(for:)")
|
||||
public func removeDisposable(_ disposeKey: DisposeKey) {
|
||||
remove(for: disposeKey)
|
||||
}
|
||||
|
||||
/**
|
||||
Removes and disposes the disposable identified by `disposeKey` from the CompositeDisposable.
|
||||
|
||||
- parameter disposeKey: Key used to identify disposable to be removed.
|
||||
*/
|
||||
public func remove(for disposeKey: DisposeKey) {
|
||||
_remove(for: disposeKey)?.dispose()
|
||||
}
|
||||
|
||||
private func _remove(for disposeKey: DisposeKey) -> Disposable? {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
return _disposables?.removeKey(disposeKey)
|
||||
}
|
||||
|
||||
/**
|
||||
Disposes all disposables in the group and removes them from the group.
|
||||
*/
|
||||
public func dispose() {
|
||||
if let disposables = _dispose() {
|
||||
disposeAll(in: disposables)
|
||||
}
|
||||
}
|
||||
|
||||
private func _dispose() -> Bag<Disposable>? {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
|
||||
let disposeBag = _disposables
|
||||
_disposables = nil
|
||||
|
||||
return disposeBag
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Disposables.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Mohsen Ramezanpoor on 01/08/2016.
|
||||
// Copyright © 2016 Mohsen Ramezanpoor. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
A collection of utility methods for common disposable operations.
|
||||
*/
|
||||
public struct Disposables {
|
||||
|
||||
private init() {}
|
||||
|
||||
}
|
||||
|
||||
public extension Disposables {
|
||||
|
||||
private static let noOp: Disposable = NopDisposable()
|
||||
|
||||
/**
|
||||
Creates a disposable that does nothing on disposal.
|
||||
*/
|
||||
static func create() -> Disposable {
|
||||
return noOp
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a disposable with the given disposables.
|
||||
*/
|
||||
static func create(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable) -> Cancelable {
|
||||
return CompositeDisposable(disposable1, disposable2, disposable3)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a disposable with the given disposables.
|
||||
*/
|
||||
static func create(_ disposable1: Disposable, _ disposable2: Disposable, _ disposable3: Disposable, _ disposables: Disposable ...) -> Cancelable {
|
||||
var disposables = disposables
|
||||
disposables.append(disposable1)
|
||||
disposables.append(disposable2)
|
||||
disposables.append(disposable3)
|
||||
return CompositeDisposable(disposables: disposables)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a disposable with the given disposables.
|
||||
*/
|
||||
static func create(_ disposables: [Disposable]) -> Cancelable {
|
||||
switch disposables.count {
|
||||
case 2:
|
||||
return Disposables.create(disposables[0], disposables[1])
|
||||
default:
|
||||
return CompositeDisposable(disposables: disposables)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// DisposeBag.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/25/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Disposable {
|
||||
/**
|
||||
Adds `self` to `bag`.
|
||||
|
||||
- parameter bag: `DisposeBag` to add `self` to.
|
||||
*/
|
||||
public func addDisposableTo(_ bag: DisposeBag) {
|
||||
bag.insert(self)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Thread safe bag that disposes added disposables on `deinit`.
|
||||
|
||||
This returns ARC (RAII) like resource management to `RxSwift`.
|
||||
|
||||
In case contained disposables need to be disposed, just put a different dispose bag
|
||||
or create a new one in its place.
|
||||
|
||||
self.existingDisposeBag = DisposeBag()
|
||||
|
||||
In case explicit disposal is necessary, there is also `CompositeDisposable`.
|
||||
*/
|
||||
public final class DisposeBag: DisposeBase {
|
||||
|
||||
private var _lock = SpinLock()
|
||||
|
||||
// state
|
||||
private var _disposables = [Disposable]()
|
||||
private var _isDisposed = false
|
||||
|
||||
/**
|
||||
Constructs new empty dispose bag.
|
||||
*/
|
||||
public override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
/**
|
||||
Adds `disposable` to be disposed when dispose bag is being deinited.
|
||||
|
||||
- parameter disposable: Disposable to add.
|
||||
*/
|
||||
@available(*, deprecated, renamed: "insert(_:)")
|
||||
public func addDisposable(_ disposable: Disposable) {
|
||||
insert(disposable)
|
||||
}
|
||||
|
||||
/**
|
||||
Adds `disposable` to be disposed when dispose bag is being deinited.
|
||||
|
||||
- parameter disposable: Disposable to add.
|
||||
*/
|
||||
public func insert(_ disposable: Disposable) {
|
||||
_insert(disposable)?.dispose()
|
||||
}
|
||||
|
||||
private func _insert(_ disposable: Disposable) -> Disposable? {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
if _isDisposed {
|
||||
return disposable
|
||||
}
|
||||
|
||||
_disposables.append(disposable)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
This is internal on purpose, take a look at `CompositeDisposable` instead.
|
||||
*/
|
||||
private func dispose() {
|
||||
let oldDisposables = _dispose()
|
||||
|
||||
for disposable in oldDisposables {
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private func _dispose() -> [Disposable] {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
|
||||
let disposables = _disposables
|
||||
|
||||
_disposables.removeAll(keepingCapacity: false)
|
||||
_isDisposed = true
|
||||
|
||||
return disposables
|
||||
}
|
||||
|
||||
deinit {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// DisposeBase.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 4/4/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Base class for all disposables.
|
||||
*/
|
||||
public class DisposeBase {
|
||||
init() {
|
||||
#if TRACE_RESOURCES
|
||||
let _ = AtomicIncrement(&resourceCount)
|
||||
#endif
|
||||
}
|
||||
|
||||
deinit {
|
||||
#if TRACE_RESOURCES
|
||||
let _ = AtomicDecrement(&resourceCount)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// NopDisposable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/15/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents a disposable that does nothing on disposal.
|
||||
|
||||
Nop = No Operation
|
||||
*/
|
||||
public struct NopDisposable : Disposable {
|
||||
|
||||
/**
|
||||
Singleton instance of `NopDisposable`.
|
||||
*/
|
||||
@available(*, deprecated, renamed: "Disposables.create()")
|
||||
public static let instance: Disposable = NopDisposable()
|
||||
|
||||
init() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Does nothing.
|
||||
*/
|
||||
public func dispose() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// RefCountDisposable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Junior B. on 10/29/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents a disposable resource that only disposes its underlying disposable resource when all dependent disposable objects have been disposed.
|
||||
*/
|
||||
public final class RefCountDisposable : DisposeBase, Cancelable {
|
||||
private var _lock = SpinLock()
|
||||
private var _disposable = nil as Disposable?
|
||||
private var _primaryDisposed = false
|
||||
private var _count = 0
|
||||
|
||||
/**
|
||||
- returns: Was resource disposed.
|
||||
*/
|
||||
public var isDisposed: Bool {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
return _disposable == nil
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a new instance of the `RefCountDisposable`.
|
||||
*/
|
||||
public init(disposable: Disposable) {
|
||||
_disposable = disposable
|
||||
super.init()
|
||||
}
|
||||
|
||||
/**
|
||||
Holds a dependent disposable that when disposed decreases the refcount on the underlying disposable.
|
||||
|
||||
When getter is called, a dependent disposable contributing to the reference count that manages the underlying disposable's lifetime is returned.
|
||||
*/
|
||||
public func retain() -> Disposable {
|
||||
return _lock.calculateLocked {
|
||||
if let _ = _disposable {
|
||||
|
||||
do {
|
||||
let _ = try incrementChecked(&_count)
|
||||
} catch (_) {
|
||||
rxFatalError("RefCountDisposable increment failed")
|
||||
}
|
||||
|
||||
return RefCountInnerDisposable(self)
|
||||
} else {
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Disposes the underlying disposable only when all dependent disposables have been disposed.
|
||||
*/
|
||||
public func dispose() {
|
||||
let oldDisposable: Disposable? = _lock.calculateLocked {
|
||||
if let oldDisposable = _disposable, !_primaryDisposed
|
||||
{
|
||||
_primaryDisposed = true
|
||||
|
||||
if (_count == 0)
|
||||
{
|
||||
_disposable = nil
|
||||
return oldDisposable
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if let disposable = oldDisposable {
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func release() {
|
||||
let oldDisposable: Disposable? = _lock.calculateLocked {
|
||||
if let oldDisposable = _disposable {
|
||||
do {
|
||||
let _ = try decrementChecked(&_count)
|
||||
} catch (_) {
|
||||
rxFatalError("RefCountDisposable decrement on release failed")
|
||||
}
|
||||
|
||||
guard _count >= 0 else {
|
||||
rxFatalError("RefCountDisposable counter is lower than 0")
|
||||
}
|
||||
|
||||
if _primaryDisposed && _count == 0 {
|
||||
_disposable = nil
|
||||
return oldDisposable
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if let disposable = oldDisposable {
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal final class RefCountInnerDisposable: DisposeBase, Disposable
|
||||
{
|
||||
private let _parent: RefCountDisposable
|
||||
private var _isDisposed: AtomicInt = 0
|
||||
|
||||
init(_ parent: RefCountDisposable)
|
||||
{
|
||||
_parent = parent
|
||||
super.init()
|
||||
}
|
||||
|
||||
internal func dispose()
|
||||
{
|
||||
if AtomicCompareAndSwap(0, 1, &_isDisposed) {
|
||||
_parent.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// ScheduledDisposable.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 6/13/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
private let disposeScheduledDisposable: (ScheduledDisposable) -> Disposable = { sd in
|
||||
sd.disposeInner()
|
||||
return Disposables.create()
|
||||
}
|
||||
|
||||
/**
|
||||
Represents a disposable resource whose disposal invocation will be scheduled on the specified scheduler.
|
||||
*/
|
||||
public final class ScheduledDisposable : Cancelable {
|
||||
public let scheduler: ImmediateSchedulerType
|
||||
|
||||
private var _isDisposed: AtomicInt = 0
|
||||
|
||||
// state
|
||||
private var _disposable: Disposable?
|
||||
|
||||
/**
|
||||
- returns: Was resource disposed.
|
||||
*/
|
||||
public var isDisposed: Bool {
|
||||
return _isDisposed == 1
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a new instance of the `ScheduledDisposable` that uses a `scheduler` on which to dispose the `disposable`.
|
||||
|
||||
- parameter scheduler: Scheduler where the disposable resource will be disposed on.
|
||||
- parameter disposable: Disposable resource to dispose on the given scheduler.
|
||||
*/
|
||||
public init(scheduler: ImmediateSchedulerType, disposable: Disposable) {
|
||||
self.scheduler = scheduler
|
||||
_disposable = disposable
|
||||
}
|
||||
|
||||
/**
|
||||
Disposes the wrapped disposable on the provided scheduler.
|
||||
*/
|
||||
public func dispose() {
|
||||
let _ = scheduler.schedule(self, action: disposeScheduledDisposable)
|
||||
}
|
||||
|
||||
func disposeInner() {
|
||||
if AtomicCompareAndSwap(0, 1, &_isDisposed) {
|
||||
_disposable!.dispose()
|
||||
_disposable = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// SerialDisposable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/12/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents a disposable resource whose underlying disposable resource can be replaced by another disposable resource, causing automatic disposal of the previous underlying disposable resource.
|
||||
*/
|
||||
public final class SerialDisposable : DisposeBase, Cancelable {
|
||||
private var _lock = SpinLock()
|
||||
|
||||
// state
|
||||
private var _current = nil as Disposable?
|
||||
private var _isDisposed = false
|
||||
|
||||
/**
|
||||
- returns: Was resource disposed.
|
||||
*/
|
||||
public var isDisposed: Bool {
|
||||
return _isDisposed
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a new instance of the `SerialDisposable`.
|
||||
*/
|
||||
override public init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
/**
|
||||
Gets or sets the underlying disposable.
|
||||
|
||||
Assigning this property disposes the previous disposable object.
|
||||
|
||||
If the `SerialDisposable` has already been disposed, assignment to this property causes immediate disposal of the given disposable object.
|
||||
*/
|
||||
public var disposable: Disposable {
|
||||
get {
|
||||
return _lock.calculateLocked {
|
||||
return self.disposable
|
||||
}
|
||||
}
|
||||
set (newDisposable) {
|
||||
let disposable: Disposable? = _lock.calculateLocked {
|
||||
if _isDisposed {
|
||||
return newDisposable
|
||||
}
|
||||
else {
|
||||
let toDispose = _current
|
||||
_current = newDisposable
|
||||
return toDispose
|
||||
}
|
||||
}
|
||||
|
||||
if let disposable = disposable {
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Disposes the underlying disposable as well as all future replacements.
|
||||
*/
|
||||
public func dispose() {
|
||||
_dispose()?.dispose()
|
||||
}
|
||||
|
||||
private func _dispose() -> Disposable? {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
if _isDisposed {
|
||||
return nil
|
||||
}
|
||||
else {
|
||||
_isDisposed = true
|
||||
let current = _current
|
||||
_current = nil
|
||||
return current
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// SingleAssignmentDisposable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/15/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents a disposable resource which only allows a single assignment of its underlying disposable resource.
|
||||
|
||||
If an underlying disposable resource has already been set, future attempts to set the underlying disposable resource will throw an exception.
|
||||
*/
|
||||
public class SingleAssignmentDisposable : DisposeBase, Disposable, Cancelable {
|
||||
private var _lock = SpinLock()
|
||||
|
||||
// state
|
||||
private var _isDisposed = false
|
||||
private var _disposableSet = false
|
||||
private var _disposable = nil as Disposable?
|
||||
|
||||
/**
|
||||
- returns: A value that indicates whether the object is disposed.
|
||||
*/
|
||||
public var isDisposed: Bool {
|
||||
return _isDisposed
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a new instance of the `SingleAssignmentDisposable`.
|
||||
*/
|
||||
public override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
/**
|
||||
Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined.
|
||||
|
||||
**Throws exception if the `SingleAssignmentDisposable` has already been assigned to.**
|
||||
*/
|
||||
public var disposable: Disposable {
|
||||
get {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
return _disposable ?? Disposables.create()
|
||||
}
|
||||
set {
|
||||
_setDisposable(newValue)?.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private func _setDisposable(_ newValue: Disposable) -> Disposable? {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
if _disposableSet {
|
||||
rxFatalError("oldState.disposable != nil")
|
||||
}
|
||||
|
||||
_disposableSet = true
|
||||
|
||||
if _isDisposed {
|
||||
return newValue
|
||||
}
|
||||
|
||||
_disposable = newValue
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
Disposes the underlying disposable.
|
||||
*/
|
||||
public func dispose() {
|
||||
if _isDisposed {
|
||||
return
|
||||
}
|
||||
_dispose()?.dispose()
|
||||
}
|
||||
|
||||
private func _dispose() -> Disposable? {
|
||||
_lock.lock(); defer { _lock.unlock() }
|
||||
|
||||
_isDisposed = true
|
||||
let disposable = _disposable
|
||||
_disposable = nil
|
||||
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// StableCompositeDisposable.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 6/12/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class StableCompositeDisposable {
|
||||
@available(*, deprecated, renamed: "Disposables.create")
|
||||
public static func create(_ disposable1: Disposable, _ disposable2: Disposable) -> Disposable {
|
||||
return Disposables.create(disposable1, disposable2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// SubscriptionDisposable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 10/25/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SubscriptionDisposable<T: SynchronizedUnsubscribeType> : Disposable {
|
||||
private let _key: T.DisposeKey
|
||||
private weak var _owner: T?
|
||||
|
||||
init(owner: T, key: T.DisposeKey) {
|
||||
_owner = owner
|
||||
_key = key
|
||||
}
|
||||
|
||||
func dispose() {
|
||||
_owner?.synchronizedUnsubscribe(_key)
|
||||
}
|
||||
}
|
||||
72
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Errors.swift
generated
Normal file
72
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Errors.swift
generated
Normal file
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// Errors.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/28/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let RxErrorDomain = "RxErrorDomain"
|
||||
let RxCompositeFailures = "RxCompositeFailures"
|
||||
|
||||
/**
|
||||
Generic Rx error codes.
|
||||
*/
|
||||
public enum RxError
|
||||
: Swift.Error
|
||||
, CustomDebugStringConvertible {
|
||||
/**
|
||||
Unknown error occured.
|
||||
*/
|
||||
case unknown
|
||||
/**
|
||||
Performing an action on disposed object.
|
||||
*/
|
||||
case disposed(object: AnyObject)
|
||||
/**
|
||||
Aritmetic overflow error.
|
||||
*/
|
||||
case overflow
|
||||
/**
|
||||
Argument out of range error.
|
||||
*/
|
||||
case argumentOutOfRange
|
||||
/**
|
||||
Sequence doesn't contain any elements.
|
||||
*/
|
||||
case noElements
|
||||
/**
|
||||
Sequence contains more than one element.
|
||||
*/
|
||||
case moreThanOneElement
|
||||
/**
|
||||
Timeout error.
|
||||
*/
|
||||
case timeout
|
||||
}
|
||||
|
||||
public extension RxError {
|
||||
/**
|
||||
A textual representation of `self`, suitable for debugging.
|
||||
*/
|
||||
public var debugDescription: String {
|
||||
switch self {
|
||||
case .unknown:
|
||||
return "Unknown error occured."
|
||||
case .disposed(let object):
|
||||
return "Object `\(object)` was already disposed."
|
||||
case .overflow:
|
||||
return "Arithmetic overflow occured."
|
||||
case .argumentOutOfRange:
|
||||
return "Argument out of range."
|
||||
case .noElements:
|
||||
return "Sequence doesn't contain any elements."
|
||||
case .moreThanOneElement:
|
||||
return "Sequence contains more than one element."
|
||||
case .timeout:
|
||||
return "Sequence timeout."
|
||||
}
|
||||
}
|
||||
}
|
||||
66
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Event.swift
generated
Normal file
66
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Event.swift
generated
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Event.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/8/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents a sequence event.
|
||||
|
||||
Sequence grammar:
|
||||
next\* (error | completed)
|
||||
*/
|
||||
public enum Event<Element> {
|
||||
/// Next element is produced.
|
||||
case next(Element)
|
||||
|
||||
/// Sequence terminated with an error.
|
||||
case error(Swift.Error)
|
||||
|
||||
/// Sequence completed successfully.
|
||||
case completed
|
||||
}
|
||||
|
||||
extension Event : CustomDebugStringConvertible {
|
||||
/// - returns: Description of event.
|
||||
public var debugDescription: String {
|
||||
switch self {
|
||||
case .next(let value):
|
||||
return "next(\(value))"
|
||||
case .error(let error):
|
||||
return "error(\(error))"
|
||||
case .completed:
|
||||
return "completed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Event {
|
||||
/// - returns: Is `Completed` or `Error` event.
|
||||
public var isStopEvent: Bool {
|
||||
switch self {
|
||||
case .next: return false
|
||||
case .error, .completed: return true
|
||||
}
|
||||
}
|
||||
|
||||
/// - returns: If `Next` event, returns element value.
|
||||
public var element: Element? {
|
||||
if case .next(let value) = self {
|
||||
return value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// - returns: If `Error` event, returns error.
|
||||
public var error: Swift.Error? {
|
||||
if case .error(let error) = self {
|
||||
return error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// String+Rx.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 12/25/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
/**
|
||||
This is needed because on Linux Swift doesn't have `rangeOfString(..., options: .BackwardsSearch)`
|
||||
*/
|
||||
func lastIndexOf(_ character: Character) -> Index? {
|
||||
var index = endIndex
|
||||
while index > startIndex {
|
||||
index = self.index(before: index)
|
||||
if self[index] == character {
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// ImmediateSchedulerType.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 5/31/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents an object that immediately schedules units of work.
|
||||
*/
|
||||
public protocol ImmediateSchedulerType {
|
||||
/**
|
||||
Schedules an action to be executed immediatelly.
|
||||
|
||||
- parameter state: State passed to the action to be executed.
|
||||
- parameter action: Action to be executed.
|
||||
- returns: The disposable object used to cancel the scheduled action (best effort).
|
||||
*/
|
||||
func schedule<StateType>(_ state: StateType, action: @escaping (StateType) -> Disposable) -> Disposable
|
||||
}
|
||||
|
||||
extension ImmediateSchedulerType {
|
||||
/**
|
||||
Schedules an action to be executed recursively.
|
||||
|
||||
- parameter state: State passed to the action to be executed.
|
||||
- parameter action: Action to execute recursively. The last parameter passed to the action is used to trigger recursive scheduling of the action, passing in recursive invocation state.
|
||||
- returns: The disposable object used to cancel the scheduled action (best effort).
|
||||
*/
|
||||
public func scheduleRecursive<State>(_ state: State, action: @escaping (_ state: State, _ recurse: (State) -> ()) -> ()) -> Disposable {
|
||||
let recursiveScheduler = RecursiveImmediateScheduler(action: action, scheduler: self)
|
||||
|
||||
recursiveScheduler.schedule(state)
|
||||
|
||||
return Disposables.create(with: recursiveScheduler.dispose)
|
||||
}
|
||||
}
|
||||
52
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Observable.swift
generated
Normal file
52
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/Observable.swift
generated
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Observable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/8/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
A type-erased `ObservableType`.
|
||||
|
||||
It represents a push style sequence.
|
||||
*/
|
||||
public class Observable<Element> : ObservableType {
|
||||
/**
|
||||
Type of elements in sequence.
|
||||
*/
|
||||
public typealias E = Element
|
||||
|
||||
init() {
|
||||
#if TRACE_RESOURCES
|
||||
OSAtomicIncrement32(&resourceCount)
|
||||
#endif
|
||||
}
|
||||
|
||||
public func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E {
|
||||
abstractMethod()
|
||||
}
|
||||
|
||||
public func asObservable() -> Observable<E> {
|
||||
return self
|
||||
}
|
||||
|
||||
deinit {
|
||||
#if TRACE_RESOURCES
|
||||
let _ = AtomicDecrement(&resourceCount)
|
||||
#endif
|
||||
}
|
||||
|
||||
// this is kind of ugly I know :(
|
||||
// Swift compiler reports "Not supported yet" when trying to override protocol extensions, so ¯\_(ツ)_/¯
|
||||
|
||||
/**
|
||||
Optimizations for map operator
|
||||
*/
|
||||
internal func composeMap<R>(_ selector: @escaping (Element) throws -> R) -> Observable<R> {
|
||||
return Map(source: self, selector: selector)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// ObservableConvertibleType.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 9/17/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Type that can be converted to observable sequence (`Observer<E>`).
|
||||
*/
|
||||
public protocol ObservableConvertibleType {
|
||||
/**
|
||||
Type of elements in sequence.
|
||||
*/
|
||||
associatedtype E
|
||||
|
||||
/**
|
||||
Converts `self` to `Observable` sequence.
|
||||
|
||||
- returns: Observable sequence that represents `self`.
|
||||
*/
|
||||
func asObservable() -> Observable<E>
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
//
|
||||
// ObservableType+Extensions.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/21/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension ObservableType {
|
||||
/**
|
||||
Subscribes an event handler to an observable sequence.
|
||||
|
||||
- parameter on: Action to invoke for each event in the observable sequence.
|
||||
- returns: Subscription object used to unsubscribe from the observable sequence.
|
||||
*/
|
||||
// @warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
public func subscribe(_ on: @escaping (Event<E>) -> Void)
|
||||
-> Disposable {
|
||||
let observer = AnonymousObserver { e in
|
||||
on(e)
|
||||
}
|
||||
return self.subscribeSafe(observer)
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/**
|
||||
Subscribes an element handler, an error handler, a completion handler and disposed handler to an observable sequence.
|
||||
|
||||
- parameter onNext: Action to invoke for each element in the observable sequence.
|
||||
- parameter onError: Action to invoke upon errored termination of the observable sequence.
|
||||
- parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
|
||||
- parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has
|
||||
gracefully completed, errored, or if the generation is cancelled by disposing subscription).
|
||||
- returns: Subscription object used to unsubscribe from the observable sequence.
|
||||
*/
|
||||
// @warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
public func subscribe(file: String = #file, line: UInt = #line, function: String = #function, onNext: ((E) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
|
||||
-> Disposable {
|
||||
|
||||
let disposable: Disposable
|
||||
|
||||
if let disposed = onDisposed {
|
||||
disposable = Disposables.create(with: disposed)
|
||||
}
|
||||
else {
|
||||
disposable = Disposables.create()
|
||||
}
|
||||
|
||||
let observer = AnonymousObserver<E> { e in
|
||||
switch e {
|
||||
case .next(let value):
|
||||
onNext?(value)
|
||||
case .error(let e):
|
||||
if let onError = onError {
|
||||
onError(e)
|
||||
}
|
||||
else {
|
||||
print("Received unhandled error: \(file):\(line):\(function) -> \(e)")
|
||||
}
|
||||
disposable.dispose()
|
||||
case .completed:
|
||||
onCompleted?()
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
return Disposables.create(
|
||||
self.subscribeSafe(observer),
|
||||
disposable
|
||||
)
|
||||
}
|
||||
#else
|
||||
/**
|
||||
Subscribes an element handler, an error handler, a completion handler and disposed handler to an observable sequence.
|
||||
|
||||
- parameter onNext: Action to invoke for each element in the observable sequence.
|
||||
- parameter onError: Action to invoke upon errored termination of the observable sequence.
|
||||
- parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
|
||||
- parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has
|
||||
gracefully completed, errored, or if the generation is cancelled by disposing subscription).
|
||||
- returns: Subscription object used to unsubscribe from the observable sequence.
|
||||
*/
|
||||
// @warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
public func subscribe(onNext: ((E) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
|
||||
-> Disposable {
|
||||
|
||||
let disposable: Disposable
|
||||
|
||||
if let disposed = onDisposed {
|
||||
disposable = Disposables.create(with: disposed)
|
||||
}
|
||||
else {
|
||||
disposable = Disposables.create()
|
||||
}
|
||||
|
||||
let observer = AnonymousObserver<E> { e in
|
||||
switch e {
|
||||
case .next(let value):
|
||||
onNext?(value)
|
||||
case .error(let e):
|
||||
onError?(e)
|
||||
disposable.dispose()
|
||||
case .completed:
|
||||
onCompleted?()
|
||||
disposable.dispose()
|
||||
}
|
||||
}
|
||||
return Disposables.create(
|
||||
self.subscribeSafe(observer),
|
||||
disposable
|
||||
)
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
Subscribes an element handler to an observable sequence.
|
||||
|
||||
- parameter onNext: Action to invoke for each element in the observable sequence.
|
||||
- returns: Subscription object used to unsubscribe from the observable sequence.
|
||||
*/
|
||||
// @warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
@available(*, deprecated, renamed: "subscribe(onNext:)")
|
||||
public func subscribeNext(_ onNext: @escaping (E) -> Void)
|
||||
-> Disposable {
|
||||
let observer = AnonymousObserver<E> { e in
|
||||
if case .next(let value) = e {
|
||||
onNext(value)
|
||||
}
|
||||
}
|
||||
return self.subscribeSafe(observer)
|
||||
}
|
||||
|
||||
/**
|
||||
Subscribes an error handler to an observable sequence.
|
||||
|
||||
- parameter onError: Action to invoke upon errored termination of the observable sequence.
|
||||
- returns: Subscription object used to unsubscribe from the observable sequence.
|
||||
*/
|
||||
// @warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
@available(*, deprecated, renamed: "subscribe(onError:)")
|
||||
public func subscribeError(_ onError: @escaping (Swift.Error) -> Void)
|
||||
-> Disposable {
|
||||
let observer = AnonymousObserver<E> { e in
|
||||
if case .error(let error) = e {
|
||||
onError(error)
|
||||
}
|
||||
}
|
||||
return self.subscribeSafe(observer)
|
||||
}
|
||||
|
||||
/**
|
||||
Subscribes a completion handler to an observable sequence.
|
||||
|
||||
- parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
|
||||
- returns: Subscription object used to unsubscribe from the observable sequence.
|
||||
*/
|
||||
// @warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
@available(*, deprecated, renamed: "subscribe(onCompleted:)")
|
||||
public func subscribeCompleted(_ onCompleted: @escaping () -> Void)
|
||||
-> Disposable {
|
||||
let observer = AnonymousObserver<E> { e in
|
||||
if case .completed = e {
|
||||
onCompleted()
|
||||
}
|
||||
}
|
||||
return self.subscribeSafe(observer)
|
||||
}
|
||||
}
|
||||
|
||||
public extension ObservableType {
|
||||
/**
|
||||
All internal subscribe calls go through this method.
|
||||
*/
|
||||
// @warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
func subscribeSafe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E {
|
||||
return self.asObservable().subscribe(observer)
|
||||
}
|
||||
}
|
||||
60
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/ObservableType.swift
generated
Normal file
60
samples/client/petstore/swift3/rxswift/SwaggerClientTests/Pods/RxSwift/RxSwift/ObservableType.swift
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// ObservableType.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 8/8/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents a push style sequence.
|
||||
*/
|
||||
public protocol ObservableType : ObservableConvertibleType {
|
||||
/**
|
||||
Type of elements in sequence.
|
||||
*/
|
||||
associatedtype E
|
||||
|
||||
/**
|
||||
Subscribes `observer` to receive events for this sequence.
|
||||
|
||||
### Grammar
|
||||
|
||||
**Next\* (Error | Completed)?**
|
||||
|
||||
* sequences can produce zero or more elements so zero or more `Next` events can be sent to `observer`
|
||||
* once an `Error` or `Completed` event is sent, the sequence terminates and can't produce any other elements
|
||||
|
||||
It is possible that events are sent from different threads, but no two events can be sent concurrently to
|
||||
`observer`.
|
||||
|
||||
### Resource Management
|
||||
|
||||
When sequence sends `Complete` or `Error` event all internal resources that compute sequence elements
|
||||
will be freed.
|
||||
|
||||
To cancel production of sequence elements and free resources immediatelly, call `dispose` on returned
|
||||
subscription.
|
||||
|
||||
- returns: Subscription for `observer` that can be used to cancel production of sequence elements and free resources.
|
||||
*/
|
||||
// @warn_unused_result(message: "http://git.io/rxs.ud")
|
||||
func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E
|
||||
}
|
||||
|
||||
extension ObservableType {
|
||||
|
||||
/**
|
||||
Default implementation of converting `ObservableType` to `Observable`.
|
||||
*/
|
||||
// @warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public func asObservable() -> Observable<E> {
|
||||
// temporary workaround
|
||||
//return Observable.create(subscribe: self.subscribe)
|
||||
return Observable.create { o in
|
||||
return self.subscribe(o)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// AddRef.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Junior B. on 30/10/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class AddRefSink<O: ObserverType> : Sink<O>, ObserverType {
|
||||
typealias Element = O.E
|
||||
|
||||
override init(observer: O) {
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>) {
|
||||
switch event {
|
||||
case .next(_):
|
||||
forwardOn(event)
|
||||
case .completed, .error(_):
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AddRef<Element> : Producer<Element> {
|
||||
typealias EventHandler = (Event<Element>) throws -> Void
|
||||
|
||||
private let _source: Observable<Element>
|
||||
private let _refCount: RefCountDisposable
|
||||
|
||||
init(source: Observable<Element>, refCount: RefCountDisposable) {
|
||||
_source = source
|
||||
_refCount = refCount
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let releaseDisposable = _refCount.retain()
|
||||
let sink = AddRefSink(observer: observer)
|
||||
sink.disposable = Disposables.create(releaseDisposable, _source.subscribeSafe(sink))
|
||||
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// Amb.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 6/14/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum AmbState {
|
||||
case neither
|
||||
case left
|
||||
case right
|
||||
}
|
||||
|
||||
class AmbObserver<ElementType, O: ObserverType> : ObserverType where O.E == ElementType {
|
||||
typealias Element = ElementType
|
||||
typealias Parent = AmbSink<ElementType, O>
|
||||
typealias This = AmbObserver<ElementType, O>
|
||||
typealias Sink = (This, Event<Element>) -> Void
|
||||
|
||||
fileprivate let _parent: Parent
|
||||
fileprivate var _sink: Sink
|
||||
fileprivate var _cancel: Disposable
|
||||
|
||||
init(parent: Parent, cancel: Disposable, sink: @escaping Sink) {
|
||||
#if TRACE_RESOURCES
|
||||
let _ = AtomicIncrement(&resourceCount)
|
||||
#endif
|
||||
|
||||
_parent = parent
|
||||
_sink = sink
|
||||
_cancel = cancel
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>) {
|
||||
_sink(self, event)
|
||||
if event.isStopEvent {
|
||||
_cancel.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
#if TRACE_RESOURCES
|
||||
let _ = AtomicDecrement(&resourceCount)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
class AmbSink<ElementType, O: ObserverType> : Sink<O> where O.E == ElementType {
|
||||
typealias Parent = Amb<ElementType>
|
||||
typealias AmbObserverType = AmbObserver<ElementType, O>
|
||||
|
||||
private let _parent: Parent
|
||||
|
||||
private let _lock = NSRecursiveLock()
|
||||
// state
|
||||
private var _choice = AmbState.neither
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let subscription1 = SingleAssignmentDisposable()
|
||||
let subscription2 = SingleAssignmentDisposable()
|
||||
let disposeAll = Disposables.create(subscription1, subscription2)
|
||||
|
||||
let forwardEvent = { (o: AmbObserverType, event: Event<ElementType>) -> Void in
|
||||
self.forwardOn(event)
|
||||
}
|
||||
|
||||
let decide = { (o: AmbObserverType, event: Event<ElementType>, me: AmbState, otherSubscription: Disposable) in
|
||||
self._lock.performLocked {
|
||||
if self._choice == .neither {
|
||||
self._choice = me
|
||||
o._sink = forwardEvent
|
||||
o._cancel = disposeAll
|
||||
otherSubscription.dispose()
|
||||
}
|
||||
|
||||
if self._choice == me {
|
||||
self.forwardOn(event)
|
||||
if event.isStopEvent {
|
||||
self.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let sink1 = AmbObserver(parent: self, cancel: subscription1) { o, e in
|
||||
decide(o, e, .left, subscription2)
|
||||
}
|
||||
|
||||
let sink2 = AmbObserver(parent: self, cancel: subscription1) { o, e in
|
||||
decide(o, e, .right, subscription1)
|
||||
}
|
||||
|
||||
subscription1.disposable = _parent._left.subscribe(sink1)
|
||||
subscription2.disposable = _parent._right.subscribe(sink2)
|
||||
|
||||
return disposeAll
|
||||
}
|
||||
}
|
||||
|
||||
class Amb<Element>: Producer<Element> {
|
||||
fileprivate let _left: Observable<Element>
|
||||
fileprivate let _right: Observable<Element>
|
||||
|
||||
init(left: Observable<Element>, right: Observable<Element>) {
|
||||
_left = left
|
||||
_right = right
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = AmbSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// AnonymousObservable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/8/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class AnonymousObservableSink<O: ObserverType> : Sink<O>, ObserverType {
|
||||
typealias E = O.E
|
||||
typealias Parent = AnonymousObservable<E>
|
||||
|
||||
// state
|
||||
private var _isStopped: AtomicInt = 0
|
||||
|
||||
override init(observer: O) {
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
switch event {
|
||||
case .next:
|
||||
if _isStopped == 1 {
|
||||
return
|
||||
}
|
||||
forwardOn(event)
|
||||
case .error, .completed:
|
||||
if AtomicCompareAndSwap(0, 1, &_isStopped) {
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func run(_ parent: Parent) -> Disposable {
|
||||
return parent._subscribeHandler(AnyObserver(self))
|
||||
}
|
||||
}
|
||||
|
||||
class AnonymousObservable<Element> : Producer<Element> {
|
||||
typealias SubscribeHandler = (AnyObserver<Element>) -> Disposable
|
||||
|
||||
let _subscribeHandler: SubscribeHandler
|
||||
|
||||
init(_ subscribeHandler: @escaping SubscribeHandler) {
|
||||
_subscribeHandler = subscribeHandler
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = AnonymousObservableSink(observer: observer)
|
||||
sink.disposable = sink.run(self)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// Buffer.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 9/13/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class BufferTimeCount<Element> : Producer<[Element]> {
|
||||
|
||||
fileprivate let _timeSpan: RxTimeInterval
|
||||
fileprivate let _count: Int
|
||||
fileprivate let _scheduler: SchedulerType
|
||||
fileprivate let _source: Observable<Element>
|
||||
|
||||
init(source: Observable<Element>, timeSpan: RxTimeInterval, count: Int, scheduler: SchedulerType) {
|
||||
_source = source
|
||||
_timeSpan = timeSpan
|
||||
_count = count
|
||||
_scheduler = scheduler
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == [Element] {
|
||||
let sink = BufferTimeCountSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
class BufferTimeCountSink<Element, O: ObserverType>
|
||||
: Sink<O>
|
||||
, LockOwnerType
|
||||
, ObserverType
|
||||
, SynchronizedOnType where O.E == [Element] {
|
||||
typealias Parent = BufferTimeCount<Element>
|
||||
typealias E = Element
|
||||
|
||||
private let _parent: Parent
|
||||
|
||||
let _lock = NSRecursiveLock()
|
||||
|
||||
// state
|
||||
private let _timerD = SerialDisposable()
|
||||
private var _buffer = [Element]()
|
||||
private var _windowID = 0
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
createTimer(_windowID)
|
||||
return Disposables.create(_timerD, _parent._source.subscribe(self))
|
||||
}
|
||||
|
||||
func startNewWindowAndSendCurrentOne() {
|
||||
_windowID = _windowID &+ 1
|
||||
let windowID = _windowID
|
||||
|
||||
let buffer = _buffer
|
||||
_buffer = []
|
||||
forwardOn(.next(buffer))
|
||||
|
||||
createTimer(windowID)
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
synchronizedOn(event)
|
||||
}
|
||||
|
||||
func _synchronized_on(_ event: Event<E>) {
|
||||
switch event {
|
||||
case .next(let element):
|
||||
_buffer.append(element)
|
||||
|
||||
if _buffer.count == _parent._count {
|
||||
startNewWindowAndSendCurrentOne()
|
||||
}
|
||||
|
||||
case .error(let error):
|
||||
_buffer = []
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
case .completed:
|
||||
forwardOn(.next(_buffer))
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
func createTimer(_ windowID: Int) {
|
||||
if _timerD.isDisposed {
|
||||
return
|
||||
}
|
||||
|
||||
if _windowID != windowID {
|
||||
return
|
||||
}
|
||||
|
||||
let nextTimer = SingleAssignmentDisposable()
|
||||
|
||||
_timerD.disposable = nextTimer
|
||||
|
||||
nextTimer.disposable = _parent._scheduler.scheduleRelative(windowID, dueTime: _parent._timeSpan) { previousWindowID in
|
||||
self._lock.performLocked {
|
||||
if previousWindowID != self._windowID {
|
||||
return
|
||||
}
|
||||
|
||||
self.startNewWindowAndSendCurrentOne()
|
||||
}
|
||||
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// Catch.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 4/19/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// catch with callback
|
||||
|
||||
class CatchSinkProxy<O: ObserverType> : ObserverType {
|
||||
typealias E = O.E
|
||||
typealias Parent = CatchSink<O>
|
||||
|
||||
private let _parent: Parent
|
||||
|
||||
init(parent: Parent) {
|
||||
_parent = parent
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
_parent.forwardOn(event)
|
||||
|
||||
switch event {
|
||||
case .next:
|
||||
break
|
||||
case .error, .completed:
|
||||
_parent.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CatchSink<O: ObserverType> : Sink<O>, ObserverType {
|
||||
typealias E = O.E
|
||||
typealias Parent = Catch<E>
|
||||
|
||||
private let _parent: Parent
|
||||
private let _subscription = SerialDisposable()
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let d1 = SingleAssignmentDisposable()
|
||||
_subscription.disposable = d1
|
||||
d1.disposable = _parent._source.subscribe(self)
|
||||
|
||||
return _subscription
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
switch event {
|
||||
case .next:
|
||||
forwardOn(event)
|
||||
case .completed:
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
case .error(let error):
|
||||
do {
|
||||
let catchSequence = try _parent._handler(error)
|
||||
|
||||
let observer = CatchSinkProxy(parent: self)
|
||||
|
||||
_subscription.disposable = catchSequence.subscribe(observer)
|
||||
}
|
||||
catch let e {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Catch<Element> : Producer<Element> {
|
||||
typealias Handler = (Swift.Error) throws -> Observable<Element>
|
||||
|
||||
fileprivate let _source: Observable<Element>
|
||||
fileprivate let _handler: Handler
|
||||
|
||||
init(source: Observable<Element>, handler: @escaping Handler) {
|
||||
_source = source
|
||||
_handler = handler
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = CatchSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
// catch enumerable
|
||||
|
||||
class CatchSequenceSink<S: Sequence, O: ObserverType>
|
||||
: TailRecursiveSink<S, O>
|
||||
, ObserverType where S.Iterator.Element : ObservableConvertibleType, S.Iterator.Element.E == O.E {
|
||||
typealias Element = O.E
|
||||
typealias Parent = CatchSequence<S>
|
||||
|
||||
private var _lastError: Swift.Error?
|
||||
|
||||
override init(observer: O) {
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>) {
|
||||
switch event {
|
||||
case .next:
|
||||
forwardOn(event)
|
||||
case .error(let error):
|
||||
_lastError = error
|
||||
schedule(.moveNext)
|
||||
case .completed:
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
override func subscribeToNext(_ source: Observable<E>) -> Disposable {
|
||||
return source.subscribe(self)
|
||||
}
|
||||
|
||||
override func done() {
|
||||
if let lastError = _lastError {
|
||||
forwardOn(.error(lastError))
|
||||
}
|
||||
else {
|
||||
forwardOn(.completed)
|
||||
}
|
||||
|
||||
self.dispose()
|
||||
}
|
||||
|
||||
override func extract(_ observable: Observable<Element>) -> SequenceGenerator? {
|
||||
if let onError = observable as? CatchSequence<S> {
|
||||
return (onError.sources.makeIterator(), nil)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CatchSequence<S: Sequence> : Producer<S.Iterator.Element.E> where S.Iterator.Element : ObservableConvertibleType {
|
||||
typealias Element = S.Iterator.Element.E
|
||||
|
||||
let sources: S
|
||||
|
||||
init(sources: S) {
|
||||
self.sources = sources
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = CatchSequenceSink<S, O>(observer: observer)
|
||||
sink.disposable = sink.run((self.sources.makeIterator(), nil))
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// CombineLatest+Collection.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 8/29/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class CombineLatestCollectionTypeSink<C: Collection, R, O: ObserverType>
|
||||
: Sink<O> where C.Iterator.Element : ObservableConvertibleType, O.E == R {
|
||||
typealias Parent = CombineLatestCollectionType<C, R>
|
||||
typealias SourceElement = C.Iterator.Element.E
|
||||
|
||||
let _parent: Parent
|
||||
|
||||
let _lock = NSRecursiveLock()
|
||||
|
||||
// state
|
||||
var _numberOfValues = 0
|
||||
var _values: [SourceElement?]
|
||||
var _isDone: [Bool]
|
||||
var _numberOfDone = 0
|
||||
var _subscriptions: [SingleAssignmentDisposable]
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
_values = [SourceElement?](repeating: nil, count: parent._count)
|
||||
_isDone = [Bool](repeating: false, count: parent._count)
|
||||
_subscriptions = Array<SingleAssignmentDisposable>()
|
||||
_subscriptions.reserveCapacity(parent._count)
|
||||
|
||||
for _ in 0 ..< parent._count {
|
||||
_subscriptions.append(SingleAssignmentDisposable())
|
||||
}
|
||||
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<SourceElement>, atIndex: Int) {
|
||||
_lock.lock(); defer { _lock.unlock() } // {
|
||||
switch event {
|
||||
case .next(let element):
|
||||
if _values[atIndex] == nil {
|
||||
_numberOfValues += 1
|
||||
}
|
||||
|
||||
_values[atIndex] = element
|
||||
|
||||
if _numberOfValues < _parent._count {
|
||||
let numberOfOthersThatAreDone = self._numberOfDone - (_isDone[atIndex] ? 1 : 0)
|
||||
if numberOfOthersThatAreDone == self._parent._count - 1 {
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let result = try _parent._resultSelector(_values.map { $0! })
|
||||
forwardOn(.next(result))
|
||||
}
|
||||
catch let error {
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
}
|
||||
|
||||
case .error(let error):
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
case .completed:
|
||||
if _isDone[atIndex] {
|
||||
return
|
||||
}
|
||||
|
||||
_isDone[atIndex] = true
|
||||
_numberOfDone += 1
|
||||
|
||||
if _numberOfDone == self._parent._count {
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
else {
|
||||
_subscriptions[atIndex].dispose()
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
var j = 0
|
||||
for i in _parent._sources {
|
||||
let index = j
|
||||
let source = i.asObservable()
|
||||
_subscriptions[j].disposable = source.subscribe(AnyObserver { event in
|
||||
self.on(event, atIndex: index)
|
||||
})
|
||||
|
||||
j += 1
|
||||
}
|
||||
|
||||
return Disposables.create(_subscriptions)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatestCollectionType<C: Collection, R> : Producer<R> where C.Iterator.Element : ObservableConvertibleType {
|
||||
typealias ResultSelector = ([C.Iterator.Element.E]) throws -> R
|
||||
|
||||
let _sources: C
|
||||
let _resultSelector: ResultSelector
|
||||
let _count: Int
|
||||
|
||||
init(sources: C, resultSelector: @escaping ResultSelector) {
|
||||
_sources = sources
|
||||
_resultSelector = resultSelector
|
||||
_count = Int(self._sources.count.toIntMax())
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == R {
|
||||
let sink = CombineLatestCollectionTypeSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,726 @@
|
||||
// This file is autogenerated. Take a look at `Preprocessor` target in RxSwift project
|
||||
//
|
||||
// CombineLatest+arity.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 4/22/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
|
||||
// 2
|
||||
|
||||
extension Observable {
|
||||
/**
|
||||
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
|
||||
|
||||
- seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
|
||||
|
||||
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
|
||||
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
|
||||
*/
|
||||
// @warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public static func combineLatest<O1: ObservableType, O2: ObservableType>
|
||||
(_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.E, O2.E) throws -> E)
|
||||
-> Observable<E> {
|
||||
return CombineLatest2(
|
||||
source1: source1.asObservable(), source2: source2.asObservable(),
|
||||
resultSelector: resultSelector
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatestSink2_<E1, E2, O: ObserverType> : CombineLatestSink<O> {
|
||||
typealias R = O.E
|
||||
typealias Parent = CombineLatest2<E1, E2, R>
|
||||
|
||||
let _parent: Parent
|
||||
|
||||
var _latestElement1: E1! = nil
|
||||
var _latestElement2: E2! = nil
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(arity: 2, observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let subscription1 = SingleAssignmentDisposable()
|
||||
let subscription2 = SingleAssignmentDisposable()
|
||||
|
||||
let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
|
||||
let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
|
||||
|
||||
subscription1.disposable = _parent._source1.subscribe(observer1)
|
||||
subscription2.disposable = _parent._source2.subscribe(observer2)
|
||||
|
||||
return Disposables.create([
|
||||
subscription1,
|
||||
subscription2
|
||||
])
|
||||
}
|
||||
|
||||
override func getResult() throws -> R {
|
||||
return try _parent._resultSelector(_latestElement1, _latestElement2)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatest2<E1, E2, R> : Producer<R> {
|
||||
typealias ResultSelector = (E1, E2) throws -> R
|
||||
|
||||
let _source1: Observable<E1>
|
||||
let _source2: Observable<E2>
|
||||
|
||||
let _resultSelector: ResultSelector
|
||||
|
||||
init(source1: Observable<E1>, source2: Observable<E2>, resultSelector: @escaping ResultSelector) {
|
||||
_source1 = source1
|
||||
_source2 = source2
|
||||
|
||||
_resultSelector = resultSelector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == R {
|
||||
let sink = CombineLatestSink2_(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 3
|
||||
|
||||
extension Observable {
|
||||
/**
|
||||
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
|
||||
|
||||
- seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
|
||||
|
||||
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
|
||||
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
|
||||
*/
|
||||
// @warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType>
|
||||
(_ source1: O1, _ source2: O2, _ source3: O3, resultSelector: @escaping (O1.E, O2.E, O3.E) throws -> E)
|
||||
-> Observable<E> {
|
||||
return CombineLatest3(
|
||||
source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(),
|
||||
resultSelector: resultSelector
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatestSink3_<E1, E2, E3, O: ObserverType> : CombineLatestSink<O> {
|
||||
typealias R = O.E
|
||||
typealias Parent = CombineLatest3<E1, E2, E3, R>
|
||||
|
||||
let _parent: Parent
|
||||
|
||||
var _latestElement1: E1! = nil
|
||||
var _latestElement2: E2! = nil
|
||||
var _latestElement3: E3! = nil
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(arity: 3, observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let subscription1 = SingleAssignmentDisposable()
|
||||
let subscription2 = SingleAssignmentDisposable()
|
||||
let subscription3 = SingleAssignmentDisposable()
|
||||
|
||||
let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
|
||||
let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
|
||||
let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
|
||||
|
||||
subscription1.disposable = _parent._source1.subscribe(observer1)
|
||||
subscription2.disposable = _parent._source2.subscribe(observer2)
|
||||
subscription3.disposable = _parent._source3.subscribe(observer3)
|
||||
|
||||
return Disposables.create([
|
||||
subscription1,
|
||||
subscription2,
|
||||
subscription3
|
||||
])
|
||||
}
|
||||
|
||||
override func getResult() throws -> R {
|
||||
return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatest3<E1, E2, E3, R> : Producer<R> {
|
||||
typealias ResultSelector = (E1, E2, E3) throws -> R
|
||||
|
||||
let _source1: Observable<E1>
|
||||
let _source2: Observable<E2>
|
||||
let _source3: Observable<E3>
|
||||
|
||||
let _resultSelector: ResultSelector
|
||||
|
||||
init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, resultSelector: @escaping ResultSelector) {
|
||||
_source1 = source1
|
||||
_source2 = source2
|
||||
_source3 = source3
|
||||
|
||||
_resultSelector = resultSelector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == R {
|
||||
let sink = CombineLatestSink3_(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 4
|
||||
|
||||
extension Observable {
|
||||
/**
|
||||
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
|
||||
|
||||
- seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
|
||||
|
||||
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
|
||||
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
|
||||
*/
|
||||
// @warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType>
|
||||
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E) throws -> E)
|
||||
-> Observable<E> {
|
||||
return CombineLatest4(
|
||||
source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(),
|
||||
resultSelector: resultSelector
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatestSink4_<E1, E2, E3, E4, O: ObserverType> : CombineLatestSink<O> {
|
||||
typealias R = O.E
|
||||
typealias Parent = CombineLatest4<E1, E2, E3, E4, R>
|
||||
|
||||
let _parent: Parent
|
||||
|
||||
var _latestElement1: E1! = nil
|
||||
var _latestElement2: E2! = nil
|
||||
var _latestElement3: E3! = nil
|
||||
var _latestElement4: E4! = nil
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(arity: 4, observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let subscription1 = SingleAssignmentDisposable()
|
||||
let subscription2 = SingleAssignmentDisposable()
|
||||
let subscription3 = SingleAssignmentDisposable()
|
||||
let subscription4 = SingleAssignmentDisposable()
|
||||
|
||||
let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
|
||||
let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
|
||||
let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
|
||||
let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4)
|
||||
|
||||
subscription1.disposable = _parent._source1.subscribe(observer1)
|
||||
subscription2.disposable = _parent._source2.subscribe(observer2)
|
||||
subscription3.disposable = _parent._source3.subscribe(observer3)
|
||||
subscription4.disposable = _parent._source4.subscribe(observer4)
|
||||
|
||||
return Disposables.create([
|
||||
subscription1,
|
||||
subscription2,
|
||||
subscription3,
|
||||
subscription4
|
||||
])
|
||||
}
|
||||
|
||||
override func getResult() throws -> R {
|
||||
return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatest4<E1, E2, E3, E4, R> : Producer<R> {
|
||||
typealias ResultSelector = (E1, E2, E3, E4) throws -> R
|
||||
|
||||
let _source1: Observable<E1>
|
||||
let _source2: Observable<E2>
|
||||
let _source3: Observable<E3>
|
||||
let _source4: Observable<E4>
|
||||
|
||||
let _resultSelector: ResultSelector
|
||||
|
||||
init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, resultSelector: @escaping ResultSelector) {
|
||||
_source1 = source1
|
||||
_source2 = source2
|
||||
_source3 = source3
|
||||
_source4 = source4
|
||||
|
||||
_resultSelector = resultSelector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == R {
|
||||
let sink = CombineLatestSink4_(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 5
|
||||
|
||||
extension Observable {
|
||||
/**
|
||||
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
|
||||
|
||||
- seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
|
||||
|
||||
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
|
||||
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
|
||||
*/
|
||||
// @warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType>
|
||||
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E) throws -> E)
|
||||
-> Observable<E> {
|
||||
return CombineLatest5(
|
||||
source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(),
|
||||
resultSelector: resultSelector
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatestSink5_<E1, E2, E3, E4, E5, O: ObserverType> : CombineLatestSink<O> {
|
||||
typealias R = O.E
|
||||
typealias Parent = CombineLatest5<E1, E2, E3, E4, E5, R>
|
||||
|
||||
let _parent: Parent
|
||||
|
||||
var _latestElement1: E1! = nil
|
||||
var _latestElement2: E2! = nil
|
||||
var _latestElement3: E3! = nil
|
||||
var _latestElement4: E4! = nil
|
||||
var _latestElement5: E5! = nil
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(arity: 5, observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let subscription1 = SingleAssignmentDisposable()
|
||||
let subscription2 = SingleAssignmentDisposable()
|
||||
let subscription3 = SingleAssignmentDisposable()
|
||||
let subscription4 = SingleAssignmentDisposable()
|
||||
let subscription5 = SingleAssignmentDisposable()
|
||||
|
||||
let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
|
||||
let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
|
||||
let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
|
||||
let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4)
|
||||
let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5)
|
||||
|
||||
subscription1.disposable = _parent._source1.subscribe(observer1)
|
||||
subscription2.disposable = _parent._source2.subscribe(observer2)
|
||||
subscription3.disposable = _parent._source3.subscribe(observer3)
|
||||
subscription4.disposable = _parent._source4.subscribe(observer4)
|
||||
subscription5.disposable = _parent._source5.subscribe(observer5)
|
||||
|
||||
return Disposables.create([
|
||||
subscription1,
|
||||
subscription2,
|
||||
subscription3,
|
||||
subscription4,
|
||||
subscription5
|
||||
])
|
||||
}
|
||||
|
||||
override func getResult() throws -> R {
|
||||
return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatest5<E1, E2, E3, E4, E5, R> : Producer<R> {
|
||||
typealias ResultSelector = (E1, E2, E3, E4, E5) throws -> R
|
||||
|
||||
let _source1: Observable<E1>
|
||||
let _source2: Observable<E2>
|
||||
let _source3: Observable<E3>
|
||||
let _source4: Observable<E4>
|
||||
let _source5: Observable<E5>
|
||||
|
||||
let _resultSelector: ResultSelector
|
||||
|
||||
init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, resultSelector: @escaping ResultSelector) {
|
||||
_source1 = source1
|
||||
_source2 = source2
|
||||
_source3 = source3
|
||||
_source4 = source4
|
||||
_source5 = source5
|
||||
|
||||
_resultSelector = resultSelector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == R {
|
||||
let sink = CombineLatestSink5_(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 6
|
||||
|
||||
extension Observable {
|
||||
/**
|
||||
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
|
||||
|
||||
- seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
|
||||
|
||||
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
|
||||
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
|
||||
*/
|
||||
// @warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType>
|
||||
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E) throws -> E)
|
||||
-> Observable<E> {
|
||||
return CombineLatest6(
|
||||
source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(),
|
||||
resultSelector: resultSelector
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatestSink6_<E1, E2, E3, E4, E5, E6, O: ObserverType> : CombineLatestSink<O> {
|
||||
typealias R = O.E
|
||||
typealias Parent = CombineLatest6<E1, E2, E3, E4, E5, E6, R>
|
||||
|
||||
let _parent: Parent
|
||||
|
||||
var _latestElement1: E1! = nil
|
||||
var _latestElement2: E2! = nil
|
||||
var _latestElement3: E3! = nil
|
||||
var _latestElement4: E4! = nil
|
||||
var _latestElement5: E5! = nil
|
||||
var _latestElement6: E6! = nil
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(arity: 6, observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let subscription1 = SingleAssignmentDisposable()
|
||||
let subscription2 = SingleAssignmentDisposable()
|
||||
let subscription3 = SingleAssignmentDisposable()
|
||||
let subscription4 = SingleAssignmentDisposable()
|
||||
let subscription5 = SingleAssignmentDisposable()
|
||||
let subscription6 = SingleAssignmentDisposable()
|
||||
|
||||
let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
|
||||
let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
|
||||
let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
|
||||
let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4)
|
||||
let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5)
|
||||
let observer6 = CombineLatestObserver(lock: _lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6)
|
||||
|
||||
subscription1.disposable = _parent._source1.subscribe(observer1)
|
||||
subscription2.disposable = _parent._source2.subscribe(observer2)
|
||||
subscription3.disposable = _parent._source3.subscribe(observer3)
|
||||
subscription4.disposable = _parent._source4.subscribe(observer4)
|
||||
subscription5.disposable = _parent._source5.subscribe(observer5)
|
||||
subscription6.disposable = _parent._source6.subscribe(observer6)
|
||||
|
||||
return Disposables.create([
|
||||
subscription1,
|
||||
subscription2,
|
||||
subscription3,
|
||||
subscription4,
|
||||
subscription5,
|
||||
subscription6
|
||||
])
|
||||
}
|
||||
|
||||
override func getResult() throws -> R {
|
||||
return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5, _latestElement6)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatest6<E1, E2, E3, E4, E5, E6, R> : Producer<R> {
|
||||
typealias ResultSelector = (E1, E2, E3, E4, E5, E6) throws -> R
|
||||
|
||||
let _source1: Observable<E1>
|
||||
let _source2: Observable<E2>
|
||||
let _source3: Observable<E3>
|
||||
let _source4: Observable<E4>
|
||||
let _source5: Observable<E5>
|
||||
let _source6: Observable<E6>
|
||||
|
||||
let _resultSelector: ResultSelector
|
||||
|
||||
init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, source6: Observable<E6>, resultSelector: @escaping ResultSelector) {
|
||||
_source1 = source1
|
||||
_source2 = source2
|
||||
_source3 = source3
|
||||
_source4 = source4
|
||||
_source5 = source5
|
||||
_source6 = source6
|
||||
|
||||
_resultSelector = resultSelector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == R {
|
||||
let sink = CombineLatestSink6_(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 7
|
||||
|
||||
extension Observable {
|
||||
/**
|
||||
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
|
||||
|
||||
- seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
|
||||
|
||||
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
|
||||
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
|
||||
*/
|
||||
// @warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType, O7: ObservableType>
|
||||
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E) throws -> E)
|
||||
-> Observable<E> {
|
||||
return CombineLatest7(
|
||||
source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(),
|
||||
resultSelector: resultSelector
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatestSink7_<E1, E2, E3, E4, E5, E6, E7, O: ObserverType> : CombineLatestSink<O> {
|
||||
typealias R = O.E
|
||||
typealias Parent = CombineLatest7<E1, E2, E3, E4, E5, E6, E7, R>
|
||||
|
||||
let _parent: Parent
|
||||
|
||||
var _latestElement1: E1! = nil
|
||||
var _latestElement2: E2! = nil
|
||||
var _latestElement3: E3! = nil
|
||||
var _latestElement4: E4! = nil
|
||||
var _latestElement5: E5! = nil
|
||||
var _latestElement6: E6! = nil
|
||||
var _latestElement7: E7! = nil
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(arity: 7, observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let subscription1 = SingleAssignmentDisposable()
|
||||
let subscription2 = SingleAssignmentDisposable()
|
||||
let subscription3 = SingleAssignmentDisposable()
|
||||
let subscription4 = SingleAssignmentDisposable()
|
||||
let subscription5 = SingleAssignmentDisposable()
|
||||
let subscription6 = SingleAssignmentDisposable()
|
||||
let subscription7 = SingleAssignmentDisposable()
|
||||
|
||||
let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
|
||||
let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
|
||||
let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
|
||||
let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4)
|
||||
let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5)
|
||||
let observer6 = CombineLatestObserver(lock: _lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6)
|
||||
let observer7 = CombineLatestObserver(lock: _lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7)
|
||||
|
||||
subscription1.disposable = _parent._source1.subscribe(observer1)
|
||||
subscription2.disposable = _parent._source2.subscribe(observer2)
|
||||
subscription3.disposable = _parent._source3.subscribe(observer3)
|
||||
subscription4.disposable = _parent._source4.subscribe(observer4)
|
||||
subscription5.disposable = _parent._source5.subscribe(observer5)
|
||||
subscription6.disposable = _parent._source6.subscribe(observer6)
|
||||
subscription7.disposable = _parent._source7.subscribe(observer7)
|
||||
|
||||
return Disposables.create([
|
||||
subscription1,
|
||||
subscription2,
|
||||
subscription3,
|
||||
subscription4,
|
||||
subscription5,
|
||||
subscription6,
|
||||
subscription7
|
||||
])
|
||||
}
|
||||
|
||||
override func getResult() throws -> R {
|
||||
return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5, _latestElement6, _latestElement7)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatest7<E1, E2, E3, E4, E5, E6, E7, R> : Producer<R> {
|
||||
typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7) throws -> R
|
||||
|
||||
let _source1: Observable<E1>
|
||||
let _source2: Observable<E2>
|
||||
let _source3: Observable<E3>
|
||||
let _source4: Observable<E4>
|
||||
let _source5: Observable<E5>
|
||||
let _source6: Observable<E6>
|
||||
let _source7: Observable<E7>
|
||||
|
||||
let _resultSelector: ResultSelector
|
||||
|
||||
init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, source6: Observable<E6>, source7: Observable<E7>, resultSelector: @escaping ResultSelector) {
|
||||
_source1 = source1
|
||||
_source2 = source2
|
||||
_source3 = source3
|
||||
_source4 = source4
|
||||
_source5 = source5
|
||||
_source6 = source6
|
||||
_source7 = source7
|
||||
|
||||
_resultSelector = resultSelector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == R {
|
||||
let sink = CombineLatestSink7_(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 8
|
||||
|
||||
extension Observable {
|
||||
/**
|
||||
Merges the specified observable sequences into one observable sequence by using the selector function whenever any of the observable sequences produces an element.
|
||||
|
||||
- seealso: [combineLatest operator on reactivex.io](http://reactivex.io/documentation/operators/combinelatest.html)
|
||||
|
||||
- parameter resultSelector: Function to invoke whenever any of the sources produces an element.
|
||||
- returns: An observable sequence containing the result of combining elements of the sources using the specified result selector function.
|
||||
*/
|
||||
// @warn_unused_result(message:"http://git.io/rxs.uo")
|
||||
public static func combineLatest<O1: ObservableType, O2: ObservableType, O3: ObservableType, O4: ObservableType, O5: ObservableType, O6: ObservableType, O7: ObservableType, O8: ObservableType>
|
||||
(_ source1: O1, _ source2: O2, _ source3: O3, _ source4: O4, _ source5: O5, _ source6: O6, _ source7: O7, _ source8: O8, resultSelector: @escaping (O1.E, O2.E, O3.E, O4.E, O5.E, O6.E, O7.E, O8.E) throws -> E)
|
||||
-> Observable<E> {
|
||||
return CombineLatest8(
|
||||
source1: source1.asObservable(), source2: source2.asObservable(), source3: source3.asObservable(), source4: source4.asObservable(), source5: source5.asObservable(), source6: source6.asObservable(), source7: source7.asObservable(), source8: source8.asObservable(),
|
||||
resultSelector: resultSelector
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatestSink8_<E1, E2, E3, E4, E5, E6, E7, E8, O: ObserverType> : CombineLatestSink<O> {
|
||||
typealias R = O.E
|
||||
typealias Parent = CombineLatest8<E1, E2, E3, E4, E5, E6, E7, E8, R>
|
||||
|
||||
let _parent: Parent
|
||||
|
||||
var _latestElement1: E1! = nil
|
||||
var _latestElement2: E2! = nil
|
||||
var _latestElement3: E3! = nil
|
||||
var _latestElement4: E4! = nil
|
||||
var _latestElement5: E5! = nil
|
||||
var _latestElement6: E6! = nil
|
||||
var _latestElement7: E7! = nil
|
||||
var _latestElement8: E8! = nil
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(arity: 8, observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let subscription1 = SingleAssignmentDisposable()
|
||||
let subscription2 = SingleAssignmentDisposable()
|
||||
let subscription3 = SingleAssignmentDisposable()
|
||||
let subscription4 = SingleAssignmentDisposable()
|
||||
let subscription5 = SingleAssignmentDisposable()
|
||||
let subscription6 = SingleAssignmentDisposable()
|
||||
let subscription7 = SingleAssignmentDisposable()
|
||||
let subscription8 = SingleAssignmentDisposable()
|
||||
|
||||
let observer1 = CombineLatestObserver(lock: _lock, parent: self, index: 0, setLatestValue: { (e: E1) -> Void in self._latestElement1 = e }, this: subscription1)
|
||||
let observer2 = CombineLatestObserver(lock: _lock, parent: self, index: 1, setLatestValue: { (e: E2) -> Void in self._latestElement2 = e }, this: subscription2)
|
||||
let observer3 = CombineLatestObserver(lock: _lock, parent: self, index: 2, setLatestValue: { (e: E3) -> Void in self._latestElement3 = e }, this: subscription3)
|
||||
let observer4 = CombineLatestObserver(lock: _lock, parent: self, index: 3, setLatestValue: { (e: E4) -> Void in self._latestElement4 = e }, this: subscription4)
|
||||
let observer5 = CombineLatestObserver(lock: _lock, parent: self, index: 4, setLatestValue: { (e: E5) -> Void in self._latestElement5 = e }, this: subscription5)
|
||||
let observer6 = CombineLatestObserver(lock: _lock, parent: self, index: 5, setLatestValue: { (e: E6) -> Void in self._latestElement6 = e }, this: subscription6)
|
||||
let observer7 = CombineLatestObserver(lock: _lock, parent: self, index: 6, setLatestValue: { (e: E7) -> Void in self._latestElement7 = e }, this: subscription7)
|
||||
let observer8 = CombineLatestObserver(lock: _lock, parent: self, index: 7, setLatestValue: { (e: E8) -> Void in self._latestElement8 = e }, this: subscription8)
|
||||
|
||||
subscription1.disposable = _parent._source1.subscribe(observer1)
|
||||
subscription2.disposable = _parent._source2.subscribe(observer2)
|
||||
subscription3.disposable = _parent._source3.subscribe(observer3)
|
||||
subscription4.disposable = _parent._source4.subscribe(observer4)
|
||||
subscription5.disposable = _parent._source5.subscribe(observer5)
|
||||
subscription6.disposable = _parent._source6.subscribe(observer6)
|
||||
subscription7.disposable = _parent._source7.subscribe(observer7)
|
||||
subscription8.disposable = _parent._source8.subscribe(observer8)
|
||||
|
||||
return Disposables.create([
|
||||
subscription1,
|
||||
subscription2,
|
||||
subscription3,
|
||||
subscription4,
|
||||
subscription5,
|
||||
subscription6,
|
||||
subscription7,
|
||||
subscription8
|
||||
])
|
||||
}
|
||||
|
||||
override func getResult() throws -> R {
|
||||
return try _parent._resultSelector(_latestElement1, _latestElement2, _latestElement3, _latestElement4, _latestElement5, _latestElement6, _latestElement7, _latestElement8)
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatest8<E1, E2, E3, E4, E5, E6, E7, E8, R> : Producer<R> {
|
||||
typealias ResultSelector = (E1, E2, E3, E4, E5, E6, E7, E8) throws -> R
|
||||
|
||||
let _source1: Observable<E1>
|
||||
let _source2: Observable<E2>
|
||||
let _source3: Observable<E3>
|
||||
let _source4: Observable<E4>
|
||||
let _source5: Observable<E5>
|
||||
let _source6: Observable<E6>
|
||||
let _source7: Observable<E7>
|
||||
let _source8: Observable<E8>
|
||||
|
||||
let _resultSelector: ResultSelector
|
||||
|
||||
init(source1: Observable<E1>, source2: Observable<E2>, source3: Observable<E3>, source4: Observable<E4>, source5: Observable<E5>, source6: Observable<E6>, source7: Observable<E7>, source8: Observable<E8>, resultSelector: @escaping ResultSelector) {
|
||||
_source1 = source1
|
||||
_source2 = source2
|
||||
_source3 = source3
|
||||
_source4 = source4
|
||||
_source5 = source5
|
||||
_source6 = source6
|
||||
_source7 = source7
|
||||
_source8 = source8
|
||||
|
||||
_resultSelector = resultSelector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == R {
|
||||
let sink = CombineLatestSink8_(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// CombineLatest.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/21/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol CombineLatestProtocol : class {
|
||||
func next(_ index: Int)
|
||||
func fail(_ error: Swift.Error)
|
||||
func done(_ index: Int)
|
||||
}
|
||||
|
||||
class CombineLatestSink<O: ObserverType>
|
||||
: Sink<O>
|
||||
, CombineLatestProtocol {
|
||||
typealias Element = O.E
|
||||
|
||||
let _lock = NSRecursiveLock()
|
||||
|
||||
private let _arity: Int
|
||||
private var _numberOfValues = 0
|
||||
private var _numberOfDone = 0
|
||||
private var _hasValue: [Bool]
|
||||
private var _isDone: [Bool]
|
||||
|
||||
init(arity: Int, observer: O) {
|
||||
_arity = arity
|
||||
_hasValue = [Bool](repeating: false, count: arity)
|
||||
_isDone = [Bool](repeating: false, count: arity)
|
||||
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func getResult() throws -> Element {
|
||||
abstractMethod()
|
||||
}
|
||||
|
||||
func next(_ index: Int) {
|
||||
if !_hasValue[index] {
|
||||
_hasValue[index] = true
|
||||
_numberOfValues += 1
|
||||
}
|
||||
|
||||
if _numberOfValues == _arity {
|
||||
do {
|
||||
let result = try getResult()
|
||||
forwardOn(.next(result))
|
||||
}
|
||||
catch let e {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
else {
|
||||
var allOthersDone = true
|
||||
|
||||
for i in 0 ..< _arity {
|
||||
if i != index && !_isDone[i] {
|
||||
allOthersDone = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if allOthersDone {
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fail(_ error: Swift.Error) {
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
}
|
||||
|
||||
func done(_ index: Int) {
|
||||
if _isDone[index] {
|
||||
return
|
||||
}
|
||||
|
||||
_isDone[index] = true
|
||||
_numberOfDone += 1
|
||||
|
||||
if _numberOfDone == _arity {
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CombineLatestObserver<ElementType>
|
||||
: ObserverType
|
||||
, LockOwnerType
|
||||
, SynchronizedOnType {
|
||||
typealias Element = ElementType
|
||||
typealias ValueSetter = (Element) -> Void
|
||||
|
||||
private let _parent: CombineLatestProtocol
|
||||
|
||||
let _lock: NSRecursiveLock
|
||||
private let _index: Int
|
||||
private let _this: Disposable
|
||||
private let _setLatestValue: ValueSetter
|
||||
|
||||
init(lock: NSRecursiveLock, parent: CombineLatestProtocol, index: Int, setLatestValue: @escaping ValueSetter, this: Disposable) {
|
||||
_lock = lock
|
||||
_parent = parent
|
||||
_index = index
|
||||
_this = this
|
||||
_setLatestValue = setLatestValue
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>) {
|
||||
synchronizedOn(event)
|
||||
}
|
||||
|
||||
func _synchronized_on(_ event: Event<Element>) {
|
||||
switch event {
|
||||
case .next(let value):
|
||||
_setLatestValue(value)
|
||||
_parent.next(_index)
|
||||
case .error(let error):
|
||||
_this.dispose()
|
||||
_parent.fail(error)
|
||||
case .completed:
|
||||
_this.dispose()
|
||||
_parent.done(_index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// Concat.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/21/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
class ConcatSink<S: Sequence, O: ObserverType>
|
||||
: TailRecursiveSink<S, O>
|
||||
, ObserverType where S.Iterator.Element : ObservableConvertibleType, S.Iterator.Element.E == O.E {
|
||||
typealias Element = O.E
|
||||
|
||||
override init(observer: O) {
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>){
|
||||
switch event {
|
||||
case .next:
|
||||
forwardOn(event)
|
||||
case .error:
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
case .completed:
|
||||
schedule(.moveNext)
|
||||
}
|
||||
}
|
||||
|
||||
override func subscribeToNext(_ source: Observable<E>) -> Disposable {
|
||||
return source.subscribe(self)
|
||||
}
|
||||
|
||||
override func extract(_ observable: Observable<E>) -> SequenceGenerator? {
|
||||
if let source = observable as? Concat<S> {
|
||||
return (source._sources.makeIterator(), source._count)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Concat<S: Sequence> : Producer<S.Iterator.Element.E> where S.Iterator.Element : ObservableConvertibleType {
|
||||
typealias Element = S.Iterator.Element.E
|
||||
|
||||
fileprivate let _sources: S
|
||||
fileprivate let _count: IntMax?
|
||||
|
||||
init(sources: S, count: IntMax?) {
|
||||
_sources = sources
|
||||
_count = count
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = ConcatSink<S, O>(observer: observer)
|
||||
sink.disposable = sink.run((_sources.makeIterator(), _count))
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// ConnectableObservable.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/1/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Represents an observable wrapper that can be connected and disconnected from its underlying observable sequence.
|
||||
*/
|
||||
public class ConnectableObservable<Element>
|
||||
: Observable<Element>
|
||||
, ConnectableObservableType {
|
||||
|
||||
/**
|
||||
Connects the observable wrapper to its source. All subscribed observers will receive values from the underlying observable sequence as long as the connection is established.
|
||||
|
||||
- returns: Disposable used to disconnect the observable wrapper from its source, causing subscribed observer to stop receiving values from the underlying observable sequence.
|
||||
*/
|
||||
public func connect() -> Disposable {
|
||||
abstractMethod()
|
||||
}
|
||||
}
|
||||
|
||||
class Connection<S: SubjectType> : Disposable {
|
||||
|
||||
private var _lock: NSRecursiveLock
|
||||
// state
|
||||
private var _parent: ConnectableObservableAdapter<S>?
|
||||
private var _subscription : Disposable?
|
||||
|
||||
init(parent: ConnectableObservableAdapter<S>, lock: NSRecursiveLock, subscription: Disposable) {
|
||||
_parent = parent
|
||||
_subscription = subscription
|
||||
_lock = lock
|
||||
}
|
||||
|
||||
func dispose() {
|
||||
_lock.lock(); defer { _lock.unlock() } // {
|
||||
guard let parent = _parent else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let oldSubscription = _subscription else {
|
||||
return
|
||||
}
|
||||
|
||||
_subscription = nil
|
||||
if parent._connection === self {
|
||||
parent._connection = nil
|
||||
}
|
||||
_parent = nil
|
||||
|
||||
oldSubscription.dispose()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectableObservableAdapter<S: SubjectType>
|
||||
: ConnectableObservable<S.E> {
|
||||
typealias ConnectionType = Connection<S>
|
||||
|
||||
fileprivate let _subject: S
|
||||
fileprivate let _source: Observable<S.SubjectObserverType.E>
|
||||
|
||||
fileprivate let _lock = NSRecursiveLock()
|
||||
|
||||
// state
|
||||
fileprivate var _connection: ConnectionType?
|
||||
|
||||
init(source: Observable<S.SubjectObserverType.E>, subject: S) {
|
||||
_source = source
|
||||
_subject = subject
|
||||
_connection = nil
|
||||
}
|
||||
|
||||
override func connect() -> Disposable {
|
||||
return _lock.calculateLocked {
|
||||
if let connection = _connection {
|
||||
return connection
|
||||
}
|
||||
|
||||
let disposable = _source.subscribe(_subject.asObserver())
|
||||
let connection = Connection(parent: self, lock: _lock, subscription: disposable)
|
||||
_connection = connection
|
||||
return connection
|
||||
}
|
||||
}
|
||||
|
||||
override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == S.E {
|
||||
return _subject.subscribe(observer)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// Debug.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 5/2/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
|
||||
|
||||
func logEvent(_ identifier: String, dateFormat: DateFormatter, content: String) {
|
||||
print("\(dateFormat.string(from: Date())): \(identifier) -> \(content)")
|
||||
}
|
||||
|
||||
class DebugSink<Source: ObservableType, O: ObserverType> : Sink<O>, ObserverType where O.E == Source.E {
|
||||
typealias Element = O.E
|
||||
typealias Parent = Debug<Source>
|
||||
|
||||
private let _parent: Parent
|
||||
private let _timestampFormatter = DateFormatter()
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
_timestampFormatter.dateFormat = dateFormat
|
||||
|
||||
logEvent(_parent._identifier, dateFormat: _timestampFormatter, content: "subscribed")
|
||||
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>) {
|
||||
let maxEventTextLength = 40
|
||||
let eventText = "\(event)"
|
||||
let eventNormalized = eventText.characters.count > maxEventTextLength
|
||||
? String(eventText.characters.prefix(maxEventTextLength / 2)) + "..." + String(eventText.characters.suffix(maxEventTextLength / 2))
|
||||
: eventText
|
||||
|
||||
logEvent(_parent._identifier, dateFormat: _timestampFormatter, content: "Event \(eventNormalized)")
|
||||
|
||||
forwardOn(event)
|
||||
if event.isStopEvent {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
override func dispose() {
|
||||
logEvent(_parent._identifier, dateFormat: _timestampFormatter, content: "isDisposed")
|
||||
super.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
class Debug<Source: ObservableType> : Producer<Source.E> {
|
||||
fileprivate let _identifier: String
|
||||
|
||||
fileprivate let _source: Source
|
||||
|
||||
init(source: Source, identifier: String?, file: String, line: UInt, function: String) {
|
||||
if let identifier = identifier {
|
||||
_identifier = identifier
|
||||
}
|
||||
else {
|
||||
let trimmedFile: String
|
||||
if let lastIndex = file.lastIndexOf("/") {
|
||||
trimmedFile = file[file.index(after: lastIndex) ..< file.endIndex]
|
||||
}
|
||||
else {
|
||||
trimmedFile = file
|
||||
}
|
||||
_identifier = "\(trimmedFile):\(line) (\(function))"
|
||||
}
|
||||
_source = source
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == Source.E {
|
||||
let sink = DebugSink(parent: self, observer: observer)
|
||||
sink.disposable = _source.subscribe(sink)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Debunce.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 9/11/16.
|
||||
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class DebounceSink<O: ObserverType>
|
||||
: Sink<O>
|
||||
, ObserverType
|
||||
, LockOwnerType
|
||||
, SynchronizedOnType {
|
||||
typealias Element = O.E
|
||||
typealias ParentType = Debounce<Element>
|
||||
|
||||
private let _parent: ParentType
|
||||
|
||||
let _lock = NSRecursiveLock()
|
||||
|
||||
// state
|
||||
private var _id = 0 as UInt64
|
||||
private var _value: Element? = nil
|
||||
|
||||
let cancellable = SerialDisposable()
|
||||
|
||||
init(parent: ParentType, observer: O) {
|
||||
_parent = parent
|
||||
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let subscription = _parent._source.subscribe(self)
|
||||
|
||||
return Disposables.create(subscription, cancellable)
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>) {
|
||||
synchronizedOn(event)
|
||||
}
|
||||
|
||||
func _synchronized_on(_ event: Event<Element>) {
|
||||
switch event {
|
||||
case .next(let element):
|
||||
_id = _id &+ 1
|
||||
let currentId = _id
|
||||
_value = element
|
||||
|
||||
|
||||
let scheduler = _parent._scheduler
|
||||
let dueTime = _parent._dueTime
|
||||
|
||||
let d = SingleAssignmentDisposable()
|
||||
self.cancellable.disposable = d
|
||||
d.disposable = scheduler.scheduleRelative(currentId, dueTime: dueTime, action: self.propagate)
|
||||
case .error:
|
||||
_value = nil
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
case .completed:
|
||||
if let value = _value {
|
||||
_value = nil
|
||||
forwardOn(.next(value))
|
||||
}
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
func propagate(_ currentId: UInt64) -> Disposable {
|
||||
_lock.lock(); defer { _lock.unlock() } // {
|
||||
let originalValue = _value
|
||||
|
||||
if let value = originalValue, _id == currentId {
|
||||
_value = nil
|
||||
forwardOn(.next(value))
|
||||
}
|
||||
// }
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
class Debounce<Element> : Producer<Element> {
|
||||
|
||||
fileprivate let _source: Observable<Element>
|
||||
fileprivate let _dueTime: RxTimeInterval
|
||||
fileprivate let _scheduler: SchedulerType
|
||||
|
||||
init(source: Observable<Element>, dueTime: RxTimeInterval, scheduler: SchedulerType) {
|
||||
_source = source
|
||||
_dueTime = dueTime
|
||||
_scheduler = scheduler
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = DebounceSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Deferred.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 4/19/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class DeferredSink<S: ObservableType, O: ObserverType> : Sink<O>, ObserverType where S.E == O.E {
|
||||
typealias E = O.E
|
||||
|
||||
private let _observableFactory: () throws -> S
|
||||
|
||||
init(observableFactory: @escaping () throws -> S, observer: O) {
|
||||
_observableFactory = observableFactory
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
do {
|
||||
let result = try _observableFactory()
|
||||
return result.subscribe(self)
|
||||
}
|
||||
catch let e {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
forwardOn(event)
|
||||
|
||||
switch event {
|
||||
case .next:
|
||||
break
|
||||
case .error:
|
||||
dispose()
|
||||
case .completed:
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Deferred<S: ObservableType> : Producer<S.E> {
|
||||
typealias Factory = () throws -> S
|
||||
|
||||
private let _observableFactory : Factory
|
||||
|
||||
init(observableFactory: @escaping Factory) {
|
||||
_observableFactory = observableFactory
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == S.E {
|
||||
let sink = DeferredSink(observableFactory: _observableFactory, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
//
|
||||
// Delay.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by tarunon on 2016/02/09.
|
||||
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class DelaySink<ElementType, O: ObserverType>
|
||||
: Sink<O>
|
||||
, ObserverType where O.E == ElementType {
|
||||
typealias E = O.E
|
||||
typealias Source = Observable<E>
|
||||
typealias DisposeKey = Bag<Disposable>.KeyType
|
||||
|
||||
private let _lock = NSRecursiveLock()
|
||||
|
||||
private let _dueTime: RxTimeInterval
|
||||
private let _scheduler: SchedulerType
|
||||
|
||||
private let _sourceSubscription = SingleAssignmentDisposable()
|
||||
private let _cancelable = SerialDisposable()
|
||||
|
||||
// is scheduled some action
|
||||
private var _active = false
|
||||
// is "run loop" on different scheduler running
|
||||
private var _running = false
|
||||
private var _errorEvent: Event<E>? = nil
|
||||
|
||||
// state
|
||||
private var _queue = Queue<(eventTime: RxTime, event: Event<E>)>(capacity: 0)
|
||||
private var _disposed = false
|
||||
|
||||
init(observer: O, dueTime: RxTimeInterval, scheduler: SchedulerType) {
|
||||
_dueTime = dueTime
|
||||
_scheduler = scheduler
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
// All of these complications in this method are caused by the fact that
|
||||
// error should be propagated immediatelly. Error can bepotentially received on different
|
||||
// scheduler so this process needs to be synchronized somehow.
|
||||
//
|
||||
// Another complication is that scheduler is potentially concurrent so internal queue is used.
|
||||
func drainQueue(state: (), scheduler: AnyRecursiveScheduler<()>) {
|
||||
|
||||
_lock.lock() // {
|
||||
let hasFailed = _errorEvent != nil
|
||||
if !hasFailed {
|
||||
_running = true
|
||||
}
|
||||
_lock.unlock() // }
|
||||
|
||||
if hasFailed {
|
||||
return
|
||||
}
|
||||
|
||||
var ranAtLeastOnce = false
|
||||
|
||||
while true {
|
||||
_lock.lock() // {
|
||||
let errorEvent = _errorEvent
|
||||
|
||||
let eventToForwardImmediatelly = ranAtLeastOnce ? nil : _queue.dequeue()?.event
|
||||
let nextEventToScheduleOriginalTime: Date? = ranAtLeastOnce && !_queue.isEmpty ? _queue.peek().eventTime : nil
|
||||
|
||||
if let _ = errorEvent {
|
||||
}
|
||||
else {
|
||||
if let _ = eventToForwardImmediatelly {
|
||||
}
|
||||
else if let _ = nextEventToScheduleOriginalTime {
|
||||
_running = false
|
||||
}
|
||||
else {
|
||||
_running = false
|
||||
_active = false
|
||||
}
|
||||
}
|
||||
_lock.unlock() // {
|
||||
|
||||
if let errorEvent = errorEvent {
|
||||
self.forwardOn(errorEvent)
|
||||
self.dispose()
|
||||
return
|
||||
}
|
||||
else {
|
||||
if let eventToForwardImmediatelly = eventToForwardImmediatelly {
|
||||
ranAtLeastOnce = true
|
||||
self.forwardOn(eventToForwardImmediatelly)
|
||||
if case .completed = eventToForwardImmediatelly {
|
||||
self.dispose()
|
||||
return
|
||||
}
|
||||
}
|
||||
else if let nextEventToScheduleOriginalTime = nextEventToScheduleOriginalTime {
|
||||
let elapsedTime = _scheduler.now.timeIntervalSince(nextEventToScheduleOriginalTime)
|
||||
let interval = _dueTime - elapsedTime
|
||||
let normalizedInterval = interval < 0.0 ? 0.0 : interval
|
||||
scheduler.schedule((), dueTime: normalizedInterval)
|
||||
return
|
||||
}
|
||||
else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
if event.isStopEvent {
|
||||
_sourceSubscription.dispose()
|
||||
}
|
||||
|
||||
switch event {
|
||||
case .error(_):
|
||||
_lock.lock() // {
|
||||
let shouldSendImmediatelly = !_running
|
||||
_queue = Queue(capacity: 0)
|
||||
_errorEvent = event
|
||||
_lock.unlock() // }
|
||||
|
||||
if shouldSendImmediatelly {
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
}
|
||||
default:
|
||||
_lock.lock() // {
|
||||
let shouldSchedule = !_active
|
||||
_active = true
|
||||
_queue.enqueue((_scheduler.now, event))
|
||||
_lock.unlock() // }
|
||||
|
||||
if shouldSchedule {
|
||||
_cancelable.disposable = _scheduler.scheduleRecursive((), dueTime: _dueTime, action: self.drainQueue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func run(source: Source) -> Disposable {
|
||||
_sourceSubscription.disposable = source.subscribeSafe(self)
|
||||
return Disposables.create(_sourceSubscription, _cancelable)
|
||||
}
|
||||
}
|
||||
|
||||
class Delay<Element>: Producer<Element> {
|
||||
private let _source: Observable<Element>
|
||||
private let _dueTime: RxTimeInterval
|
||||
private let _scheduler: SchedulerType
|
||||
|
||||
init(source: Observable<Element>, dueTime: RxTimeInterval, scheduler: SchedulerType) {
|
||||
_source = source
|
||||
_dueTime = dueTime
|
||||
_scheduler = scheduler
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = DelaySink(observer: observer, dueTime: _dueTime, scheduler: _scheduler)
|
||||
sink.disposable = sink.run(source: _source)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// DelaySubscription.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 6/14/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class DelaySubscriptionSink<ElementType, O: ObserverType>
|
||||
: Sink<O>
|
||||
, ObserverType where O.E == ElementType {
|
||||
typealias Parent = DelaySubscription<ElementType>
|
||||
typealias E = O.E
|
||||
|
||||
private let _parent: Parent
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
forwardOn(event)
|
||||
if event.isStopEvent {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DelaySubscription<Element>: Producer<Element> {
|
||||
private let _source: Observable<Element>
|
||||
private let _dueTime: RxTimeInterval
|
||||
private let _scheduler: SchedulerType
|
||||
|
||||
init(source: Observable<Element>, dueTime: RxTimeInterval, scheduler: SchedulerType) {
|
||||
_source = source
|
||||
_dueTime = dueTime
|
||||
_scheduler = scheduler
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = DelaySubscriptionSink(parent: self, observer: observer)
|
||||
sink.disposable = _scheduler.scheduleRelative((), dueTime: _dueTime) { _ in
|
||||
return self._source.subscribe(sink)
|
||||
}
|
||||
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// DistinctUntilChanged.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/15/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class DistinctUntilChangedSink<O: ObserverType, Key>: Sink<O>, ObserverType {
|
||||
typealias E = O.E
|
||||
|
||||
private let _parent: DistinctUntilChanged<E, Key>
|
||||
private var _currentKey: Key? = nil
|
||||
|
||||
init(parent: DistinctUntilChanged<E, Key>, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
switch event {
|
||||
case .next(let value):
|
||||
do {
|
||||
let key = try _parent._selector(value)
|
||||
var areEqual = false
|
||||
if let currentKey = _currentKey {
|
||||
areEqual = try _parent._comparer(currentKey, key)
|
||||
}
|
||||
|
||||
if areEqual {
|
||||
return
|
||||
}
|
||||
|
||||
_currentKey = key
|
||||
|
||||
forwardOn(event)
|
||||
}
|
||||
catch let error {
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
}
|
||||
case .error, .completed:
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DistinctUntilChanged<Element, Key>: Producer<Element> {
|
||||
typealias KeySelector = (Element) throws -> Key
|
||||
typealias EqualityComparer = (Key, Key) throws -> Bool
|
||||
|
||||
fileprivate let _source: Observable<Element>
|
||||
fileprivate let _selector: KeySelector
|
||||
fileprivate let _comparer: EqualityComparer
|
||||
|
||||
init(source: Observable<Element>, selector: @escaping KeySelector, comparer: @escaping EqualityComparer) {
|
||||
_source = source
|
||||
_selector = selector
|
||||
_comparer = comparer
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = DistinctUntilChangedSink(parent: self, observer: observer)
|
||||
sink.disposable = _source.subscribe(sink)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// Do.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/21/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class DoSink<O: ObserverType> : Sink<O>, ObserverType {
|
||||
typealias Element = O.E
|
||||
typealias Parent = Do<Element>
|
||||
|
||||
private let _parent: Parent
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>) {
|
||||
do {
|
||||
try _parent._eventHandler(event)
|
||||
forwardOn(event)
|
||||
if event.isStopEvent {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
catch let error {
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Do<Element> : Producer<Element> {
|
||||
typealias EventHandler = (Event<Element>) throws -> Void
|
||||
|
||||
fileprivate let _source: Observable<Element>
|
||||
fileprivate let _eventHandler: EventHandler
|
||||
fileprivate let _onSubscribe: (() -> ())?
|
||||
fileprivate let _onDispose: (() -> ())?
|
||||
|
||||
init(source: Observable<Element>, eventHandler: @escaping EventHandler, onSubscribe: (() -> ())?, onDispose: (() -> ())?) {
|
||||
_source = source
|
||||
_eventHandler = eventHandler
|
||||
_onSubscribe = onSubscribe
|
||||
_onDispose = onDispose
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
_onSubscribe?()
|
||||
let sink = DoSink(parent: self, observer: observer)
|
||||
let subscription = _source.subscribe(sink)
|
||||
let onDispose = _onDispose
|
||||
sink.disposable = Disposables.create {
|
||||
subscription.dispose()
|
||||
onDispose?()
|
||||
}
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// ElementAt.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Junior B. on 21/10/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
class ElementAtSink<SourceType, O: ObserverType> : Sink<O>, ObserverType where O.E == SourceType {
|
||||
typealias Parent = ElementAt<SourceType>
|
||||
|
||||
let _parent: Parent
|
||||
var _i: Int
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
_i = parent._index
|
||||
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<SourceType>) {
|
||||
switch event {
|
||||
case .next(_):
|
||||
|
||||
if (_i == 0) {
|
||||
forwardOn(event)
|
||||
forwardOn(.completed)
|
||||
self.dispose()
|
||||
}
|
||||
|
||||
do {
|
||||
let _ = try decrementChecked(&_i)
|
||||
} catch(let e) {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
return
|
||||
}
|
||||
|
||||
case .error(let e):
|
||||
forwardOn(.error(e))
|
||||
self.dispose()
|
||||
case .completed:
|
||||
if (_parent._throwOnEmpty) {
|
||||
forwardOn(.error(RxError.argumentOutOfRange))
|
||||
} else {
|
||||
forwardOn(.completed)
|
||||
}
|
||||
|
||||
self.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ElementAt<SourceType> : Producer<SourceType> {
|
||||
|
||||
let _source: Observable<SourceType>
|
||||
let _throwOnEmpty: Bool
|
||||
let _index: Int
|
||||
|
||||
init(source: Observable<SourceType>, index: Int, throwOnEmpty: Bool) {
|
||||
if index < 0 {
|
||||
rxFatalError("index can't be negative")
|
||||
}
|
||||
|
||||
self._source = source
|
||||
self._index = index
|
||||
self._throwOnEmpty = throwOnEmpty
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == SourceType {
|
||||
let sink = ElementAtSink(parent: self, observer: observer)
|
||||
sink.disposable = _source.subscribeSafe(sink)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Empty.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 8/30/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class Empty<Element> : Producer<Element> {
|
||||
override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
observer.on(.completed)
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Error.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 8/30/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class Error<Element> : Producer<Element> {
|
||||
private let _error: Swift.Error
|
||||
|
||||
init(error: Swift.Error) {
|
||||
_error = error
|
||||
}
|
||||
|
||||
override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
observer.on(.error(_error))
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// Filter.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/17/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class FilterSink<O : ObserverType>: Sink<O>, ObserverType {
|
||||
typealias Predicate = (Element) throws -> Bool
|
||||
typealias Element = O.E
|
||||
|
||||
private let _predicate: Predicate
|
||||
|
||||
init(predicate: @escaping Predicate, observer: O) {
|
||||
_predicate = predicate
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>) {
|
||||
switch event {
|
||||
case .next(let value):
|
||||
do {
|
||||
let satisfies = try _predicate(value)
|
||||
if satisfies {
|
||||
forwardOn(.next(value))
|
||||
}
|
||||
}
|
||||
catch let e {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
}
|
||||
case .completed, .error:
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Filter<Element> : Producer<Element> {
|
||||
typealias Predicate = (Element) throws -> Bool
|
||||
|
||||
private let _source: Observable<Element>
|
||||
private let _predicate: Predicate
|
||||
|
||||
init(source: Observable<Element>, predicate: @escaping Predicate) {
|
||||
_source = source
|
||||
_predicate = predicate
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = FilterSink(predicate: _predicate, observer: observer)
|
||||
sink.disposable = _source.subscribe(sink)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// Generate.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 9/2/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class GenerateSink<S, O: ObserverType> : Sink<O> {
|
||||
typealias Parent = Generate<S, O.E>
|
||||
|
||||
private let _parent: Parent
|
||||
|
||||
private var _state: S
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
_state = parent._initialState
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
return _parent._scheduler.scheduleRecursive(true) { (isFirst, recurse) -> Void in
|
||||
do {
|
||||
if !isFirst {
|
||||
self._state = try self._parent._iterate(self._state)
|
||||
}
|
||||
|
||||
if try self._parent._condition(self._state) {
|
||||
let result = try self._parent._resultSelector(self._state)
|
||||
self.forwardOn(.next(result))
|
||||
|
||||
recurse(false)
|
||||
}
|
||||
else {
|
||||
self.forwardOn(.completed)
|
||||
self.dispose()
|
||||
}
|
||||
}
|
||||
catch let error {
|
||||
self.forwardOn(.error(error))
|
||||
self.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Generate<S, E> : Producer<E> {
|
||||
fileprivate let _initialState: S
|
||||
fileprivate let _condition: (S) throws -> Bool
|
||||
fileprivate let _iterate: (S) throws -> S
|
||||
fileprivate let _resultSelector: (S) throws -> E
|
||||
fileprivate let _scheduler: ImmediateSchedulerType
|
||||
|
||||
init(initialState: S, condition: @escaping (S) throws -> Bool, iterate: @escaping (S) throws -> S, resultSelector: @escaping (S) throws -> E, scheduler: ImmediateSchedulerType) {
|
||||
_initialState = initialState
|
||||
_condition = condition
|
||||
_iterate = iterate
|
||||
_resultSelector = resultSelector
|
||||
_scheduler = scheduler
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == E {
|
||||
let sink = GenerateSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Just.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 8/30/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class JustScheduledSink<O: ObserverType> : Sink<O> {
|
||||
typealias Parent = JustScheduled<O.E>
|
||||
|
||||
private let _parent: Parent
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let scheduler = _parent._scheduler
|
||||
return scheduler.schedule(_parent._element) { element in
|
||||
self.forwardOn(.next(element))
|
||||
return scheduler.schedule(()) { _ in
|
||||
self.forwardOn(.completed)
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class JustScheduled<Element> : Producer<Element> {
|
||||
fileprivate let _scheduler: ImmediateSchedulerType
|
||||
fileprivate let _element: Element
|
||||
|
||||
init(element: Element, scheduler: ImmediateSchedulerType) {
|
||||
_scheduler = scheduler
|
||||
_element = element
|
||||
}
|
||||
|
||||
override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == E {
|
||||
let sink = JustScheduledSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
class Just<Element> : Producer<Element> {
|
||||
private let _element: Element
|
||||
|
||||
init(element: Element) {
|
||||
_element = element
|
||||
}
|
||||
|
||||
override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
observer.on(.next(_element))
|
||||
observer.on(.completed)
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
//
|
||||
// Map.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/15/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class MapSink<SourceType, O : ObserverType> : Sink<O>, ObserverType {
|
||||
typealias Selector = (SourceType) throws -> ResultType
|
||||
|
||||
typealias ResultType = O.E
|
||||
typealias Element = SourceType
|
||||
|
||||
private let _selector: Selector
|
||||
|
||||
init(selector: @escaping Selector, observer: O) {
|
||||
_selector = selector
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<SourceType>) {
|
||||
switch event {
|
||||
case .next(let element):
|
||||
do {
|
||||
let mappedElement = try _selector(element)
|
||||
forwardOn(.next(mappedElement))
|
||||
}
|
||||
catch let e {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
}
|
||||
case .error(let error):
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
case .completed:
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MapWithIndexSink<SourceType, O : ObserverType> : Sink<O>, ObserverType {
|
||||
typealias Selector = (SourceType, Int) throws -> ResultType
|
||||
|
||||
typealias ResultType = O.E
|
||||
typealias Element = SourceType
|
||||
typealias Parent = MapWithIndex<SourceType, ResultType>
|
||||
|
||||
private let _selector: Selector
|
||||
|
||||
private var _index = 0
|
||||
|
||||
init(selector: @escaping Selector, observer: O) {
|
||||
_selector = selector
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<SourceType>) {
|
||||
switch event {
|
||||
case .next(let element):
|
||||
do {
|
||||
let mappedElement = try _selector(element, try incrementChecked(&_index))
|
||||
forwardOn(.next(mappedElement))
|
||||
}
|
||||
catch let e {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
}
|
||||
case .error(let error):
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
case .completed:
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MapWithIndex<SourceType, ResultType> : Producer<ResultType> {
|
||||
typealias Selector = (SourceType, Int) throws -> ResultType
|
||||
|
||||
private let _source: Observable<SourceType>
|
||||
|
||||
private let _selector: Selector
|
||||
|
||||
init(source: Observable<SourceType>, selector: @escaping Selector) {
|
||||
_source = source
|
||||
_selector = selector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == ResultType {
|
||||
let sink = MapWithIndexSink(selector: _selector, observer: observer)
|
||||
sink.disposable = _source.subscribe(sink)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
#if TRACE_RESOURCES
|
||||
public var numberOfMapOperators: Int32 = 0
|
||||
#endif
|
||||
|
||||
class Map<SourceType, ResultType>: Producer<ResultType> {
|
||||
typealias Selector = (SourceType) throws -> ResultType
|
||||
|
||||
private let _source: Observable<SourceType>
|
||||
|
||||
private let _selector: Selector
|
||||
|
||||
init(source: Observable<SourceType>, selector: @escaping Selector) {
|
||||
_source = source
|
||||
_selector = selector
|
||||
|
||||
#if TRACE_RESOURCES
|
||||
let _ = AtomicIncrement(&numberOfMapOperators)
|
||||
#endif
|
||||
}
|
||||
|
||||
override func composeMap<R>(_ selector: @escaping (ResultType) throws -> R) -> Observable<R> {
|
||||
let originalSelector = _selector
|
||||
return Map<SourceType, R>(source: _source, selector: { (s: SourceType) throws -> R in
|
||||
let r: ResultType = try originalSelector(s)
|
||||
return try selector(r)
|
||||
})
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == ResultType {
|
||||
let sink = MapSink(selector: _selector, observer: observer)
|
||||
sink.disposable = _source.subscribe(sink)
|
||||
return sink
|
||||
}
|
||||
|
||||
#if TRACE_RESOURCES
|
||||
deinit {
|
||||
let _ = AtomicDecrement(&numberOfMapOperators)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
//
|
||||
// Merge.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/28/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: Limited concurrency version
|
||||
|
||||
class MergeLimitedSinkIter<S: ObservableConvertibleType, O: ObserverType>
|
||||
: ObserverType
|
||||
, LockOwnerType
|
||||
, SynchronizedOnType where S.E == O.E {
|
||||
typealias E = O.E
|
||||
typealias DisposeKey = Bag<Disposable>.KeyType
|
||||
typealias Parent = MergeLimitedSink<S, O>
|
||||
|
||||
private let _parent: Parent
|
||||
private let _disposeKey: DisposeKey
|
||||
|
||||
var _lock: NSRecursiveLock {
|
||||
return _parent._lock
|
||||
}
|
||||
|
||||
init(parent: Parent, disposeKey: DisposeKey) {
|
||||
_parent = parent
|
||||
_disposeKey = disposeKey
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
synchronizedOn(event)
|
||||
}
|
||||
|
||||
func _synchronized_on(_ event: Event<E>) {
|
||||
switch event {
|
||||
case .next:
|
||||
_parent.forwardOn(event)
|
||||
case .error:
|
||||
_parent.forwardOn(event)
|
||||
_parent.dispose()
|
||||
case .completed:
|
||||
_parent._group.remove(for: _disposeKey)
|
||||
if let next = _parent._queue.dequeue() {
|
||||
_parent.subscribe(next, group: _parent._group)
|
||||
}
|
||||
else {
|
||||
_parent._activeCount = _parent._activeCount - 1
|
||||
|
||||
if _parent._stopped && _parent._activeCount == 0 {
|
||||
_parent.forwardOn(.completed)
|
||||
_parent.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MergeLimitedSink<S: ObservableConvertibleType, O: ObserverType>
|
||||
: Sink<O>
|
||||
, ObserverType
|
||||
, LockOwnerType
|
||||
, SynchronizedOnType where S.E == O.E {
|
||||
typealias E = S
|
||||
typealias QueueType = Queue<S>
|
||||
|
||||
fileprivate let _maxConcurrent: Int
|
||||
|
||||
let _lock = NSRecursiveLock()
|
||||
|
||||
// state
|
||||
fileprivate var _stopped = false
|
||||
fileprivate var _activeCount = 0
|
||||
fileprivate var _queue = QueueType(capacity: 2)
|
||||
|
||||
fileprivate let _sourceSubscription = SingleAssignmentDisposable()
|
||||
fileprivate let _group = CompositeDisposable()
|
||||
|
||||
init(maxConcurrent: Int, observer: O) {
|
||||
_maxConcurrent = maxConcurrent
|
||||
|
||||
let _ = _group.insert(_sourceSubscription)
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run(_ source: Observable<S>) -> Disposable {
|
||||
let _ = _group.insert(_sourceSubscription)
|
||||
|
||||
let disposable = source.subscribe(self)
|
||||
_sourceSubscription.disposable = disposable
|
||||
return _group
|
||||
}
|
||||
|
||||
func subscribe(_ innerSource: E, group: CompositeDisposable) {
|
||||
let subscription = SingleAssignmentDisposable()
|
||||
|
||||
let key = group.insert(subscription)
|
||||
|
||||
if let key = key {
|
||||
let observer = MergeLimitedSinkIter(parent: self, disposeKey: key)
|
||||
|
||||
let disposable = innerSource.asObservable().subscribe(observer)
|
||||
subscription.disposable = disposable
|
||||
}
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
synchronizedOn(event)
|
||||
}
|
||||
|
||||
func _synchronized_on(_ event: Event<E>) {
|
||||
switch event {
|
||||
case .next(let value):
|
||||
let subscribe: Bool
|
||||
if _activeCount < _maxConcurrent {
|
||||
_activeCount += 1
|
||||
subscribe = true
|
||||
}
|
||||
else {
|
||||
_queue.enqueue(value)
|
||||
subscribe = false
|
||||
}
|
||||
|
||||
if subscribe {
|
||||
self.subscribe(value, group: _group)
|
||||
}
|
||||
case .error(let error):
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
case .completed:
|
||||
if _activeCount == 0 {
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
else {
|
||||
_sourceSubscription.dispose()
|
||||
}
|
||||
|
||||
_stopped = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MergeLimited<S: ObservableConvertibleType> : Producer<S.E> {
|
||||
private let _source: Observable<S>
|
||||
private let _maxConcurrent: Int
|
||||
|
||||
init(source: Observable<S>, maxConcurrent: Int) {
|
||||
_source = source
|
||||
_maxConcurrent = maxConcurrent
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == S.E {
|
||||
let sink = MergeLimitedSink<S, O>(maxConcurrent: _maxConcurrent, observer: observer)
|
||||
sink.disposable = sink.run(_source)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Merge
|
||||
|
||||
final class MergeBasicSink<S: ObservableConvertibleType, O: ObserverType> : MergeSink<S, S, O> where O.E == S.E {
|
||||
override init(observer: O) {
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
override func performMap(_ element: S) throws -> S {
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: flatMap
|
||||
|
||||
final class FlatMapSink<SourceType, S: ObservableConvertibleType, O: ObserverType> : MergeSink<SourceType, S, O> where O.E == S.E {
|
||||
typealias Selector = (SourceType) throws -> S
|
||||
|
||||
private let _selector: Selector
|
||||
|
||||
init(selector: @escaping Selector, observer: O) {
|
||||
_selector = selector
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
override func performMap(_ element: SourceType) throws -> S {
|
||||
return try _selector(element)
|
||||
}
|
||||
}
|
||||
|
||||
final class FlatMapWithIndexSink<SourceType, S: ObservableConvertibleType, O: ObserverType> : MergeSink<SourceType, S, O> where O.E == S.E {
|
||||
typealias Selector = (SourceType, Int) throws -> S
|
||||
|
||||
private var _index = 0
|
||||
private let _selector: Selector
|
||||
|
||||
init(selector: @escaping Selector, observer: O) {
|
||||
_selector = selector
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
override func performMap(_ element: SourceType) throws -> S {
|
||||
return try _selector(element, try incrementChecked(&_index))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: FlatMapFirst
|
||||
|
||||
final class FlatMapFirstSink<SourceType, S: ObservableConvertibleType, O: ObserverType> : MergeSink<SourceType, S, O> where O.E == S.E {
|
||||
typealias Selector = (SourceType) throws -> S
|
||||
|
||||
private let _selector: Selector
|
||||
|
||||
override var subscribeNext: Bool {
|
||||
return _group.count == MergeNoIterators
|
||||
}
|
||||
|
||||
init(selector: @escaping Selector, observer: O) {
|
||||
_selector = selector
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
override func performMap(_ element: SourceType) throws -> S {
|
||||
return try _selector(element)
|
||||
}
|
||||
}
|
||||
|
||||
// It's value is one because initial source subscription is always in CompositeDisposable
|
||||
private let MergeNoIterators = 1
|
||||
|
||||
class MergeSinkIter<SourceType, S: ObservableConvertibleType, O: ObserverType> : ObserverType where O.E == S.E {
|
||||
typealias Parent = MergeSink<SourceType, S, O>
|
||||
typealias DisposeKey = CompositeDisposable.DisposeKey
|
||||
typealias E = O.E
|
||||
|
||||
private let _parent: Parent
|
||||
private let _disposeKey: DisposeKey
|
||||
|
||||
init(parent: Parent, disposeKey: DisposeKey) {
|
||||
_parent = parent
|
||||
_disposeKey = disposeKey
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
switch event {
|
||||
case .next(let value):
|
||||
_parent._lock.lock(); defer { _parent._lock.unlock() } // lock {
|
||||
_parent.forwardOn(.next(value))
|
||||
// }
|
||||
case .error(let error):
|
||||
_parent._lock.lock(); defer { _parent._lock.unlock() } // lock {
|
||||
_parent.forwardOn(.error(error))
|
||||
_parent.dispose()
|
||||
// }
|
||||
case .completed:
|
||||
_parent._group.remove(for: _disposeKey)
|
||||
// If this has returned true that means that `Completed` should be sent.
|
||||
// In case there is a race who will sent first completed,
|
||||
// lock will sort it out. When first Completed message is sent
|
||||
// it will set observer to nil, and thus prevent further complete messages
|
||||
// to be sent, and thus preserving the sequence grammar.
|
||||
if _parent._stopped && _parent._group.count == MergeNoIterators {
|
||||
_parent._lock.lock(); defer { _parent._lock.unlock() } // lock {
|
||||
_parent.forwardOn(.completed)
|
||||
_parent.dispose()
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MergeSink<SourceType, S: ObservableConvertibleType, O: ObserverType>
|
||||
: Sink<O>
|
||||
, ObserverType where O.E == S.E {
|
||||
typealias ResultType = O.E
|
||||
typealias Element = SourceType
|
||||
|
||||
fileprivate let _lock = NSRecursiveLock()
|
||||
|
||||
fileprivate var subscribeNext: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// state
|
||||
fileprivate let _group = CompositeDisposable()
|
||||
fileprivate let _sourceSubscription = SingleAssignmentDisposable()
|
||||
|
||||
fileprivate var _stopped = false
|
||||
|
||||
override init(observer: O) {
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func performMap(_ element: SourceType) throws -> S {
|
||||
abstractMethod()
|
||||
}
|
||||
|
||||
func on(_ event: Event<SourceType>) {
|
||||
switch event {
|
||||
case .next(let element):
|
||||
if !subscribeNext {
|
||||
return
|
||||
}
|
||||
do {
|
||||
let value = try performMap(element)
|
||||
subscribeInner(value.asObservable())
|
||||
}
|
||||
catch let e {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
}
|
||||
case .error(let error):
|
||||
_lock.lock(); defer { _lock.unlock() } // lock {
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
// }
|
||||
case .completed:
|
||||
_lock.lock(); defer { _lock.unlock() } // lock {
|
||||
_stopped = true
|
||||
if _group.count == MergeNoIterators {
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
else {
|
||||
_sourceSubscription.dispose()
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
func subscribeInner(_ source: Observable<O.E>) {
|
||||
let iterDisposable = SingleAssignmentDisposable()
|
||||
if let disposeKey = _group.insert(iterDisposable) {
|
||||
let iter = MergeSinkIter(parent: self, disposeKey: disposeKey)
|
||||
let subscription = source.subscribe(iter)
|
||||
iterDisposable.disposable = subscription
|
||||
}
|
||||
}
|
||||
|
||||
func run(_ source: Observable<SourceType>) -> Disposable {
|
||||
let _ = _group.insert(_sourceSubscription)
|
||||
|
||||
let subscription = source.subscribe(self)
|
||||
_sourceSubscription.disposable = subscription
|
||||
|
||||
return _group
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Producers
|
||||
|
||||
final class FlatMap<SourceType, S: ObservableConvertibleType>: Producer<S.E> {
|
||||
typealias Selector = (SourceType) throws -> S
|
||||
|
||||
private let _source: Observable<SourceType>
|
||||
|
||||
private let _selector: Selector
|
||||
|
||||
init(source: Observable<SourceType>, selector: @escaping Selector) {
|
||||
_source = source
|
||||
_selector = selector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == S.E {
|
||||
let sink = FlatMapSink(selector: _selector, observer: observer)
|
||||
sink.disposable = sink.run(_source)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
final class FlatMapWithIndex<SourceType, S: ObservableConvertibleType>: Producer<S.E> {
|
||||
typealias Selector = (SourceType, Int) throws -> S
|
||||
|
||||
private let _source: Observable<SourceType>
|
||||
|
||||
private let _selector: Selector
|
||||
|
||||
init(source: Observable<SourceType>, selector: @escaping Selector) {
|
||||
_source = source
|
||||
_selector = selector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == S.E {
|
||||
let sink = FlatMapWithIndexSink<SourceType, S, O>(selector: _selector, observer: observer)
|
||||
sink.disposable = sink.run(_source)
|
||||
return sink
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final class FlatMapFirst<SourceType, S: ObservableConvertibleType>: Producer<S.E> {
|
||||
typealias Selector = (SourceType) throws -> S
|
||||
|
||||
private let _source: Observable<SourceType>
|
||||
|
||||
private let _selector: Selector
|
||||
|
||||
init(source: Observable<SourceType>, selector: @escaping Selector) {
|
||||
_source = source
|
||||
_selector = selector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == S.E {
|
||||
let sink = FlatMapFirstSink<SourceType, S, O>(selector: _selector, observer: observer)
|
||||
sink.disposable = sink.run(_source)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
final class Merge<S: ObservableConvertibleType> : Producer<S.E> {
|
||||
private let _source: Observable<S>
|
||||
|
||||
init(source: Observable<S>) {
|
||||
_source = source
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == S.E {
|
||||
let sink = MergeBasicSink<S, O>(observer: observer)
|
||||
sink.disposable = sink.run(_source)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// Multicast.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/27/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class MulticastSink<S: SubjectType, O: ObserverType>: Sink<O>, ObserverType {
|
||||
typealias Element = O.E
|
||||
typealias ResultType = Element
|
||||
typealias MutlicastType = Multicast<S, O.E>
|
||||
|
||||
private let _parent: MutlicastType
|
||||
|
||||
init(parent: MutlicastType, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
do {
|
||||
let subject = try _parent._subjectSelector()
|
||||
let connectable = ConnectableObservableAdapter(source: _parent._source, subject: subject)
|
||||
|
||||
let observable = try _parent._selector(connectable)
|
||||
|
||||
let subscription = observable.subscribe(self)
|
||||
let connection = connectable.connect()
|
||||
|
||||
return Disposables.create(subscription, connection)
|
||||
}
|
||||
catch let e {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
func on(_ event: Event<ResultType>) {
|
||||
forwardOn(event)
|
||||
switch event {
|
||||
case .next: break
|
||||
case .error, .completed:
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Multicast<S: SubjectType, R>: Producer<R> {
|
||||
typealias SubjectSelectorType = () throws -> S
|
||||
typealias SelectorType = (Observable<S.E>) throws -> Observable<R>
|
||||
|
||||
fileprivate let _source: Observable<S.SubjectObserverType.E>
|
||||
fileprivate let _subjectSelector: SubjectSelectorType
|
||||
fileprivate let _selector: SelectorType
|
||||
|
||||
init(source: Observable<S.SubjectObserverType.E>, subjectSelector: @escaping SubjectSelectorType, selector: @escaping SelectorType) {
|
||||
_source = source
|
||||
_subjectSelector = subjectSelector
|
||||
_selector = selector
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == R {
|
||||
let sink = MulticastSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Never.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 8/30/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class Never<Element> : Producer<Element> {
|
||||
override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// ObserveOn.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 7/25/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ObserveOn<E> : Producer<E> {
|
||||
let scheduler: ImmediateSchedulerType
|
||||
let source: Observable<E>
|
||||
|
||||
init(source: Observable<E>, scheduler: ImmediateSchedulerType) {
|
||||
self.scheduler = scheduler
|
||||
self.source = source
|
||||
|
||||
#if TRACE_RESOURCES
|
||||
let _ = AtomicIncrement(&resourceCount)
|
||||
#endif
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == E {
|
||||
let sink = ObserveOnSink(scheduler: scheduler, observer: observer)
|
||||
sink._subscription.disposable = source.subscribe(sink)
|
||||
return sink
|
||||
}
|
||||
|
||||
#if TRACE_RESOURCES
|
||||
deinit {
|
||||
let _ = AtomicDecrement(&resourceCount)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
enum ObserveOnState : Int32 {
|
||||
// pump is not running
|
||||
case stopped = 0
|
||||
// pump is running
|
||||
case running = 1
|
||||
}
|
||||
|
||||
class ObserveOnSink<O: ObserverType> : ObserverBase<O.E> {
|
||||
typealias E = O.E
|
||||
|
||||
let _scheduler: ImmediateSchedulerType
|
||||
|
||||
var _lock = SpinLock()
|
||||
|
||||
// state
|
||||
var _state = ObserveOnState.stopped
|
||||
var _observer: O?
|
||||
var _queue = Queue<Event<E>>(capacity: 10)
|
||||
|
||||
let _scheduleDisposable = SerialDisposable()
|
||||
let _subscription = SingleAssignmentDisposable()
|
||||
|
||||
init(scheduler: ImmediateSchedulerType, observer: O) {
|
||||
_scheduler = scheduler
|
||||
_observer = observer
|
||||
}
|
||||
|
||||
override func onCore(_ event: Event<E>) {
|
||||
let shouldStart = _lock.calculateLocked { () -> Bool in
|
||||
self._queue.enqueue(event)
|
||||
|
||||
switch self._state {
|
||||
case .stopped:
|
||||
self._state = .running
|
||||
return true
|
||||
case .running:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if shouldStart {
|
||||
_scheduleDisposable.disposable = self._scheduler.scheduleRecursive((), action: self.run)
|
||||
}
|
||||
}
|
||||
|
||||
func run(_ state: Void, recurse: (Void) -> Void) {
|
||||
let (nextEvent, observer) = self._lock.calculateLocked { () -> (Event<E>?, O?) in
|
||||
if self._queue.count > 0 {
|
||||
return (self._queue.dequeue(), self._observer)
|
||||
}
|
||||
else {
|
||||
self._state = .stopped
|
||||
return (nil, self._observer)
|
||||
}
|
||||
}
|
||||
|
||||
if let nextEvent = nextEvent {
|
||||
observer?.on(nextEvent)
|
||||
if nextEvent.isStopEvent {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
let shouldContinue = _shouldContinue_synchronized()
|
||||
|
||||
if shouldContinue {
|
||||
recurse()
|
||||
}
|
||||
}
|
||||
|
||||
func _shouldContinue_synchronized() -> Bool {
|
||||
_lock.lock(); defer { _lock.unlock() } // {
|
||||
if self._queue.count > 0 {
|
||||
return true
|
||||
}
|
||||
else {
|
||||
self._state = .stopped
|
||||
return false
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
override func dispose() {
|
||||
super.dispose()
|
||||
|
||||
_subscription.dispose()
|
||||
_scheduleDisposable.dispose()
|
||||
|
||||
_lock.lock(); defer { _lock.unlock() } // {
|
||||
_observer = nil
|
||||
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// ObserveOnSerialDispatchQueue.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 5/31/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
#if TRACE_RESOURCES
|
||||
/**
|
||||
Counts number of `SerialDispatchQueueObservables`.
|
||||
|
||||
Purposed for unit tests.
|
||||
*/
|
||||
public var numberOfSerialDispatchQueueObservables: AtomicInt = 0
|
||||
#endif
|
||||
|
||||
class ObserveOnSerialDispatchQueueSink<O: ObserverType> : ObserverBase<O.E> {
|
||||
let scheduler: SerialDispatchQueueScheduler
|
||||
let observer: O
|
||||
|
||||
let subscription = SingleAssignmentDisposable()
|
||||
|
||||
var cachedScheduleLambda: ((ObserveOnSerialDispatchQueueSink<O>, Event<E>) -> Disposable)!
|
||||
|
||||
init(scheduler: SerialDispatchQueueScheduler, observer: O) {
|
||||
self.scheduler = scheduler
|
||||
self.observer = observer
|
||||
super.init()
|
||||
|
||||
cachedScheduleLambda = { sink, event in
|
||||
sink.observer.on(event)
|
||||
|
||||
if event.isStopEvent {
|
||||
sink.dispose()
|
||||
}
|
||||
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
override func onCore(_ event: Event<E>) {
|
||||
let _ = self.scheduler.schedule((self, event), action: cachedScheduleLambda)
|
||||
}
|
||||
|
||||
override func dispose() {
|
||||
super.dispose()
|
||||
|
||||
subscription.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
class ObserveOnSerialDispatchQueue<E> : Producer<E> {
|
||||
let scheduler: SerialDispatchQueueScheduler
|
||||
let source: Observable<E>
|
||||
|
||||
init(source: Observable<E>, scheduler: SerialDispatchQueueScheduler) {
|
||||
self.scheduler = scheduler
|
||||
self.source = source
|
||||
|
||||
#if TRACE_RESOURCES
|
||||
let _ = AtomicIncrement(&resourceCount)
|
||||
let _ = AtomicIncrement(&numberOfSerialDispatchQueueObservables)
|
||||
#endif
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == E {
|
||||
let sink = ObserveOnSerialDispatchQueueSink(scheduler: scheduler, observer: observer)
|
||||
sink.subscription.disposable = source.subscribe(sink)
|
||||
return sink
|
||||
}
|
||||
|
||||
#if TRACE_RESOURCES
|
||||
deinit {
|
||||
let _ = AtomicDecrement(&resourceCount)
|
||||
let _ = AtomicDecrement(&numberOfSerialDispatchQueueObservables)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Producer.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 2/20/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class Producer<Element> : Observable<Element> {
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
if !CurrentThreadScheduler.isScheduleRequired {
|
||||
return run(observer)
|
||||
}
|
||||
else {
|
||||
return CurrentThreadScheduler.instance.schedule(()) { _ in
|
||||
return self.run(observer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
abstractMethod()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Range.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 9/13/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class RangeProducer<E: SignedInteger> : Producer<E> {
|
||||
fileprivate let _start: E
|
||||
fileprivate let _count: E
|
||||
fileprivate let _scheduler: ImmediateSchedulerType
|
||||
|
||||
init(start: E, count: E, scheduler: ImmediateSchedulerType) {
|
||||
if count < 0 {
|
||||
rxFatalError("count can't be negative")
|
||||
}
|
||||
|
||||
if start &+ (count - 1) < start {
|
||||
rxFatalError("overflow of count")
|
||||
}
|
||||
|
||||
_start = start
|
||||
_count = count
|
||||
_scheduler = scheduler
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == E {
|
||||
let sink = RangeSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
class RangeSink<O: ObserverType> : Sink<O> where O.E: SignedInteger {
|
||||
typealias Parent = RangeProducer<O.E>
|
||||
|
||||
private let _parent: Parent
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
return _parent._scheduler.scheduleRecursive(0 as O.E) { i, recurse in
|
||||
if i < self._parent._count {
|
||||
self.forwardOn(.next(self._parent._start + i))
|
||||
recurse(i + 1)
|
||||
}
|
||||
else {
|
||||
self.forwardOn(.completed)
|
||||
self.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// Reduce.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 4/1/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ReduceSink<SourceType, AccumulateType, O: ObserverType> : Sink<O>, ObserverType {
|
||||
typealias ResultType = O.E
|
||||
typealias Parent = Reduce<SourceType, AccumulateType, ResultType>
|
||||
|
||||
private let _parent: Parent
|
||||
private var _accumulation: AccumulateType
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
_accumulation = parent._seed
|
||||
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<SourceType>) {
|
||||
switch event {
|
||||
case .next(let value):
|
||||
do {
|
||||
_accumulation = try _parent._accumulator(_accumulation, value)
|
||||
}
|
||||
catch let e {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
}
|
||||
case .error(let e):
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
case .completed:
|
||||
do {
|
||||
let result = try _parent._mapResult(_accumulation)
|
||||
forwardOn(.next(result))
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
catch let e {
|
||||
forwardOn(.error(e))
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Reduce<SourceType, AccumulateType, ResultType> : Producer<ResultType> {
|
||||
typealias AccumulatorType = (AccumulateType, SourceType) throws -> AccumulateType
|
||||
typealias ResultSelectorType = (AccumulateType) throws -> ResultType
|
||||
|
||||
fileprivate let _source: Observable<SourceType>
|
||||
fileprivate let _seed: AccumulateType
|
||||
fileprivate let _accumulator: AccumulatorType
|
||||
fileprivate let _mapResult: ResultSelectorType
|
||||
|
||||
init(source: Observable<SourceType>, seed: AccumulateType, accumulator: @escaping AccumulatorType, mapResult: @escaping ResultSelectorType) {
|
||||
_source = source
|
||||
_seed = seed
|
||||
_accumulator = accumulator
|
||||
_mapResult = mapResult
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == ResultType {
|
||||
let sink = ReduceSink(parent: self, observer: observer)
|
||||
sink.disposable = _source.subscribe(sink)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// RefCount.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Krunoslav Zaher on 3/5/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class RefCountSink<CO: ConnectableObservableType, O: ObserverType>
|
||||
: Sink<O>
|
||||
, ObserverType where CO.E == O.E {
|
||||
typealias Element = O.E
|
||||
typealias Parent = RefCount<CO>
|
||||
|
||||
private let _parent: Parent
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
let subscription = _parent._source.subscribeSafe(self)
|
||||
|
||||
_parent._lock.lock(); defer { _parent._lock.unlock() } // {
|
||||
if _parent._count == 0 {
|
||||
_parent._count = 1
|
||||
_parent._connectableSubscription = _parent._source.connect()
|
||||
}
|
||||
else {
|
||||
_parent._count = _parent._count + 1
|
||||
}
|
||||
// }
|
||||
|
||||
return Disposables.create {
|
||||
subscription.dispose()
|
||||
self._parent._lock.lock(); defer { self._parent._lock.unlock() } // {
|
||||
if self._parent._count == 1 {
|
||||
self._parent._connectableSubscription!.dispose()
|
||||
self._parent._count = 0
|
||||
self._parent._connectableSubscription = nil
|
||||
}
|
||||
else if self._parent._count > 1 {
|
||||
self._parent._count = self._parent._count - 1
|
||||
}
|
||||
else {
|
||||
rxFatalError("Something went wrong with RefCount disposing mechanism")
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>) {
|
||||
switch event {
|
||||
case .next:
|
||||
forwardOn(event)
|
||||
case .error, .completed:
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RefCount<CO: ConnectableObservableType>: Producer<CO.E> {
|
||||
fileprivate let _lock = NSRecursiveLock()
|
||||
|
||||
// state
|
||||
fileprivate var _count = 0
|
||||
fileprivate var _connectableSubscription = nil as Disposable?
|
||||
|
||||
fileprivate let _source: CO
|
||||
|
||||
init(source: CO) {
|
||||
_source = source
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == CO.E {
|
||||
let sink = RefCountSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Repeat.swift
|
||||
// RxExample
|
||||
//
|
||||
// Created by Krunoslav Zaher on 9/13/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class RepeatElement<Element> : Producer<Element> {
|
||||
fileprivate let _element: Element
|
||||
fileprivate let _scheduler: ImmediateSchedulerType
|
||||
|
||||
init(element: Element, scheduler: ImmediateSchedulerType) {
|
||||
_element = element
|
||||
_scheduler = scheduler
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = RepeatElementSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
|
||||
return sink
|
||||
}
|
||||
}
|
||||
|
||||
class RepeatElementSink<O: ObserverType> : Sink<O> {
|
||||
typealias Parent = RepeatElement<O.E>
|
||||
|
||||
private let _parent: Parent
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
return _parent._scheduler.scheduleRecursive(_parent._element) { e, recurse in
|
||||
self.forwardOn(.next(e))
|
||||
recurse(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
//
|
||||
// RetryWhen.swift
|
||||
// Rx
|
||||
//
|
||||
// Created by Junior B. on 06/10/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class RetryTriggerSink<S: Sequence, O: ObserverType, TriggerObservable: ObservableType, Error>
|
||||
: ObserverType where S.Iterator.Element : ObservableType, S.Iterator.Element.E == O.E {
|
||||
typealias E = TriggerObservable.E
|
||||
|
||||
typealias Parent = RetryWhenSequenceSinkIter<S, O, TriggerObservable, Error>
|
||||
|
||||
fileprivate let _parent: Parent
|
||||
|
||||
init(parent: Parent) {
|
||||
_parent = parent
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
switch event {
|
||||
case .next:
|
||||
_parent._parent._lastError = nil
|
||||
_parent._parent.schedule(.moveNext)
|
||||
case .error(let e):
|
||||
_parent._parent.forwardOn(.error(e))
|
||||
_parent._parent.dispose()
|
||||
case .completed:
|
||||
_parent._parent.forwardOn(.completed)
|
||||
_parent._parent.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RetryWhenSequenceSinkIter<S: Sequence, O: ObserverType, TriggerObservable: ObservableType, Error>
|
||||
: SingleAssignmentDisposable
|
||||
, ObserverType where S.Iterator.Element : ObservableType, S.Iterator.Element.E == O.E {
|
||||
typealias E = O.E
|
||||
typealias Parent = RetryWhenSequenceSink<S, O, TriggerObservable, Error>
|
||||
|
||||
fileprivate let _parent: Parent
|
||||
fileprivate let _errorHandlerSubscription = SingleAssignmentDisposable()
|
||||
|
||||
init(parent: Parent) {
|
||||
_parent = parent
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
switch event {
|
||||
case .next:
|
||||
_parent.forwardOn(event)
|
||||
case .error(let error):
|
||||
_parent._lastError = error
|
||||
|
||||
if let failedWith = error as? Error {
|
||||
// dispose current subscription
|
||||
super.dispose()
|
||||
|
||||
let errorHandlerSubscription = _parent._notifier.subscribe(RetryTriggerSink(parent: self))
|
||||
_errorHandlerSubscription.disposable = errorHandlerSubscription
|
||||
_parent._errorSubject.on(.next(failedWith))
|
||||
}
|
||||
else {
|
||||
_parent.forwardOn(.error(error))
|
||||
_parent.dispose()
|
||||
}
|
||||
case .completed:
|
||||
_parent.forwardOn(event)
|
||||
_parent.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
override func dispose() {
|
||||
super.dispose()
|
||||
_errorHandlerSubscription.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
class RetryWhenSequenceSink<S: Sequence, O: ObserverType, TriggerObservable: ObservableType, Error>
|
||||
: TailRecursiveSink<S, O> where S.Iterator.Element : ObservableType, S.Iterator.Element.E == O.E {
|
||||
typealias Element = O.E
|
||||
typealias Parent = RetryWhenSequence<S, TriggerObservable, Error>
|
||||
|
||||
let _lock = NSRecursiveLock()
|
||||
|
||||
fileprivate let _parent: Parent
|
||||
|
||||
fileprivate var _lastError: Swift.Error?
|
||||
fileprivate let _errorSubject = PublishSubject<Error>()
|
||||
fileprivate let _handler: Observable<TriggerObservable.E>
|
||||
fileprivate let _notifier = PublishSubject<TriggerObservable.E>()
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
_handler = parent._notificationHandler(_errorSubject).asObservable()
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
override func done() {
|
||||
if let lastError = _lastError {
|
||||
forwardOn(.error(lastError))
|
||||
_lastError = nil
|
||||
}
|
||||
else {
|
||||
forwardOn(.completed)
|
||||
}
|
||||
|
||||
dispose()
|
||||
}
|
||||
|
||||
override func extract(_ observable: Observable<E>) -> SequenceGenerator? {
|
||||
// It is important to always return `nil` here because there are sideffects in the `run` method
|
||||
// that are dependant on particular `retryWhen` operator so single operator stack can't be reused in this
|
||||
// case.
|
||||
return nil
|
||||
}
|
||||
|
||||
override func subscribeToNext(_ source: Observable<E>) -> Disposable {
|
||||
let iter = RetryWhenSequenceSinkIter(parent: self)
|
||||
iter.disposable = source.subscribe(iter)
|
||||
return iter
|
||||
}
|
||||
|
||||
override func run(_ sources: SequenceGenerator) -> Disposable {
|
||||
let triggerSubscription = _handler.subscribe(_notifier.asObserver())
|
||||
let superSubscription = super.run(sources)
|
||||
return Disposables.create(superSubscription, triggerSubscription)
|
||||
}
|
||||
}
|
||||
|
||||
class RetryWhenSequence<S: Sequence, TriggerObservable: ObservableType, Error> : Producer<S.Iterator.Element.E> where S.Iterator.Element : ObservableType {
|
||||
typealias Element = S.Iterator.Element.E
|
||||
|
||||
fileprivate let _sources: S
|
||||
fileprivate let _notificationHandler: (Observable<Error>) -> TriggerObservable
|
||||
|
||||
init(sources: S, notificationHandler: @escaping (Observable<Error>) -> TriggerObservable) {
|
||||
_sources = sources
|
||||
_notificationHandler = notificationHandler
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = RetryWhenSequenceSink<S, O, TriggerObservable, Error>(parent: self, observer: observer)
|
||||
sink.disposable = sink.run((self._sources.makeIterator(), nil))
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// Sample.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 5/1/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class SamplerSink<O: ObserverType, ElementType, SampleType>
|
||||
: ObserverType
|
||||
, LockOwnerType
|
||||
, SynchronizedOnType where O.E == ElementType {
|
||||
typealias E = SampleType
|
||||
|
||||
typealias Parent = SampleSequenceSink<O, SampleType>
|
||||
|
||||
fileprivate let _parent: Parent
|
||||
|
||||
var _lock: NSRecursiveLock {
|
||||
return _parent._lock
|
||||
}
|
||||
|
||||
init(parent: Parent) {
|
||||
_parent = parent
|
||||
}
|
||||
|
||||
func on(_ event: Event<E>) {
|
||||
synchronizedOn(event)
|
||||
}
|
||||
|
||||
func _synchronized_on(_ event: Event<E>) {
|
||||
switch event {
|
||||
case .next:
|
||||
if let element = _parent._element {
|
||||
if _parent._parent._onlyNew {
|
||||
_parent._element = nil
|
||||
}
|
||||
|
||||
_parent.forwardOn(.next(element))
|
||||
}
|
||||
|
||||
if _parent._atEnd {
|
||||
_parent.forwardOn(.completed)
|
||||
_parent.dispose()
|
||||
}
|
||||
case .error(let e):
|
||||
_parent.forwardOn(.error(e))
|
||||
_parent.dispose()
|
||||
case .completed:
|
||||
if let element = _parent._element {
|
||||
_parent._element = nil
|
||||
_parent.forwardOn(.next(element))
|
||||
}
|
||||
if _parent._atEnd {
|
||||
_parent.forwardOn(.completed)
|
||||
_parent.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SampleSequenceSink<O: ObserverType, SampleType>
|
||||
: Sink<O>
|
||||
, ObserverType
|
||||
, LockOwnerType
|
||||
, SynchronizedOnType {
|
||||
typealias Element = O.E
|
||||
typealias Parent = Sample<Element, SampleType>
|
||||
|
||||
fileprivate let _parent: Parent
|
||||
|
||||
let _lock = NSRecursiveLock()
|
||||
|
||||
// state
|
||||
fileprivate var _element = nil as Element?
|
||||
fileprivate var _atEnd = false
|
||||
|
||||
fileprivate let _sourceSubscription = SingleAssignmentDisposable()
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func run() -> Disposable {
|
||||
_sourceSubscription.disposable = _parent._source.subscribe(self)
|
||||
let samplerSubscription = _parent._sampler.subscribe(SamplerSink(parent: self))
|
||||
|
||||
return Disposables.create(_sourceSubscription, samplerSubscription)
|
||||
}
|
||||
|
||||
func on(_ event: Event<Element>) {
|
||||
synchronizedOn(event)
|
||||
}
|
||||
|
||||
func _synchronized_on(_ event: Event<Element>) {
|
||||
switch event {
|
||||
case .next(let element):
|
||||
_element = element
|
||||
case .error:
|
||||
forwardOn(event)
|
||||
dispose()
|
||||
case .completed:
|
||||
_atEnd = true
|
||||
_sourceSubscription.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Sample<Element, SampleType> : Producer<Element> {
|
||||
fileprivate let _source: Observable<Element>
|
||||
fileprivate let _sampler: Observable<SampleType>
|
||||
fileprivate let _onlyNew: Bool
|
||||
|
||||
init(source: Observable<Element>, sampler: Observable<SampleType>, onlyNew: Bool) {
|
||||
_source = source
|
||||
_sampler = sampler
|
||||
_onlyNew = onlyNew
|
||||
}
|
||||
|
||||
override func run<O: ObserverType>(_ observer: O) -> Disposable where O.E == Element {
|
||||
let sink = SampleSequenceSink(parent: self, observer: observer)
|
||||
sink.disposable = sink.run()
|
||||
return sink
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Scan.swift
|
||||
// RxSwift
|
||||
//
|
||||
// Created by Krunoslav Zaher on 6/14/15.
|
||||
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ScanSink<ElementType, Accumulate, O: ObserverType> : Sink<O>, ObserverType where O.E == Accumulate {
|
||||
typealias Parent = Scan<ElementType, Accumulate>
|
||||
typealias E = ElementType
|
||||
|
||||
fileprivate let _parent: Parent
|
||||
fileprivate var _accumulate: Accumulate
|
||||
|
||||
init(parent: Parent, observer: O) {
|
||||
_parent = parent
|
||||
_accumulate = parent._seed
|
||||
super.init(observer: observer)
|
||||
}
|
||||
|
||||
func on(_ event: Event<ElementType>) {
|
||||
switch event {
|
||||
case .next(let element):
|
||||
do {
|
||||
_accumulate = try _parent._accumulator(_accumulate, element)
|
||||
forwardOn(.next(_accumulate))
|
||||
}
|
||||
catch let error {
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
}
|
||||
case .error(let error):
|
||||
forwardOn(.error(error))
|
||||
dispose()
|
||||
case .completed:
|
||||
forwardOn(.completed)
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Scan<Element, Accumulate>: Producer<Accumulate> {
|
||||
typealias Accumulator = (Accumulate, Element) throws -> Accumulate
|
||||
|
||||
fileprivate let _source: Observable<Element>
|
||||
fileprivate let _seed: Accumulate
|
||||
fileprivate let _accumulator: Accumulator
|
||||
|
||||
init(source: Observable<Element>, seed: Accumulate, accumulator: @escaping Accumulator) {
|
||||
_source = source
|
||||
_seed = seed
|
||||
_accumulator = accumulator
|
||||
}
|
||||
|
||||
override func run<O : ObserverType>(_ observer: O) -> Disposable where O.E == Accumulate {
|
||||
let sink = ScanSink(parent: self, observer: observer)
|
||||
sink.disposable = _source.subscribe(sink)
|
||||
return sink
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user