123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832 |
- //
- // Concurrency.swift
- //
- // Copyright (c) 2021 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 compiler(>=5.6.0) && canImport(_Concurrency)
- import Foundation
- // MARK: - Request Event Streams
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- extension Request {
- /// Creates a `StreamOf<Progress>` for the instance's upload progress.
- ///
- /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- ///
- /// - Returns: The `StreamOf<Progress>`.
- public func uploadProgress(bufferingPolicy: StreamOf<Progress>.BufferingPolicy = .unbounded) -> StreamOf<Progress> {
- stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
- uploadProgress(queue: underlyingQueue) { progress in
- continuation.yield(progress)
- }
- }
- }
- /// Creates a `StreamOf<Progress>` for the instance's download progress.
- ///
- /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- ///
- /// - Returns: The `StreamOf<Progress>`.
- public func downloadProgress(bufferingPolicy: StreamOf<Progress>.BufferingPolicy = .unbounded) -> StreamOf<Progress> {
- stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
- downloadProgress(queue: underlyingQueue) { progress in
- continuation.yield(progress)
- }
- }
- }
- /// Creates a `StreamOf<URLRequest>` for the `URLRequest`s produced for the instance.
- ///
- /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- ///
- /// - Returns: The `StreamOf<URLRequest>`.
- public func urlRequests(bufferingPolicy: StreamOf<URLRequest>.BufferingPolicy = .unbounded) -> StreamOf<URLRequest> {
- stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
- onURLRequestCreation(on: underlyingQueue) { request in
- continuation.yield(request)
- }
- }
- }
- /// Creates a `StreamOf<URLSessionTask>` for the `URLSessionTask`s produced for the instance.
- ///
- /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- ///
- /// - Returns: The `StreamOf<URLSessionTask>`.
- public func urlSessionTasks(bufferingPolicy: StreamOf<URLSessionTask>.BufferingPolicy = .unbounded) -> StreamOf<URLSessionTask> {
- stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
- onURLSessionTaskCreation(on: underlyingQueue) { task in
- continuation.yield(task)
- }
- }
- }
- /// Creates a `StreamOf<String>` for the cURL descriptions produced for the instance.
- ///
- /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- ///
- /// - Returns: The `StreamOf<String>`.
- public func cURLDescriptions(bufferingPolicy: StreamOf<String>.BufferingPolicy = .unbounded) -> StreamOf<String> {
- stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
- cURLDescription(on: underlyingQueue) { description in
- continuation.yield(description)
- }
- }
- }
- fileprivate func stream<T>(of type: T.Type = T.self,
- bufferingPolicy: StreamOf<T>.BufferingPolicy = .unbounded,
- yielder: @escaping (StreamOf<T>.Continuation) -> Void) -> StreamOf<T> {
- StreamOf<T>(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
- yielder(continuation)
- // Must come after serializers run in order to catch retry progress.
- onFinish {
- continuation.finish()
- }
- }
- }
- }
- // MARK: - DataTask
- /// Value used to `await` a `DataResponse` and associated values.
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- public struct DataTask<Value> {
- /// `DataResponse` produced by the `DataRequest` and its response handler.
- public var response: DataResponse<Value, AFError> {
- get async {
- if shouldAutomaticallyCancel {
- return await withTaskCancellationHandler {
- await task.value
- } onCancel: {
- cancel()
- }
- } else {
- return await task.value
- }
- }
- }
- /// `Result` of any response serialization performed for the `response`.
- public var result: Result<Value, AFError> {
- get async { await response.result }
- }
- /// `Value` returned by the `response`.
- public var value: Value {
- get async throws {
- try await result.get()
- }
- }
- private let request: DataRequest
- private let task: Task<DataResponse<Value, AFError>, Never>
- private let shouldAutomaticallyCancel: Bool
- fileprivate init(request: DataRequest, task: Task<DataResponse<Value, AFError>, Never>, shouldAutomaticallyCancel: Bool) {
- self.request = request
- self.task = task
- self.shouldAutomaticallyCancel = shouldAutomaticallyCancel
- }
- /// Cancel the underlying `DataRequest` and `Task`.
- public func cancel() {
- task.cancel()
- }
- /// Resume the underlying `DataRequest`.
- public func resume() {
- request.resume()
- }
- /// Suspend the underlying `DataRequest`.
- public func suspend() {
- request.suspend()
- }
- }
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- extension DataRequest {
- /// Creates a `StreamOf<HTTPURLResponse>` for the instance's responses.
- ///
- /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- ///
- /// - Returns: The `StreamOf<HTTPURLResponse>`.
- public func httpResponses(bufferingPolicy: StreamOf<HTTPURLResponse>.BufferingPolicy = .unbounded) -> StreamOf<HTTPURLResponse> {
- stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
- onHTTPResponse(on: underlyingQueue) { response in
- continuation.yield(response)
- }
- }
- }
- #if swift(>=5.7)
- /// Sets an async closure returning a `Request.ResponseDisposition`, called whenever the `DataRequest` produces an
- /// `HTTPURLResponse`.
- ///
- /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
- /// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
- /// where responses after the first will contain the part headers.
- ///
- /// - Parameters:
- /// - handler: Async closure executed when a new `HTTPURLResponse` is received and returning a
- /// `ResponseDisposition` value. This value determines whether to continue the request or cancel it as
- /// if `cancel()` had been called on the instance. Note, this closure is called on an arbitrary thread,
- /// so any synchronous calls in it will execute in that context.
- ///
- /// - Returns: The instance.
- @_disfavoredOverload
- @discardableResult
- public func onHTTPResponse(
- perform handler: @escaping @Sendable (_ response: HTTPURLResponse) async -> ResponseDisposition
- ) -> Self {
- onHTTPResponse(on: underlyingQueue) { response, completionHandler in
- Task {
- let disposition = await handler(response)
- completionHandler(disposition)
- }
- }
- return self
- }
- /// Sets an async closure called whenever the `DataRequest` produces an `HTTPURLResponse`.
- ///
- /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
- /// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
- /// where responses after the first will contain the part headers.
- ///
- /// - Parameters:
- /// - handler: Async closure executed when a new `HTTPURLResponse` is received. Note, this closure is called on an
- /// arbitrary thread, so any synchronous calls in it will execute in that context.
- ///
- /// - Returns: The instance.
- @discardableResult
- public func onHTTPResponse(perform handler: @escaping @Sendable (_ response: HTTPURLResponse) async -> Void) -> Self {
- onHTTPResponse { response in
- await handler(response)
- return .allow
- }
- return self
- }
- #endif
- /// Creates a `DataTask` to `await` a `Data` value.
- ///
- /// - Parameters:
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DataTask`'s async
- /// properties. `true` by default.
- /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before completion.
- /// - emptyResponseCodes: HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
- /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
- ///
- /// - Returns: The `DataTask`.
- public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
- dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
- emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
- emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DataTask<Data> {
- serializingResponse(using: DataResponseSerializer(dataPreprocessor: dataPreprocessor,
- emptyResponseCodes: emptyResponseCodes,
- emptyRequestMethods: emptyRequestMethods),
- automaticallyCancelling: shouldAutomaticallyCancel)
- }
- /// Creates a `DataTask` to `await` serialization of a `Decodable` value.
- ///
- /// - Parameters:
- /// - type: `Decodable` type to decode from response data.
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DataTask`'s async
- /// properties. `true` by default.
- /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
- /// `PassthroughPreprocessor()` by default.
- /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
- /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
- /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
- ///
- /// - Returns: The `DataTask`.
- public func serializingDecodable<Value: Decodable>(_ type: Value.Type = Value.self,
- automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
- dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<Value>.defaultDataPreprocessor,
- decoder: DataDecoder = JSONDecoder(),
- emptyResponseCodes: Set<Int> = DecodableResponseSerializer<Value>.defaultEmptyResponseCodes,
- emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<Value>.defaultEmptyRequestMethods) -> DataTask<Value> {
- serializingResponse(using: DecodableResponseSerializer<Value>(dataPreprocessor: dataPreprocessor,
- decoder: decoder,
- emptyResponseCodes: emptyResponseCodes,
- emptyRequestMethods: emptyRequestMethods),
- automaticallyCancelling: shouldAutomaticallyCancel)
- }
- /// Creates a `DataTask` to `await` serialization of a `String` value.
- ///
- /// - Parameters:
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DataTask`'s async
- /// properties. `true` by default.
- /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
- /// `PassthroughPreprocessor()` by default.
- /// - encoding: `String.Encoding` to use during serialization. Defaults to `nil`, in which case
- /// the encoding will be determined from the server response, falling back to the
- /// default HTTP character set, `ISO-8859-1`.
- /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
- /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
- ///
- /// - Returns: The `DataTask`.
- public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
- dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
- encoding: String.Encoding? = nil,
- emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
- emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DataTask<String> {
- serializingResponse(using: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
- encoding: encoding,
- emptyResponseCodes: emptyResponseCodes,
- emptyRequestMethods: emptyRequestMethods),
- automaticallyCancelling: shouldAutomaticallyCancel)
- }
- /// Creates a `DataTask` to `await` serialization using the provided `ResponseSerializer` instance.
- ///
- /// - Parameters:
- /// - serializer: `ResponseSerializer` responsible for serializing the request, response, and data.
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DataTask`'s async
- /// properties. `true` by default.
- ///
- /// - Returns: The `DataTask`.
- public func serializingResponse<Serializer: ResponseSerializer>(using serializer: Serializer,
- automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
- -> DataTask<Serializer.SerializedObject> {
- dataTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
- response(queue: underlyingQueue,
- responseSerializer: serializer,
- completionHandler: $0)
- }
- }
- /// Creates a `DataTask` to `await` serialization using the provided `DataResponseSerializerProtocol` instance.
- ///
- /// - Parameters:
- /// - serializer: `DataResponseSerializerProtocol` responsible for serializing the request,
- /// response, and data.
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DataTask`'s async
- /// properties. `true` by default.
- ///
- /// - Returns: The `DataTask`.
- public func serializingResponse<Serializer: DataResponseSerializerProtocol>(using serializer: Serializer,
- automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
- -> DataTask<Serializer.SerializedObject> {
- dataTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
- response(queue: underlyingQueue,
- responseSerializer: serializer,
- completionHandler: $0)
- }
- }
- private func dataTask<Value>(automaticallyCancelling shouldAutomaticallyCancel: Bool,
- forResponse onResponse: @escaping (@escaping (DataResponse<Value, AFError>) -> Void) -> Void)
- -> DataTask<Value> {
- let task = Task {
- await withTaskCancellationHandler {
- await withCheckedContinuation { continuation in
- onResponse {
- continuation.resume(returning: $0)
- }
- }
- } onCancel: {
- self.cancel()
- }
- }
- return DataTask<Value>(request: self, task: task, shouldAutomaticallyCancel: shouldAutomaticallyCancel)
- }
- }
- // MARK: - DownloadTask
- /// Value used to `await` a `DownloadResponse` and associated values.
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- public struct DownloadTask<Value> {
- /// `DownloadResponse` produced by the `DownloadRequest` and its response handler.
- public var response: DownloadResponse<Value, AFError> {
- get async {
- if shouldAutomaticallyCancel {
- return await withTaskCancellationHandler {
- await task.value
- } onCancel: {
- cancel()
- }
- } else {
- return await task.value
- }
- }
- }
- /// `Result` of any response serialization performed for the `response`.
- public var result: Result<Value, AFError> {
- get async { await response.result }
- }
- /// `Value` returned by the `response`.
- public var value: Value {
- get async throws {
- try await result.get()
- }
- }
- private let task: Task<AFDownloadResponse<Value>, Never>
- private let request: DownloadRequest
- private let shouldAutomaticallyCancel: Bool
- fileprivate init(request: DownloadRequest, task: Task<AFDownloadResponse<Value>, Never>, shouldAutomaticallyCancel: Bool) {
- self.request = request
- self.task = task
- self.shouldAutomaticallyCancel = shouldAutomaticallyCancel
- }
- /// Cancel the underlying `DownloadRequest` and `Task`.
- public func cancel() {
- task.cancel()
- }
- /// Resume the underlying `DownloadRequest`.
- public func resume() {
- request.resume()
- }
- /// Suspend the underlying `DownloadRequest`.
- public func suspend() {
- request.suspend()
- }
- }
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- extension DownloadRequest {
- /// Creates a `DownloadTask` to `await` a `Data` value.
- ///
- /// - Parameters:
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
- /// properties. `true` by default.
- /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before completion.
- /// - emptyResponseCodes: HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
- /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
- ///
- /// - Returns: The `DownloadTask`.
- public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
- dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
- emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
- emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask<Data> {
- serializingDownload(using: DataResponseSerializer(dataPreprocessor: dataPreprocessor,
- emptyResponseCodes: emptyResponseCodes,
- emptyRequestMethods: emptyRequestMethods),
- automaticallyCancelling: shouldAutomaticallyCancel)
- }
- /// Creates a `DownloadTask` to `await` serialization of a `Decodable` value.
- ///
- /// - Note: This serializer reads the entire response into memory before parsing.
- ///
- /// - Parameters:
- /// - type: `Decodable` type to decode from response data.
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
- /// properties. `true` by default.
- /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
- /// `PassthroughPreprocessor()` by default.
- /// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
- /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
- /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
- ///
- /// - Returns: The `DownloadTask`.
- public func serializingDecodable<Value: Decodable>(_ type: Value.Type = Value.self,
- automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
- dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<Value>.defaultDataPreprocessor,
- decoder: DataDecoder = JSONDecoder(),
- emptyResponseCodes: Set<Int> = DecodableResponseSerializer<Value>.defaultEmptyResponseCodes,
- emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<Value>.defaultEmptyRequestMethods) -> DownloadTask<Value> {
- serializingDownload(using: DecodableResponseSerializer<Value>(dataPreprocessor: dataPreprocessor,
- decoder: decoder,
- emptyResponseCodes: emptyResponseCodes,
- emptyRequestMethods: emptyRequestMethods),
- automaticallyCancelling: shouldAutomaticallyCancel)
- }
- /// Creates a `DownloadTask` to `await` serialization of the downloaded file's `URL` on disk.
- ///
- /// - Parameters:
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
- /// properties. `true` by default.
- ///
- /// - Returns: The `DownloadTask`.
- public func serializingDownloadedFileURL(automaticallyCancelling shouldAutomaticallyCancel: Bool = true) -> DownloadTask<URL> {
- serializingDownload(using: URLResponseSerializer(),
- automaticallyCancelling: shouldAutomaticallyCancel)
- }
- /// Creates a `DownloadTask` to `await` serialization of a `String` value.
- ///
- /// - Parameters:
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
- /// properties. `true` by default.
- /// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the
- /// serializer. `PassthroughPreprocessor()` by default.
- /// - encoding: `String.Encoding` to use during serialization. Defaults to `nil`, in which case
- /// the encoding will be determined from the server response, falling back to the
- /// default HTTP character set, `ISO-8859-1`.
- /// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
- /// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
- ///
- /// - Returns: The `DownloadTask`.
- public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
- dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
- encoding: String.Encoding? = nil,
- emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
- emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask<String> {
- serializingDownload(using: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
- encoding: encoding,
- emptyResponseCodes: emptyResponseCodes,
- emptyRequestMethods: emptyRequestMethods),
- automaticallyCancelling: shouldAutomaticallyCancel)
- }
- /// Creates a `DownloadTask` to `await` serialization using the provided `ResponseSerializer` instance.
- ///
- /// - Parameters:
- /// - serializer: `ResponseSerializer` responsible for serializing the request, response, and data.
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
- /// properties. `true` by default.
- ///
- /// - Returns: The `DownloadTask`.
- public func serializingDownload<Serializer: ResponseSerializer>(using serializer: Serializer,
- automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
- -> DownloadTask<Serializer.SerializedObject> {
- downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
- response(queue: underlyingQueue,
- responseSerializer: serializer,
- completionHandler: $0)
- }
- }
- /// Creates a `DownloadTask` to `await` serialization using the provided `DownloadResponseSerializerProtocol`
- /// instance.
- ///
- /// - Parameters:
- /// - serializer: `DownloadResponseSerializerProtocol` responsible for serializing the request,
- /// response, and data.
- /// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
- /// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
- /// properties. `true` by default.
- ///
- /// - Returns: The `DownloadTask`.
- public func serializingDownload<Serializer: DownloadResponseSerializerProtocol>(using serializer: Serializer,
- automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
- -> DownloadTask<Serializer.SerializedObject> {
- downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
- response(queue: underlyingQueue,
- responseSerializer: serializer,
- completionHandler: $0)
- }
- }
- private func downloadTask<Value>(automaticallyCancelling shouldAutomaticallyCancel: Bool,
- forResponse onResponse: @escaping (@escaping (DownloadResponse<Value, AFError>) -> Void) -> Void)
- -> DownloadTask<Value> {
- let task = Task {
- await withTaskCancellationHandler {
- await withCheckedContinuation { continuation in
- onResponse {
- continuation.resume(returning: $0)
- }
- }
- } onCancel: {
- self.cancel()
- }
- }
- return DownloadTask<Value>(request: self, task: task, shouldAutomaticallyCancel: shouldAutomaticallyCancel)
- }
- }
- // MARK: - DataStreamTask
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- public struct DataStreamTask {
- // Type of created streams.
- public typealias Stream<Success, Failure: Error> = StreamOf<DataStreamRequest.Stream<Success, Failure>>
- private let request: DataStreamRequest
- fileprivate init(request: DataStreamRequest) {
- self.request = request
- }
- /// Creates a `Stream` of `Data` values from the underlying `DataStreamRequest`.
- ///
- /// - Parameters:
- /// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
- /// which observation of the stream stops. `true` by default.
- /// - bufferingPolicy: ` BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- ///
- /// - Returns: The `Stream`.
- public func streamingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, bufferingPolicy: Stream<Data, Never>.BufferingPolicy = .unbounded) -> Stream<Data, Never> {
- createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
- request.responseStream(on: .streamCompletionQueue(forRequestID: request.id), stream: onStream)
- }
- }
- /// Creates a `Stream` of `UTF-8` `String`s from the underlying `DataStreamRequest`.
- ///
- /// - Parameters:
- /// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
- /// which observation of the stream stops. `true` by default.
- /// - bufferingPolicy: ` BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- /// - Returns:
- public func streamingStrings(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, bufferingPolicy: Stream<String, Never>.BufferingPolicy = .unbounded) -> Stream<String, Never> {
- createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
- request.responseStreamString(on: .streamCompletionQueue(forRequestID: request.id), stream: onStream)
- }
- }
- /// Creates a `Stream` of `Decodable` values from the underlying `DataStreamRequest`.
- ///
- /// - Parameters:
- /// - type: `Decodable` type to be serialized from stream payloads.
- /// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
- /// which observation of the stream stops. `true` by default.
- /// - bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- ///
- /// - Returns: The `Stream`.
- public func streamingDecodables<T>(_ type: T.Type = T.self,
- automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
- bufferingPolicy: Stream<T, AFError>.BufferingPolicy = .unbounded)
- -> Stream<T, AFError> where T: Decodable {
- streamingResponses(serializedUsing: DecodableStreamSerializer<T>(),
- automaticallyCancelling: shouldAutomaticallyCancel,
- bufferingPolicy: bufferingPolicy)
- }
- /// Creates a `Stream` of values using the provided `DataStreamSerializer` from the underlying `DataStreamRequest`.
- ///
- /// - Parameters:
- /// - serializer: `DataStreamSerializer` to use to serialize incoming `Data`.
- /// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
- /// which observation of the stream stops. `true` by default.
- /// - bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- ///
- /// - Returns: The `Stream`.
- public func streamingResponses<Serializer: DataStreamSerializer>(serializedUsing serializer: Serializer,
- automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
- bufferingPolicy: Stream<Serializer.SerializedObject, AFError>.BufferingPolicy = .unbounded)
- -> Stream<Serializer.SerializedObject, AFError> {
- createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
- request.responseStream(using: serializer,
- on: .streamCompletionQueue(forRequestID: request.id),
- stream: onStream)
- }
- }
- private func createStream<Success, Failure: Error>(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
- bufferingPolicy: Stream<Success, Failure>.BufferingPolicy = .unbounded,
- forResponse onResponse: @escaping (@escaping (DataStreamRequest.Stream<Success, Failure>) -> Void) -> Void)
- -> Stream<Success, Failure> {
- StreamOf(bufferingPolicy: bufferingPolicy) {
- guard shouldAutomaticallyCancel,
- request.isInitialized || request.isResumed || request.isSuspended else { return }
- cancel()
- } builder: { continuation in
- onResponse { stream in
- continuation.yield(stream)
- if case .complete = stream.event {
- continuation.finish()
- }
- }
- }
- }
- /// Cancel the underlying `DataStreamRequest`.
- public func cancel() {
- request.cancel()
- }
- /// Resume the underlying `DataStreamRequest`.
- public func resume() {
- request.resume()
- }
- /// Suspend the underlying `DataStreamRequest`.
- public func suspend() {
- request.suspend()
- }
- }
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- extension DataStreamRequest {
- /// Creates a `StreamOf<HTTPURLResponse>` for the instance's responses.
- ///
- /// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
- ///
- /// - Returns: The `StreamOf<HTTPURLResponse>`.
- public func httpResponses(bufferingPolicy: StreamOf<HTTPURLResponse>.BufferingPolicy = .unbounded) -> StreamOf<HTTPURLResponse> {
- stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
- onHTTPResponse(on: underlyingQueue) { response in
- continuation.yield(response)
- }
- }
- }
- #if swift(>=5.7)
- /// Sets an async closure returning a `Request.ResponseDisposition`, called whenever the `DataStreamRequest`
- /// produces an `HTTPURLResponse`.
- ///
- /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
- /// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
- /// where responses after the first will contain the part headers.
- ///
- /// - Parameters:
- /// - handler: Async closure executed when a new `HTTPURLResponse` is received and returning a
- /// `ResponseDisposition` value. This value determines whether to continue the request or cancel it as
- /// if `cancel()` had been called on the instance. Note, this closure is called on an arbitrary thread,
- /// so any synchronous calls in it will execute in that context.
- ///
- /// - Returns: The instance.
- @_disfavoredOverload
- @discardableResult
- public func onHTTPResponse(perform handler: @escaping @Sendable (HTTPURLResponse) async -> ResponseDisposition) -> Self {
- onHTTPResponse(on: underlyingQueue) { response, completionHandler in
- Task {
- let disposition = await handler(response)
- completionHandler(disposition)
- }
- }
- return self
- }
- /// Sets an async closure called whenever the `DataStreamRequest` produces an `HTTPURLResponse`.
- ///
- /// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
- /// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
- /// where responses after the first will contain the part headers.
- ///
- /// - Parameters:
- /// - handler: Async closure executed when a new `HTTPURLResponse` is received. Note, this closure is called on an
- /// arbitrary thread, so any synchronous calls in it will execute in that context.
- ///
- /// - Returns: The instance.
- @discardableResult
- public func onHTTPResponse(perform handler: @escaping @Sendable (HTTPURLResponse) async -> Void) -> Self {
- onHTTPResponse { response in
- await handler(response)
- return .allow
- }
- return self
- }
- #endif
- /// Creates a `DataStreamTask` used to `await` streams of serialized values.
- ///
- /// - Returns: The `DataStreamTask`.
- public func streamTask() -> DataStreamTask {
- DataStreamTask(request: self)
- }
- }
- extension DispatchQueue {
- fileprivate static let singleEventQueue = DispatchQueue(label: "org.alamofire.concurrencySingleEventQueue",
- attributes: .concurrent)
- fileprivate static func streamCompletionQueue(forRequestID id: UUID) -> DispatchQueue {
- DispatchQueue(label: "org.alamofire.concurrencyStreamCompletionQueue-\(id)", target: .singleEventQueue)
- }
- }
- /// An asynchronous sequence generated from an underlying `AsyncStream`. Only produced by Alamofire.
- @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
- public struct StreamOf<Element>: AsyncSequence {
- public typealias AsyncIterator = Iterator
- public typealias BufferingPolicy = AsyncStream<Element>.Continuation.BufferingPolicy
- fileprivate typealias Continuation = AsyncStream<Element>.Continuation
- private let bufferingPolicy: BufferingPolicy
- private let onTermination: (() -> Void)?
- private let builder: (Continuation) -> Void
- fileprivate init(bufferingPolicy: BufferingPolicy = .unbounded,
- onTermination: (() -> Void)? = nil,
- builder: @escaping (Continuation) -> Void) {
- self.bufferingPolicy = bufferingPolicy
- self.onTermination = onTermination
- self.builder = builder
- }
- public func makeAsyncIterator() -> Iterator {
- var continuation: AsyncStream<Element>.Continuation?
- let stream = AsyncStream<Element>(bufferingPolicy: bufferingPolicy) { innerContinuation in
- continuation = innerContinuation
- builder(innerContinuation)
- }
- return Iterator(iterator: stream.makeAsyncIterator()) {
- continuation?.finish()
- onTermination?()
- }
- }
- public struct Iterator: AsyncIteratorProtocol {
- private final class Token {
- private let onDeinit: () -> Void
- init(onDeinit: @escaping () -> Void) {
- self.onDeinit = onDeinit
- }
- deinit {
- onDeinit()
- }
- }
- private var iterator: AsyncStream<Element>.AsyncIterator
- private let token: Token
- init(iterator: AsyncStream<Element>.AsyncIterator, onCancellation: @escaping () -> Void) {
- self.iterator = iterator
- token = Token(onDeinit: onCancellation)
- }
- public mutating func next() async -> Element? {
- await iterator.next()
- }
- }
- }
- #endif
|