1 |
efrain |
1 |
//
|
|
|
2 |
// Concurrency.swift
|
|
|
3 |
//
|
|
|
4 |
// Copyright (c) 2021 Alamofire Software Foundation (http://alamofire.org/)
|
|
|
5 |
//
|
|
|
6 |
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
7 |
// of this software and associated documentation files (the "Software"), to deal
|
|
|
8 |
// in the Software without restriction, including without limitation the rights
|
|
|
9 |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
10 |
// copies of the Software, and to permit persons to whom the Software is
|
|
|
11 |
// furnished to do so, subject to the following conditions:
|
|
|
12 |
//
|
|
|
13 |
// The above copyright notice and this permission notice shall be included in
|
|
|
14 |
// all copies or substantial portions of the Software.
|
|
|
15 |
//
|
|
|
16 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
17 |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
18 |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
19 |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
20 |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
21 |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
22 |
// THE SOFTWARE.
|
|
|
23 |
//
|
|
|
24 |
|
|
|
25 |
#if compiler(>=5.6.0) && canImport(_Concurrency)
|
|
|
26 |
|
|
|
27 |
import Foundation
|
|
|
28 |
|
|
|
29 |
// MARK: - Request Event Streams
|
|
|
30 |
|
|
|
31 |
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
|
32 |
extension Request {
|
|
|
33 |
/// Creates a `StreamOf<Progress>` for the instance's upload progress.
|
|
|
34 |
///
|
|
|
35 |
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
|
36 |
///
|
|
|
37 |
/// - Returns: The `StreamOf<Progress>`.
|
|
|
38 |
public func uploadProgress(bufferingPolicy: StreamOf<Progress>.BufferingPolicy = .unbounded) -> StreamOf<Progress> {
|
|
|
39 |
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
|
40 |
uploadProgress(queue: .singleEventQueue) { progress in
|
|
|
41 |
continuation.yield(progress)
|
|
|
42 |
}
|
|
|
43 |
}
|
|
|
44 |
}
|
|
|
45 |
|
|
|
46 |
/// Creates a `StreamOf<Progress>` for the instance's download progress.
|
|
|
47 |
///
|
|
|
48 |
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
|
49 |
///
|
|
|
50 |
/// - Returns: The `StreamOf<Progress>`.
|
|
|
51 |
public func downloadProgress(bufferingPolicy: StreamOf<Progress>.BufferingPolicy = .unbounded) -> StreamOf<Progress> {
|
|
|
52 |
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
|
53 |
downloadProgress(queue: .singleEventQueue) { progress in
|
|
|
54 |
continuation.yield(progress)
|
|
|
55 |
}
|
|
|
56 |
}
|
|
|
57 |
}
|
|
|
58 |
|
|
|
59 |
/// Creates a `StreamOf<URLRequest>` for the `URLRequest`s produced for the instance.
|
|
|
60 |
///
|
|
|
61 |
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
|
62 |
///
|
|
|
63 |
/// - Returns: The `StreamOf<URLRequest>`.
|
|
|
64 |
public func urlRequests(bufferingPolicy: StreamOf<URLRequest>.BufferingPolicy = .unbounded) -> StreamOf<URLRequest> {
|
|
|
65 |
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
|
66 |
onURLRequestCreation(on: .singleEventQueue) { request in
|
|
|
67 |
continuation.yield(request)
|
|
|
68 |
}
|
|
|
69 |
}
|
|
|
70 |
}
|
|
|
71 |
|
|
|
72 |
/// Creates a `StreamOf<URLSessionTask>` for the `URLSessionTask`s produced for the instance.
|
|
|
73 |
///
|
|
|
74 |
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
|
75 |
///
|
|
|
76 |
/// - Returns: The `StreamOf<URLSessionTask>`.
|
|
|
77 |
public func urlSessionTasks(bufferingPolicy: StreamOf<URLSessionTask>.BufferingPolicy = .unbounded) -> StreamOf<URLSessionTask> {
|
|
|
78 |
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
|
79 |
onURLSessionTaskCreation(on: .singleEventQueue) { task in
|
|
|
80 |
continuation.yield(task)
|
|
|
81 |
}
|
|
|
82 |
}
|
|
|
83 |
}
|
|
|
84 |
|
|
|
85 |
/// Creates a `StreamOf<String>` for the cURL descriptions produced for the instance.
|
|
|
86 |
///
|
|
|
87 |
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
|
88 |
///
|
|
|
89 |
/// - Returns: The `StreamOf<String>`.
|
|
|
90 |
public func cURLDescriptions(bufferingPolicy: StreamOf<String>.BufferingPolicy = .unbounded) -> StreamOf<String> {
|
|
|
91 |
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
|
92 |
cURLDescription(on: .singleEventQueue) { description in
|
|
|
93 |
continuation.yield(description)
|
|
|
94 |
}
|
|
|
95 |
}
|
|
|
96 |
}
|
|
|
97 |
|
|
|
98 |
private func stream<T>(of type: T.Type = T.self,
|
|
|
99 |
bufferingPolicy: StreamOf<T>.BufferingPolicy = .unbounded,
|
|
|
100 |
yielder: @escaping (StreamOf<T>.Continuation) -> Void) -> StreamOf<T> {
|
|
|
101 |
StreamOf<T>(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
|
102 |
yielder(continuation)
|
|
|
103 |
// Must come after serializers run in order to catch retry progress.
|
|
|
104 |
onFinish {
|
|
|
105 |
continuation.finish()
|
|
|
106 |
}
|
|
|
107 |
}
|
|
|
108 |
}
|
|
|
109 |
}
|
|
|
110 |
|
|
|
111 |
// MARK: - DataTask
|
|
|
112 |
|
|
|
113 |
/// Value used to `await` a `DataResponse` and associated values.
|
|
|
114 |
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
|
115 |
public struct DataTask<Value> {
|
|
|
116 |
/// `DataResponse` produced by the `DataRequest` and its response handler.
|
|
|
117 |
public var response: DataResponse<Value, AFError> {
|
|
|
118 |
get async {
|
|
|
119 |
if shouldAutomaticallyCancel {
|
|
|
120 |
return await withTaskCancellationHandler {
|
|
|
121 |
self.cancel()
|
|
|
122 |
} operation: {
|
|
|
123 |
await task.value
|
|
|
124 |
}
|
|
|
125 |
} else {
|
|
|
126 |
return await task.value
|
|
|
127 |
}
|
|
|
128 |
}
|
|
|
129 |
}
|
|
|
130 |
|
|
|
131 |
/// `Result` of any response serialization performed for the `response`.
|
|
|
132 |
public var result: Result<Value, AFError> {
|
|
|
133 |
get async { await response.result }
|
|
|
134 |
}
|
|
|
135 |
|
|
|
136 |
/// `Value` returned by the `response`.
|
|
|
137 |
public var value: Value {
|
|
|
138 |
get async throws {
|
|
|
139 |
try await result.get()
|
|
|
140 |
}
|
|
|
141 |
}
|
|
|
142 |
|
|
|
143 |
private let request: DataRequest
|
|
|
144 |
private let task: Task<DataResponse<Value, AFError>, Never>
|
|
|
145 |
private let shouldAutomaticallyCancel: Bool
|
|
|
146 |
|
|
|
147 |
fileprivate init(request: DataRequest, task: Task<DataResponse<Value, AFError>, Never>, shouldAutomaticallyCancel: Bool) {
|
|
|
148 |
self.request = request
|
|
|
149 |
self.task = task
|
|
|
150 |
self.shouldAutomaticallyCancel = shouldAutomaticallyCancel
|
|
|
151 |
}
|
|
|
152 |
|
|
|
153 |
/// Cancel the underlying `DataRequest` and `Task`.
|
|
|
154 |
public func cancel() {
|
|
|
155 |
task.cancel()
|
|
|
156 |
}
|
|
|
157 |
|
|
|
158 |
/// Resume the underlying `DataRequest`.
|
|
|
159 |
public func resume() {
|
|
|
160 |
request.resume()
|
|
|
161 |
}
|
|
|
162 |
|
|
|
163 |
/// Suspend the underlying `DataRequest`.
|
|
|
164 |
public func suspend() {
|
|
|
165 |
request.suspend()
|
|
|
166 |
}
|
|
|
167 |
}
|
|
|
168 |
|
|
|
169 |
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
|
170 |
extension DataRequest {
|
|
|
171 |
/// Creates a `DataTask` to `await` a `Data` value.
|
|
|
172 |
///
|
|
|
173 |
/// - Parameters:
|
|
|
174 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
175 |
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
|
|
|
176 |
/// properties. `false` by default.
|
|
|
177 |
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before completion.
|
|
|
178 |
/// - emptyResponseCodes: HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
|
|
|
179 |
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
|
180 |
///
|
|
|
181 |
/// - Returns: The `DataTask`.
|
|
|
182 |
public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
|
|
|
183 |
dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
|
|
|
184 |
emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
|
|
|
185 |
emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DataTask<Data> {
|
|
|
186 |
serializingResponse(using: DataResponseSerializer(dataPreprocessor: dataPreprocessor,
|
|
|
187 |
emptyResponseCodes: emptyResponseCodes,
|
|
|
188 |
emptyRequestMethods: emptyRequestMethods),
|
|
|
189 |
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
|
190 |
}
|
|
|
191 |
|
|
|
192 |
/// Creates a `DataTask` to `await` serialization of a `Decodable` value.
|
|
|
193 |
///
|
|
|
194 |
/// - Parameters:
|
|
|
195 |
/// - type: `Decodable` type to decode from response data.
|
|
|
196 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
197 |
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
|
|
|
198 |
/// properties. `false` by default.
|
|
|
199 |
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
|
|
|
200 |
/// `PassthroughPreprocessor()` by default.
|
|
|
201 |
/// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
|
|
|
202 |
/// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
|
|
|
203 |
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
|
204 |
///
|
|
|
205 |
/// - Returns: The `DataTask`.
|
|
|
206 |
public func serializingDecodable<Value: Decodable>(_ type: Value.Type = Value.self,
|
|
|
207 |
automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
|
|
|
208 |
dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<Value>.defaultDataPreprocessor,
|
|
|
209 |
decoder: DataDecoder = JSONDecoder(),
|
|
|
210 |
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<Value>.defaultEmptyResponseCodes,
|
|
|
211 |
emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<Value>.defaultEmptyRequestMethods) -> DataTask<Value> {
|
|
|
212 |
serializingResponse(using: DecodableResponseSerializer<Value>(dataPreprocessor: dataPreprocessor,
|
|
|
213 |
decoder: decoder,
|
|
|
214 |
emptyResponseCodes: emptyResponseCodes,
|
|
|
215 |
emptyRequestMethods: emptyRequestMethods),
|
|
|
216 |
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
|
217 |
}
|
|
|
218 |
|
|
|
219 |
/// Creates a `DataTask` to `await` serialization of a `String` value.
|
|
|
220 |
///
|
|
|
221 |
/// - Parameters:
|
|
|
222 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
223 |
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
|
|
|
224 |
/// properties. `false` by default.
|
|
|
225 |
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
|
|
|
226 |
/// `PassthroughPreprocessor()` by default.
|
|
|
227 |
/// - encoding: `String.Encoding` to use during serialization. Defaults to `nil`, in which case
|
|
|
228 |
/// the encoding will be determined from the server response, falling back to the
|
|
|
229 |
/// default HTTP character set, `ISO-8859-1`.
|
|
|
230 |
/// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
|
|
|
231 |
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
|
232 |
///
|
|
|
233 |
/// - Returns: The `DataTask`.
|
|
|
234 |
public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
|
|
|
235 |
dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
|
|
|
236 |
encoding: String.Encoding? = nil,
|
|
|
237 |
emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
|
|
|
238 |
emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DataTask<String> {
|
|
|
239 |
serializingResponse(using: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
|
|
|
240 |
encoding: encoding,
|
|
|
241 |
emptyResponseCodes: emptyResponseCodes,
|
|
|
242 |
emptyRequestMethods: emptyRequestMethods),
|
|
|
243 |
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
|
244 |
}
|
|
|
245 |
|
|
|
246 |
/// Creates a `DataTask` to `await` serialization using the provided `ResponseSerializer` instance.
|
|
|
247 |
///
|
|
|
248 |
/// - Parameters:
|
|
|
249 |
/// - serializer: `ResponseSerializer` responsible for serializing the request, response, and data.
|
|
|
250 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
251 |
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
|
|
|
252 |
/// properties. `false` by default.
|
|
|
253 |
///
|
|
|
254 |
/// - Returns: The `DataTask`.
|
|
|
255 |
public func serializingResponse<Serializer: ResponseSerializer>(using serializer: Serializer,
|
|
|
256 |
automaticallyCancelling shouldAutomaticallyCancel: Bool = false)
|
|
|
257 |
-> DataTask<Serializer.SerializedObject> {
|
|
|
258 |
dataTask(automaticallyCancelling: shouldAutomaticallyCancel) {
|
|
|
259 |
self.response(queue: .singleEventQueue,
|
|
|
260 |
responseSerializer: serializer,
|
|
|
261 |
completionHandler: $0)
|
|
|
262 |
}
|
|
|
263 |
}
|
|
|
264 |
|
|
|
265 |
/// Creates a `DataTask` to `await` serialization using the provided `DataResponseSerializerProtocol` instance.
|
|
|
266 |
///
|
|
|
267 |
/// - Parameters:
|
|
|
268 |
/// - serializer: `DataResponseSerializerProtocol` responsible for serializing the request,
|
|
|
269 |
/// response, and data.
|
|
|
270 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
271 |
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
|
|
|
272 |
/// properties. `false` by default.
|
|
|
273 |
///
|
|
|
274 |
/// - Returns: The `DataTask`.
|
|
|
275 |
public func serializingResponse<Serializer: DataResponseSerializerProtocol>(using serializer: Serializer,
|
|
|
276 |
automaticallyCancelling shouldAutomaticallyCancel: Bool = false)
|
|
|
277 |
-> DataTask<Serializer.SerializedObject> {
|
|
|
278 |
dataTask(automaticallyCancelling: shouldAutomaticallyCancel) {
|
|
|
279 |
self.response(queue: .singleEventQueue,
|
|
|
280 |
responseSerializer: serializer,
|
|
|
281 |
completionHandler: $0)
|
|
|
282 |
}
|
|
|
283 |
}
|
|
|
284 |
|
|
|
285 |
private func dataTask<Value>(automaticallyCancelling shouldAutomaticallyCancel: Bool,
|
|
|
286 |
forResponse onResponse: @escaping (@escaping (DataResponse<Value, AFError>) -> Void) -> Void)
|
|
|
287 |
-> DataTask<Value> {
|
|
|
288 |
let task = Task {
|
|
|
289 |
await withTaskCancellationHandler {
|
|
|
290 |
self.cancel()
|
|
|
291 |
} operation: {
|
|
|
292 |
await withCheckedContinuation { continuation in
|
|
|
293 |
onResponse {
|
|
|
294 |
continuation.resume(returning: $0)
|
|
|
295 |
}
|
|
|
296 |
}
|
|
|
297 |
}
|
|
|
298 |
}
|
|
|
299 |
|
|
|
300 |
return DataTask<Value>(request: self, task: task, shouldAutomaticallyCancel: shouldAutomaticallyCancel)
|
|
|
301 |
}
|
|
|
302 |
}
|
|
|
303 |
|
|
|
304 |
// MARK: - DownloadTask
|
|
|
305 |
|
|
|
306 |
/// Value used to `await` a `DownloadResponse` and associated values.
|
|
|
307 |
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
|
308 |
public struct DownloadTask<Value> {
|
|
|
309 |
/// `DownloadResponse` produced by the `DownloadRequest` and its response handler.
|
|
|
310 |
public var response: DownloadResponse<Value, AFError> {
|
|
|
311 |
get async {
|
|
|
312 |
if shouldAutomaticallyCancel {
|
|
|
313 |
return await withTaskCancellationHandler {
|
|
|
314 |
self.cancel()
|
|
|
315 |
} operation: {
|
|
|
316 |
await task.value
|
|
|
317 |
}
|
|
|
318 |
} else {
|
|
|
319 |
return await task.value
|
|
|
320 |
}
|
|
|
321 |
}
|
|
|
322 |
}
|
|
|
323 |
|
|
|
324 |
/// `Result` of any response serialization performed for the `response`.
|
|
|
325 |
public var result: Result<Value, AFError> {
|
|
|
326 |
get async { await response.result }
|
|
|
327 |
}
|
|
|
328 |
|
|
|
329 |
/// `Value` returned by the `response`.
|
|
|
330 |
public var value: Value {
|
|
|
331 |
get async throws {
|
|
|
332 |
try await result.get()
|
|
|
333 |
}
|
|
|
334 |
}
|
|
|
335 |
|
|
|
336 |
private let task: Task<AFDownloadResponse<Value>, Never>
|
|
|
337 |
private let request: DownloadRequest
|
|
|
338 |
private let shouldAutomaticallyCancel: Bool
|
|
|
339 |
|
|
|
340 |
fileprivate init(request: DownloadRequest, task: Task<AFDownloadResponse<Value>, Never>, shouldAutomaticallyCancel: Bool) {
|
|
|
341 |
self.request = request
|
|
|
342 |
self.task = task
|
|
|
343 |
self.shouldAutomaticallyCancel = shouldAutomaticallyCancel
|
|
|
344 |
}
|
|
|
345 |
|
|
|
346 |
/// Cancel the underlying `DownloadRequest` and `Task`.
|
|
|
347 |
public func cancel() {
|
|
|
348 |
task.cancel()
|
|
|
349 |
}
|
|
|
350 |
|
|
|
351 |
/// Resume the underlying `DownloadRequest`.
|
|
|
352 |
public func resume() {
|
|
|
353 |
request.resume()
|
|
|
354 |
}
|
|
|
355 |
|
|
|
356 |
/// Suspend the underlying `DownloadRequest`.
|
|
|
357 |
public func suspend() {
|
|
|
358 |
request.suspend()
|
|
|
359 |
}
|
|
|
360 |
}
|
|
|
361 |
|
|
|
362 |
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
|
363 |
extension DownloadRequest {
|
|
|
364 |
/// Creates a `DownloadTask` to `await` a `Data` value.
|
|
|
365 |
///
|
|
|
366 |
/// - Parameters:
|
|
|
367 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
368 |
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
|
369 |
/// properties. `false` by default.
|
|
|
370 |
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before completion.
|
|
|
371 |
/// - emptyResponseCodes: HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
|
|
|
372 |
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
|
373 |
///
|
|
|
374 |
/// - Returns: The `DownloadTask`.
|
|
|
375 |
public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
|
|
|
376 |
dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
|
|
|
377 |
emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
|
|
|
378 |
emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask<Data> {
|
|
|
379 |
serializingDownload(using: DataResponseSerializer(dataPreprocessor: dataPreprocessor,
|
|
|
380 |
emptyResponseCodes: emptyResponseCodes,
|
|
|
381 |
emptyRequestMethods: emptyRequestMethods),
|
|
|
382 |
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
|
383 |
}
|
|
|
384 |
|
|
|
385 |
/// Creates a `DownloadTask` to `await` serialization of a `Decodable` value.
|
|
|
386 |
///
|
|
|
387 |
/// - Note: This serializer reads the entire response into memory before parsing.
|
|
|
388 |
///
|
|
|
389 |
/// - Parameters:
|
|
|
390 |
/// - type: `Decodable` type to decode from response data.
|
|
|
391 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
392 |
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
|
393 |
/// properties. `false` by default.
|
|
|
394 |
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
|
|
|
395 |
/// `PassthroughPreprocessor()` by default.
|
|
|
396 |
/// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
|
|
|
397 |
/// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
|
|
|
398 |
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
|
399 |
///
|
|
|
400 |
/// - Returns: The `DownloadTask`.
|
|
|
401 |
public func serializingDecodable<Value: Decodable>(_ type: Value.Type = Value.self,
|
|
|
402 |
automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
|
|
|
403 |
dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<Value>.defaultDataPreprocessor,
|
|
|
404 |
decoder: DataDecoder = JSONDecoder(),
|
|
|
405 |
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<Value>.defaultEmptyResponseCodes,
|
|
|
406 |
emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<Value>.defaultEmptyRequestMethods) -> DownloadTask<Value> {
|
|
|
407 |
serializingDownload(using: DecodableResponseSerializer<Value>(dataPreprocessor: dataPreprocessor,
|
|
|
408 |
decoder: decoder,
|
|
|
409 |
emptyResponseCodes: emptyResponseCodes,
|
|
|
410 |
emptyRequestMethods: emptyRequestMethods),
|
|
|
411 |
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
|
412 |
}
|
|
|
413 |
|
|
|
414 |
/// Creates a `DownloadTask` to `await` serialization of the downloaded file's `URL` on disk.
|
|
|
415 |
///
|
|
|
416 |
/// - Parameters:
|
|
|
417 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
418 |
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
|
419 |
/// properties. `false` by default.
|
|
|
420 |
///
|
|
|
421 |
/// - Returns: The `DownloadTask`.
|
|
|
422 |
public func serializingDownloadedFileURL(automaticallyCancelling shouldAutomaticallyCancel: Bool = false) -> DownloadTask<URL> {
|
|
|
423 |
serializingDownload(using: URLResponseSerializer(),
|
|
|
424 |
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
|
425 |
}
|
|
|
426 |
|
|
|
427 |
/// Creates a `DownloadTask` to `await` serialization of a `String` value.
|
|
|
428 |
///
|
|
|
429 |
/// - Parameters:
|
|
|
430 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
431 |
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
|
432 |
/// properties. `false` by default.
|
|
|
433 |
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the
|
|
|
434 |
/// serializer. `PassthroughPreprocessor()` by default.
|
|
|
435 |
/// - encoding: `String.Encoding` to use during serialization. Defaults to `nil`, in which case
|
|
|
436 |
/// the encoding will be determined from the server response, falling back to the
|
|
|
437 |
/// default HTTP character set, `ISO-8859-1`.
|
|
|
438 |
/// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
|
|
|
439 |
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
|
440 |
///
|
|
|
441 |
/// - Returns: The `DownloadTask`.
|
|
|
442 |
public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = false,
|
|
|
443 |
dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
|
|
|
444 |
encoding: String.Encoding? = nil,
|
|
|
445 |
emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
|
|
|
446 |
emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask<String> {
|
|
|
447 |
serializingDownload(using: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
|
|
|
448 |
encoding: encoding,
|
|
|
449 |
emptyResponseCodes: emptyResponseCodes,
|
|
|
450 |
emptyRequestMethods: emptyRequestMethods),
|
|
|
451 |
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
|
452 |
}
|
|
|
453 |
|
|
|
454 |
/// Creates a `DownloadTask` to `await` serialization using the provided `ResponseSerializer` instance.
|
|
|
455 |
///
|
|
|
456 |
/// - Parameters:
|
|
|
457 |
/// - serializer: `ResponseSerializer` responsible for serializing the request, response, and data.
|
|
|
458 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
459 |
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
|
460 |
/// properties. `false` by default.
|
|
|
461 |
///
|
|
|
462 |
/// - Returns: The `DownloadTask`.
|
|
|
463 |
public func serializingDownload<Serializer: ResponseSerializer>(using serializer: Serializer,
|
|
|
464 |
automaticallyCancelling shouldAutomaticallyCancel: Bool = false)
|
|
|
465 |
-> DownloadTask<Serializer.SerializedObject> {
|
|
|
466 |
downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) {
|
|
|
467 |
self.response(queue: .singleEventQueue,
|
|
|
468 |
responseSerializer: serializer,
|
|
|
469 |
completionHandler: $0)
|
|
|
470 |
}
|
|
|
471 |
}
|
|
|
472 |
|
|
|
473 |
/// Creates a `DownloadTask` to `await` serialization using the provided `DownloadResponseSerializerProtocol`
|
|
|
474 |
/// instance.
|
|
|
475 |
///
|
|
|
476 |
/// - Parameters:
|
|
|
477 |
/// - serializer: `DownloadResponseSerializerProtocol` responsible for serializing the request,
|
|
|
478 |
/// response, and data.
|
|
|
479 |
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
|
480 |
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
|
481 |
/// properties. `false` by default.
|
|
|
482 |
///
|
|
|
483 |
/// - Returns: The `DownloadTask`.
|
|
|
484 |
public func serializingDownload<Serializer: DownloadResponseSerializerProtocol>(using serializer: Serializer,
|
|
|
485 |
automaticallyCancelling shouldAutomaticallyCancel: Bool = false)
|
|
|
486 |
-> DownloadTask<Serializer.SerializedObject> {
|
|
|
487 |
downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) {
|
|
|
488 |
self.response(queue: .singleEventQueue,
|
|
|
489 |
responseSerializer: serializer,
|
|
|
490 |
completionHandler: $0)
|
|
|
491 |
}
|
|
|
492 |
}
|
|
|
493 |
|
|
|
494 |
private func downloadTask<Value>(automaticallyCancelling shouldAutomaticallyCancel: Bool,
|
|
|
495 |
forResponse onResponse: @escaping (@escaping (DownloadResponse<Value, AFError>) -> Void) -> Void)
|
|
|
496 |
-> DownloadTask<Value> {
|
|
|
497 |
let task = Task {
|
|
|
498 |
await withTaskCancellationHandler {
|
|
|
499 |
self.cancel()
|
|
|
500 |
} operation: {
|
|
|
501 |
await withCheckedContinuation { continuation in
|
|
|
502 |
onResponse {
|
|
|
503 |
continuation.resume(returning: $0)
|
|
|
504 |
}
|
|
|
505 |
}
|
|
|
506 |
}
|
|
|
507 |
}
|
|
|
508 |
|
|
|
509 |
return DownloadTask<Value>(request: self, task: task, shouldAutomaticallyCancel: shouldAutomaticallyCancel)
|
|
|
510 |
}
|
|
|
511 |
}
|
|
|
512 |
|
|
|
513 |
// MARK: - DataStreamTask
|
|
|
514 |
|
|
|
515 |
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
|
516 |
public struct DataStreamTask {
|
|
|
517 |
// Type of created streams.
|
|
|
518 |
public typealias Stream<Success, Failure: Error> = StreamOf<DataStreamRequest.Stream<Success, Failure>>
|
|
|
519 |
|
|
|
520 |
private let request: DataStreamRequest
|
|
|
521 |
|
|
|
522 |
fileprivate init(request: DataStreamRequest) {
|
|
|
523 |
self.request = request
|
|
|
524 |
}
|
|
|
525 |
|
|
|
526 |
/// Creates a `Stream` of `Data` values from the underlying `DataStreamRequest`.
|
|
|
527 |
///
|
|
|
528 |
/// - Parameters:
|
|
|
529 |
/// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
|
|
|
530 |
/// which observation of the stream stops. `true` by default.
|
|
|
531 |
/// - bufferingPolicy: ` BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
|
532 |
///
|
|
|
533 |
/// - Returns: The `Stream`.
|
|
|
534 |
public func streamingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, bufferingPolicy: Stream<Data, Never>.BufferingPolicy = .unbounded) -> Stream<Data, Never> {
|
|
|
535 |
createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
|
|
|
536 |
self.request.responseStream(on: .streamCompletionQueue(forRequestID: request.id), stream: onStream)
|
|
|
537 |
}
|
|
|
538 |
}
|
|
|
539 |
|
|
|
540 |
/// Creates a `Stream` of `UTF-8` `String`s from the underlying `DataStreamRequest`.
|
|
|
541 |
///
|
|
|
542 |
/// - Parameters:
|
|
|
543 |
/// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
|
|
|
544 |
/// which observation of the stream stops. `true` by default.
|
|
|
545 |
/// - bufferingPolicy: ` BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
|
546 |
/// - Returns:
|
|
|
547 |
public func streamingStrings(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, bufferingPolicy: Stream<String, Never>.BufferingPolicy = .unbounded) -> Stream<String, Never> {
|
|
|
548 |
createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
|
|
|
549 |
self.request.responseStreamString(on: .streamCompletionQueue(forRequestID: request.id), stream: onStream)
|
|
|
550 |
}
|
|
|
551 |
}
|
|
|
552 |
|
|
|
553 |
/// Creates a `Stream` of `Decodable` values from the underlying `DataStreamRequest`.
|
|
|
554 |
///
|
|
|
555 |
/// - Parameters:
|
|
|
556 |
/// - type: `Decodable` type to be serialized from stream payloads.
|
|
|
557 |
/// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
|
|
|
558 |
/// which observation of the stream stops. `true` by default.
|
|
|
559 |
/// - bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
|
560 |
///
|
|
|
561 |
/// - Returns: The `Stream`.
|
|
|
562 |
public func streamingDecodables<T>(_ type: T.Type = T.self,
|
|
|
563 |
automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
|
564 |
bufferingPolicy: Stream<T, AFError>.BufferingPolicy = .unbounded)
|
|
|
565 |
-> Stream<T, AFError> where T: Decodable {
|
|
|
566 |
streamingResponses(serializedUsing: DecodableStreamSerializer<T>(),
|
|
|
567 |
automaticallyCancelling: shouldAutomaticallyCancel,
|
|
|
568 |
bufferingPolicy: bufferingPolicy)
|
|
|
569 |
}
|
|
|
570 |
|
|
|
571 |
/// Creates a `Stream` of values using the provided `DataStreamSerializer` from the underlying `DataStreamRequest`.
|
|
|
572 |
///
|
|
|
573 |
/// - Parameters:
|
|
|
574 |
/// - serializer: `DataStreamSerializer` to use to serialize incoming `Data`.
|
|
|
575 |
/// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
|
|
|
576 |
/// which observation of the stream stops. `true` by default.
|
|
|
577 |
/// - bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
|
578 |
///
|
|
|
579 |
/// - Returns: The `Stream`.
|
|
|
580 |
public func streamingResponses<Serializer: DataStreamSerializer>(serializedUsing serializer: Serializer,
|
|
|
581 |
automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
|
582 |
bufferingPolicy: Stream<Serializer.SerializedObject, AFError>.BufferingPolicy = .unbounded)
|
|
|
583 |
-> Stream<Serializer.SerializedObject, AFError> {
|
|
|
584 |
createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
|
|
|
585 |
self.request.responseStream(using: serializer,
|
|
|
586 |
on: .streamCompletionQueue(forRequestID: request.id),
|
|
|
587 |
stream: onStream)
|
|
|
588 |
}
|
|
|
589 |
}
|
|
|
590 |
|
|
|
591 |
private func createStream<Success, Failure: Error>(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
|
592 |
bufferingPolicy: Stream<Success, Failure>.BufferingPolicy = .unbounded,
|
|
|
593 |
forResponse onResponse: @escaping (@escaping (DataStreamRequest.Stream<Success, Failure>) -> Void) -> Void)
|
|
|
594 |
-> Stream<Success, Failure> {
|
|
|
595 |
StreamOf(bufferingPolicy: bufferingPolicy) {
|
|
|
596 |
guard shouldAutomaticallyCancel,
|
|
|
597 |
request.isInitialized || request.isResumed || request.isSuspended else { return }
|
|
|
598 |
|
|
|
599 |
cancel()
|
|
|
600 |
} builder: { continuation in
|
|
|
601 |
onResponse { stream in
|
|
|
602 |
continuation.yield(stream)
|
|
|
603 |
if case .complete = stream.event {
|
|
|
604 |
continuation.finish()
|
|
|
605 |
}
|
|
|
606 |
}
|
|
|
607 |
}
|
|
|
608 |
}
|
|
|
609 |
|
|
|
610 |
/// Cancel the underlying `DataStreamRequest`.
|
|
|
611 |
public func cancel() {
|
|
|
612 |
request.cancel()
|
|
|
613 |
}
|
|
|
614 |
|
|
|
615 |
/// Resume the underlying `DataStreamRequest`.
|
|
|
616 |
public func resume() {
|
|
|
617 |
request.resume()
|
|
|
618 |
}
|
|
|
619 |
|
|
|
620 |
/// Suspend the underlying `DataStreamRequest`.
|
|
|
621 |
public func suspend() {
|
|
|
622 |
request.suspend()
|
|
|
623 |
}
|
|
|
624 |
}
|
|
|
625 |
|
|
|
626 |
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
|
627 |
extension DataStreamRequest {
|
|
|
628 |
/// Creates a `DataStreamTask` used to `await` streams of serialized values.
|
|
|
629 |
///
|
|
|
630 |
/// - Returns: The `DataStreamTask`.
|
|
|
631 |
public func streamTask() -> DataStreamTask {
|
|
|
632 |
DataStreamTask(request: self)
|
|
|
633 |
}
|
|
|
634 |
}
|
|
|
635 |
|
|
|
636 |
extension DispatchQueue {
|
|
|
637 |
fileprivate static let singleEventQueue = DispatchQueue(label: "org.alamofire.concurrencySingleEventQueue",
|
|
|
638 |
attributes: .concurrent)
|
|
|
639 |
|
|
|
640 |
fileprivate static func streamCompletionQueue(forRequestID id: UUID) -> DispatchQueue {
|
|
|
641 |
DispatchQueue(label: "org.alamofire.concurrencyStreamCompletionQueue-\(id)", target: .singleEventQueue)
|
|
|
642 |
}
|
|
|
643 |
}
|
|
|
644 |
|
|
|
645 |
/// An asynchronous sequence generated from an underlying `AsyncStream`. Only produced by Alamofire.
|
|
|
646 |
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
|
647 |
public struct StreamOf<Element>: AsyncSequence {
|
|
|
648 |
public typealias AsyncIterator = Iterator
|
|
|
649 |
public typealias BufferingPolicy = AsyncStream<Element>.Continuation.BufferingPolicy
|
|
|
650 |
fileprivate typealias Continuation = AsyncStream<Element>.Continuation
|
|
|
651 |
|
|
|
652 |
private let bufferingPolicy: BufferingPolicy
|
|
|
653 |
private let onTermination: (() -> Void)?
|
|
|
654 |
private let builder: (Continuation) -> Void
|
|
|
655 |
|
|
|
656 |
fileprivate init(bufferingPolicy: BufferingPolicy = .unbounded,
|
|
|
657 |
onTermination: (() -> Void)? = nil,
|
|
|
658 |
builder: @escaping (Continuation) -> Void) {
|
|
|
659 |
self.bufferingPolicy = bufferingPolicy
|
|
|
660 |
self.onTermination = onTermination
|
|
|
661 |
self.builder = builder
|
|
|
662 |
}
|
|
|
663 |
|
|
|
664 |
public func makeAsyncIterator() -> Iterator {
|
|
|
665 |
var continuation: AsyncStream<Element>.Continuation?
|
|
|
666 |
let stream = AsyncStream<Element> { innerContinuation in
|
|
|
667 |
continuation = innerContinuation
|
|
|
668 |
builder(innerContinuation)
|
|
|
669 |
}
|
|
|
670 |
|
|
|
671 |
return Iterator(iterator: stream.makeAsyncIterator()) {
|
|
|
672 |
continuation?.finish()
|
|
|
673 |
self.onTermination?()
|
|
|
674 |
}
|
|
|
675 |
}
|
|
|
676 |
|
|
|
677 |
public struct Iterator: AsyncIteratorProtocol {
|
|
|
678 |
private final class Token {
|
|
|
679 |
private let onDeinit: () -> Void
|
|
|
680 |
|
|
|
681 |
init(onDeinit: @escaping () -> Void) {
|
|
|
682 |
self.onDeinit = onDeinit
|
|
|
683 |
}
|
|
|
684 |
|
|
|
685 |
deinit {
|
|
|
686 |
onDeinit()
|
|
|
687 |
}
|
|
|
688 |
}
|
|
|
689 |
|
|
|
690 |
private var iterator: AsyncStream<Element>.AsyncIterator
|
|
|
691 |
private let token: Token
|
|
|
692 |
|
|
|
693 |
init(iterator: AsyncStream<Element>.AsyncIterator, onCancellation: @escaping () -> Void) {
|
|
|
694 |
self.iterator = iterator
|
|
|
695 |
token = Token(onDeinit: onCancellation)
|
|
|
696 |
}
|
|
|
697 |
|
|
|
698 |
public mutating func next() async -> Element? {
|
|
|
699 |
await iterator.next()
|
|
|
700 |
}
|
|
|
701 |
}
|
|
|
702 |
}
|
|
|
703 |
|
|
|
704 |
#endif
|