Commit 4045a382d42cd91676ddb2d369de041d7004c0e5
0 parents
元码工程初始化提交
Showing
15 changed files
with
4579 additions
and
0 deletions
Too many changes to show.
To preserve performance only 15 of 109 files are displayed.
Podfile
0 → 100644
| 1 | +++ a/Podfile | ||
| 1 | +# Uncomment the next line to define a global platform for your project | ||
| 2 | +# platform :ios, '9.0' | ||
| 3 | + | ||
| 4 | +target 'metaCode' do | ||
| 5 | + # Comment the next line if you don't want to use dynamic frameworks | ||
| 6 | + use_frameworks! | ||
| 7 | + | ||
| 8 | + pod 'Alamofire' | ||
| 9 | + | ||
| 10 | + # Pods for metaCode | ||
| 11 | + | ||
| 12 | + target 'metaCodeTests' do | ||
| 13 | + inherit! :search_paths | ||
| 14 | + # Pods for testing | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + target 'metaCodeUITests' do | ||
| 18 | + # Pods for testing | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | +end |
Podfile.lock
0 → 100644
| 1 | +++ a/Podfile.lock | ||
| 1 | +PODS: | ||
| 2 | + - Alamofire (5.7.1) | ||
| 3 | + | ||
| 4 | +DEPENDENCIES: | ||
| 5 | + - Alamofire | ||
| 6 | + | ||
| 7 | +SPEC REPOS: | ||
| 8 | + trunk: | ||
| 9 | + - Alamofire | ||
| 10 | + | ||
| 11 | +SPEC CHECKSUMS: | ||
| 12 | + Alamofire: 0123a34370cb170936ae79a8df46cc62b2edeb88 | ||
| 13 | + | ||
| 14 | +PODFILE CHECKSUM: eff4d6eae7591679717a1823b39a746e49739fe5 | ||
| 15 | + | ||
| 16 | +COCOAPODS: 1.11.3 |
Pods/Alamofire/LICENSE
0 → 100644
| 1 | +++ a/Pods/Alamofire/LICENSE | ||
| 1 | +Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/) | ||
| 2 | + | ||
| 3 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 4 | +of this software and associated documentation files (the "Software"), to deal | ||
| 5 | +in the Software without restriction, including without limitation the rights | ||
| 6 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 7 | +copies of the Software, and to permit persons to whom the Software is | ||
| 8 | +furnished to do so, subject to the following conditions: | ||
| 9 | + | ||
| 10 | +The above copyright notice and this permission notice shall be included in | ||
| 11 | +all copies or substantial portions of the Software. | ||
| 12 | + | ||
| 13 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 14 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 15 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 16 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 17 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 18 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| 19 | +THE SOFTWARE. |
Pods/Alamofire/README.md
0 → 100644
| 1 | +++ a/Pods/Alamofire/README.md | ||
| 1 | + | ||
| 2 | + | ||
| 3 | +[](https://img.shields.io/badge/Swift-5.5_5.6_5.7_5.8-Orange?style=flat-square) | ||
| 4 | +[](https://img.shields.io/badge/Platforms-macOS_iOS_tvOS_watchOS_Linux_Windows-Green?style=flat-square) | ||
| 5 | +[](https://img.shields.io/cocoapods/v/Alamofire.svg) | ||
| 6 | +[](https://github.com/Carthage/Carthage) | ||
| 7 | +[](https://img.shields.io/badge/Swift_Package_Manager-compatible-orange?style=flat-square) | ||
| 8 | +[](https://twitter.com/AlamofireSF) | ||
| 9 | +[](https://forums.swift.org/c/related-projects/alamofire/37) | ||
| 10 | + | ||
| 11 | +Alamofire is an HTTP networking library written in Swift. | ||
| 12 | + | ||
| 13 | +- [Features](#features) | ||
| 14 | +- [Component Libraries](#component-libraries) | ||
| 15 | +- [Requirements](#requirements) | ||
| 16 | +- [Migration Guides](#migration-guides) | ||
| 17 | +- [Communication](#communication) | ||
| 18 | +- [Installation](#installation) | ||
| 19 | +- [Contributing](#contributing) | ||
| 20 | +- [Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#using-alamofire) | ||
| 21 | + - [**Introduction -**](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#introduction) [Making Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#making-requests), [Response Handling](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-handling), [Response Validation](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-validation), [Response Caching](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-caching) | ||
| 22 | + - **HTTP -** [HTTP Methods](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-methods), [Parameters and Parameter Encoder](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md##request-parameters-and-parameter-encoders), [HTTP Headers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#http-headers), [Authentication](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#authentication) | ||
| 23 | + - **Large Data -** [Downloading Data to a File](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#downloading-data-to-a-file), [Uploading Data to a Server](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#uploading-data-to-a-server) | ||
| 24 | + - **Tools -** [Statistical Metrics](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#statistical-metrics), [cURL Command Output](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#curl-command-output) | ||
| 25 | +- [Advanced Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md) | ||
| 26 | + - **URL Session -** [Session Manager](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session), [Session Delegate](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#sessiondelegate), [Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#request) | ||
| 27 | + - **Routing -** [Routing Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#routing-requests), [Adapting and Retrying Requests](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#adapting-and-retrying-requests-with-requestinterceptor) | ||
| 28 | + - **Model Objects -** [Custom Response Handlers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#customizing-response-handlers) | ||
| 29 | + - **Advanced Concurrency -** [Swift Concurrency](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#using-alamofire-with-swift-concurrency) and [Combine](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#using-alamofire-with-combine) | ||
| 30 | + - **Connection -** [Security](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#security), [Network Reachability](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#network-reachability) | ||
| 31 | +- [Open Radars](#open-radars) | ||
| 32 | +- [FAQ](#faq) | ||
| 33 | +- [Credits](#credits) | ||
| 34 | +- [Donations](#donations) | ||
| 35 | +- [License](#license) | ||
| 36 | + | ||
| 37 | +## Features | ||
| 38 | + | ||
| 39 | +- [x] Chainable Request / Response Methods | ||
| 40 | +- [x] Swift Concurrency Support Back to iOS 13, macOS 10.15, tvOS 13, and watchOS 6. | ||
| 41 | +- [x] Combine Support | ||
| 42 | +- [x] URL / JSON Parameter Encoding | ||
| 43 | +- [x] Upload File / Data / Stream / MultipartFormData | ||
| 44 | +- [x] Download File using Request or Resume Data | ||
| 45 | +- [x] Authentication with `URLCredential` | ||
| 46 | +- [x] HTTP Response Validation | ||
| 47 | +- [x] Upload and Download Progress Closures with Progress | ||
| 48 | +- [x] cURL Command Output | ||
| 49 | +- [x] Dynamically Adapt and Retry Requests | ||
| 50 | +- [x] TLS Certificate and Public Key Pinning | ||
| 51 | +- [x] Network Reachability | ||
| 52 | +- [x] Comprehensive Unit and Integration Test Coverage | ||
| 53 | +- [x] [Complete Documentation](https://alamofire.github.io/Alamofire) | ||
| 54 | + | ||
| 55 | +## Component Libraries | ||
| 56 | + | ||
| 57 | +In order to keep Alamofire focused specifically on core networking implementations, additional component libraries have been created by the [Alamofire Software Foundation](https://github.com/Alamofire/Foundation) to bring additional functionality to the Alamofire ecosystem. | ||
| 58 | + | ||
| 59 | +- [AlamofireImage](https://github.com/Alamofire/AlamofireImage) - An image library including image response serializers, `UIImage` and `UIImageView` extensions, custom image filters, an auto-purging in-memory cache, and a priority-based image downloading system. | ||
| 60 | +- [AlamofireNetworkActivityIndicator](https://github.com/Alamofire/AlamofireNetworkActivityIndicator) - Controls the visibility of the network activity indicator on iOS using Alamofire. It contains configurable delay timers to help mitigate flicker and can support `URLSession` instances not managed by Alamofire. | ||
| 61 | + | ||
| 62 | +## Requirements | ||
| 63 | + | ||
| 64 | +| Platform | Minimum Swift Version | Installation | Status | | ||
| 65 | +| --- | --- | --- | --- | | ||
| 66 | +| iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+ | 5.5 | [CocoaPods](#cocoapods), [Carthage](#carthage), [Swift Package Manager](#swift-package-manager), [Manual](#manually) | Fully Tested | | ||
| 67 | +| Linux | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported | | ||
| 68 | +| Windows | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported | | ||
| 69 | + | ||
| 70 | +#### Known Issues on Linux and Windows | ||
| 71 | + | ||
| 72 | +Alamofire builds on Linux and Windows but there are missing features and many issues in the underlying `swift-corelibs-foundation` that prevent full functionality and may cause crashes. These include: | ||
| 73 | +- `ServerTrustManager` and associated certificate functionality is unavailable, so there is no certificate pinning and no client certificate support. | ||
| 74 | +- Various methods of HTTP authentication may crash, including HTTP Basic and HTTP Digest. Crashes may occur if responses contain server challenges. | ||
| 75 | +- Cache control through `CachedResponseHandler` and associated APIs is unavailable, as the underlying delegate methods aren't called. | ||
| 76 | +- `URLSessionTaskMetrics` are never gathered. | ||
| 77 | + | ||
| 78 | +Due to these issues, Alamofire is unsupported on Linux and Windows. Please report any crashes to the [Swift bug reporter](https://bugs.swift.org). | ||
| 79 | + | ||
| 80 | +## Migration Guides | ||
| 81 | + | ||
| 82 | +- [Alamofire 5.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%205.0%20Migration%20Guide.md) | ||
| 83 | +- [Alamofire 4.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md) | ||
| 84 | +- [Alamofire 3.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md) | ||
| 85 | +- [Alamofire 2.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%202.0%20Migration%20Guide.md) | ||
| 86 | + | ||
| 87 | +## Communication | ||
| 88 | +- If you **need help with making network requests** using Alamofire, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire) and tag `alamofire`. | ||
| 89 | +- If you need to **find or understand an API**, check [our documentation](http://alamofire.github.io/Alamofire/) or [Apple's documentation for `URLSession`](https://developer.apple.com/documentation/foundation/url_loading_system), on top of which Alamofire is built. | ||
| 90 | +- If you need **help with an Alamofire feature**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). | ||
| 91 | +- If you'd like to **discuss Alamofire best practices**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). | ||
| 92 | +- If you'd like to **discuss a feature request**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire). | ||
| 93 | +- If you **found a bug**, open an issue here on GitHub and follow the guide. The more detail the better! | ||
| 94 | + | ||
| 95 | +## Installation | ||
| 96 | + | ||
| 97 | +### CocoaPods | ||
| 98 | + | ||
| 99 | +[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`: | ||
| 100 | + | ||
| 101 | +```ruby | ||
| 102 | +pod 'Alamofire' | ||
| 103 | +``` | ||
| 104 | + | ||
| 105 | +### Carthage | ||
| 106 | + | ||
| 107 | +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`: | ||
| 108 | + | ||
| 109 | +```ogdl | ||
| 110 | +github "Alamofire/Alamofire" | ||
| 111 | +``` | ||
| 112 | + | ||
| 113 | +### Swift Package Manager | ||
| 114 | + | ||
| 115 | +The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler. | ||
| 116 | + | ||
| 117 | +Once you have your Swift package set up, adding Alamofire as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`. | ||
| 118 | + | ||
| 119 | +```swift | ||
| 120 | +dependencies: [ | ||
| 121 | + .package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.6.4")) | ||
| 122 | +] | ||
| 123 | +``` | ||
| 124 | + | ||
| 125 | +### Manually | ||
| 126 | + | ||
| 127 | +If you prefer not to use any of the aforementioned dependency managers, you can integrate Alamofire into your project manually. | ||
| 128 | + | ||
| 129 | +#### Embedded Framework | ||
| 130 | + | ||
| 131 | +- Open up Terminal, `cd` into your top-level project directory, and run the following command "if" your project is not initialized as a git repository: | ||
| 132 | + | ||
| 133 | + ```bash | ||
| 134 | + $ git init | ||
| 135 | + ``` | ||
| 136 | + | ||
| 137 | +- Add Alamofire as a git [submodule](https://git-scm.com/docs/git-submodule) by running the following command: | ||
| 138 | + | ||
| 139 | + ```bash | ||
| 140 | + $ git submodule add https://github.com/Alamofire/Alamofire.git | ||
| 141 | + ``` | ||
| 142 | + | ||
| 143 | +- Open the new `Alamofire` folder, and drag the `Alamofire.xcodeproj` into the Project Navigator of your application's Xcode project. | ||
| 144 | + | ||
| 145 | + > It should appear nested underneath your application's blue project icon. Whether it is above or below all the other Xcode groups does not matter. | ||
| 146 | + | ||
| 147 | +- Select the `Alamofire.xcodeproj` in the Project Navigator and verify the deployment target matches that of your application target. | ||
| 148 | +- Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the "Targets" heading in the sidebar. | ||
| 149 | +- In the tab bar at the top of that window, open the "General" panel. | ||
| 150 | +- Click on the `+` button under the "Embedded Binaries" section. | ||
| 151 | +- You will see two different `Alamofire.xcodeproj` folders each with two different versions of the `Alamofire.framework` nested inside a `Products` folder. | ||
| 152 | + | ||
| 153 | + > It does not matter which `Products` folder you choose from, but it does matter whether you choose the top or bottom `Alamofire.framework`. | ||
| 154 | + | ||
| 155 | +- Select the top `Alamofire.framework` for iOS and the bottom one for macOS. | ||
| 156 | + | ||
| 157 | + > You can verify which one you selected by inspecting the build log for your project. The build target for `Alamofire` will be listed as `Alamofire iOS`, `Alamofire macOS`, `Alamofire tvOS`, or `Alamofire watchOS`. | ||
| 158 | + | ||
| 159 | +- And that's it! | ||
| 160 | + | ||
| 161 | + > The `Alamofire.framework` is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device. | ||
| 162 | + | ||
| 163 | +## Contributing | ||
| 164 | + | ||
| 165 | +Before contributing to Alamofire, please read the instructions detailed in our [contribution guide](https://github.com/Alamofire/Alamofire/blob/master/CONTRIBUTING.md). | ||
| 166 | + | ||
| 167 | +## Open Radars | ||
| 168 | + | ||
| 169 | +The following radars have some effect on the current implementation of Alamofire. | ||
| 170 | + | ||
| 171 | +- [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in the test case | ||
| 172 | +- `rdar://26870455` - Background URL Session Configurations do not work in the simulator | ||
| 173 | +- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest` | ||
| 174 | + | ||
| 175 | +## Resolved Radars | ||
| 176 | + | ||
| 177 | +The following radars have been resolved over time after being filed against the Alamofire project. | ||
| 178 | + | ||
| 179 | +- [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage. | ||
| 180 | + - (Resolved): 9/1/17 in Xcode 9 beta 6. | ||
| 181 | +- [`rdar://36082113`](http://openradar.appspot.com/radar?id=4942308441063424) - `URLSessionTaskMetrics` failing to link on watchOS 3.0+ | ||
| 182 | + - (Resolved): Just add `CFNetwork` to your linked frameworks. | ||
| 183 | +- `FB7624529` - `urlSession(_:task:didFinishCollecting:)` never called on watchOS | ||
| 184 | + - (Resolved): Metrics now collected on watchOS 7+. | ||
| 185 | + | ||
| 186 | +## FAQ | ||
| 187 | + | ||
| 188 | +### What's the origin of the name Alamofire? | ||
| 189 | + | ||
| 190 | +Alamofire is named after the [Alamo Fire flower](https://aggie-horticulture.tamu.edu/wildseed/alamofire.html), a hybrid variant of the Bluebonnet, the official state flower of Texas. | ||
| 191 | + | ||
| 192 | +## Credits | ||
| 193 | + | ||
| 194 | +Alamofire is owned and maintained by the [Alamofire Software Foundation](http://alamofire.org). You can follow them on Twitter at [@AlamofireSF](https://twitter.com/AlamofireSF) for project updates and releases. | ||
| 195 | + | ||
| 196 | +### Security Disclosure | ||
| 197 | + | ||
| 198 | +If you believe you have identified a security vulnerability with Alamofire, you should report it as soon as possible via email to security@alamofire.org. Please do not post it to a public issue tracker. | ||
| 199 | + | ||
| 200 | +## Sponsorship | ||
| 201 | + | ||
| 202 | +The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise money to officially stay registered as a federal non-profit organization. | ||
| 203 | +Registering will allow Foundation members to gain some legal protections and also allow us to put donations to use, tax-free. | ||
| 204 | +Sponsoring the ASF will enable us to: | ||
| 205 | + | ||
| 206 | +- Pay our yearly legal fees to keep the non-profit in good status | ||
| 207 | +- Pay for our mail servers to help us stay on top of all questions and security issues | ||
| 208 | +- Potentially fund test servers to make it easier for us to test the edge cases | ||
| 209 | +- Potentially fund developers to work on one of our projects full-time | ||
| 210 | + | ||
| 211 | +The community adoption of the ASF libraries has been amazing. | ||
| 212 | +We are greatly humbled by your enthusiasm around the projects and want to continue to do everything we can to move the needle forward. | ||
| 213 | +With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members. | ||
| 214 | +If you use any of our libraries for work, see if your employers would be interested in donating. | ||
| 215 | +Any amount you can donate, whether once or monthly, to help us reach our goal would be greatly appreciated. | ||
| 216 | + | ||
| 217 | +[Sponsor Alamofire](https://github.com/sponsors/Alamofire) | ||
| 218 | + | ||
| 219 | +## Supporters | ||
| 220 | + | ||
| 221 | +[MacStadium](https://macstadium.com) provides Alamofire with a free, hosted Mac mini. | ||
| 222 | + | ||
| 223 | + | ||
| 224 | + | ||
| 225 | +## License | ||
| 226 | + | ||
| 227 | +Alamofire is released under the MIT license. [See LICENSE](https://github.com/Alamofire/Alamofire/blob/master/LICENSE) for details. |
Pods/Alamofire/Source/AFError.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/AFError.swift | ||
| 1 | +// | ||
| 2 | +// AFError.swift | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2014-2018 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 | +import Foundation | ||
| 26 | + | ||
| 27 | +/// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with | ||
| 28 | +/// their own associated reasons. | ||
| 29 | +public enum AFError: Error { | ||
| 30 | + /// The underlying reason the `.multipartEncodingFailed` error occurred. | ||
| 31 | + public enum MultipartEncodingFailureReason { | ||
| 32 | + /// The `fileURL` provided for reading an encodable body part isn't a file `URL`. | ||
| 33 | + case bodyPartURLInvalid(url: URL) | ||
| 34 | + /// The filename of the `fileURL` provided has either an empty `lastPathComponent` or `pathExtension. | ||
| 35 | + case bodyPartFilenameInvalid(in: URL) | ||
| 36 | + /// The file at the `fileURL` provided was not reachable. | ||
| 37 | + case bodyPartFileNotReachable(at: URL) | ||
| 38 | + /// Attempting to check the reachability of the `fileURL` provided threw an error. | ||
| 39 | + case bodyPartFileNotReachableWithError(atURL: URL, error: Error) | ||
| 40 | + /// The file at the `fileURL` provided is actually a directory. | ||
| 41 | + case bodyPartFileIsDirectory(at: URL) | ||
| 42 | + /// The size of the file at the `fileURL` provided was not returned by the system. | ||
| 43 | + case bodyPartFileSizeNotAvailable(at: URL) | ||
| 44 | + /// The attempt to find the size of the file at the `fileURL` provided threw an error. | ||
| 45 | + case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error) | ||
| 46 | + /// An `InputStream` could not be created for the provided `fileURL`. | ||
| 47 | + case bodyPartInputStreamCreationFailed(for: URL) | ||
| 48 | + /// An `OutputStream` could not be created when attempting to write the encoded data to disk. | ||
| 49 | + case outputStreamCreationFailed(for: URL) | ||
| 50 | + /// The encoded body data could not be written to disk because a file already exists at the provided `fileURL`. | ||
| 51 | + case outputStreamFileAlreadyExists(at: URL) | ||
| 52 | + /// The `fileURL` provided for writing the encoded body data to disk is not a file `URL`. | ||
| 53 | + case outputStreamURLInvalid(url: URL) | ||
| 54 | + /// The attempt to write the encoded body data to disk failed with an underlying error. | ||
| 55 | + case outputStreamWriteFailed(error: Error) | ||
| 56 | + /// The attempt to read an encoded body part `InputStream` failed with underlying system error. | ||
| 57 | + case inputStreamReadFailed(error: Error) | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + /// Represents unexpected input stream length that occur when encoding the `MultipartFormData`. Instances will be | ||
| 61 | + /// embedded within an `AFError.multipartEncodingFailed` `.inputStreamReadFailed` case. | ||
| 62 | + public struct UnexpectedInputStreamLength: Error { | ||
| 63 | + /// The expected byte count to read. | ||
| 64 | + public var bytesExpected: UInt64 | ||
| 65 | + /// The actual byte count read. | ||
| 66 | + public var bytesRead: UInt64 | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + /// The underlying reason the `.parameterEncodingFailed` error occurred. | ||
| 70 | + public enum ParameterEncodingFailureReason { | ||
| 71 | + /// The `URLRequest` did not have a `URL` to encode. | ||
| 72 | + case missingURL | ||
| 73 | + /// JSON serialization failed with an underlying system error during the encoding process. | ||
| 74 | + case jsonEncodingFailed(error: Error) | ||
| 75 | + /// Custom parameter encoding failed due to the associated `Error`. | ||
| 76 | + case customEncodingFailed(error: Error) | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + /// The underlying reason the `.parameterEncoderFailed` error occurred. | ||
| 80 | + public enum ParameterEncoderFailureReason { | ||
| 81 | + /// Possible missing components. | ||
| 82 | + public enum RequiredComponent { | ||
| 83 | + /// The `URL` was missing or unable to be extracted from the passed `URLRequest` or during encoding. | ||
| 84 | + case url | ||
| 85 | + /// The `HTTPMethod` could not be extracted from the passed `URLRequest`. | ||
| 86 | + case httpMethod(rawValue: String) | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + /// A `RequiredComponent` was missing during encoding. | ||
| 90 | + case missingRequiredComponent(RequiredComponent) | ||
| 91 | + /// The underlying encoder failed with the associated error. | ||
| 92 | + case encoderFailed(error: Error) | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + /// The underlying reason the `.responseValidationFailed` error occurred. | ||
| 96 | + public enum ResponseValidationFailureReason { | ||
| 97 | + /// The data file containing the server response did not exist. | ||
| 98 | + case dataFileNil | ||
| 99 | + /// The data file containing the server response at the associated `URL` could not be read. | ||
| 100 | + case dataFileReadFailed(at: URL) | ||
| 101 | + /// The response did not contain a `Content-Type` and the `acceptableContentTypes` provided did not contain a | ||
| 102 | + /// wildcard type. | ||
| 103 | + case missingContentType(acceptableContentTypes: [String]) | ||
| 104 | + /// The response `Content-Type` did not match any type in the provided `acceptableContentTypes`. | ||
| 105 | + case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String) | ||
| 106 | + /// The response status code was not acceptable. | ||
| 107 | + case unacceptableStatusCode(code: Int) | ||
| 108 | + /// Custom response validation failed due to the associated `Error`. | ||
| 109 | + case customValidationFailed(error: Error) | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + /// The underlying reason the response serialization error occurred. | ||
| 113 | + public enum ResponseSerializationFailureReason { | ||
| 114 | + /// The server response contained no data or the data was zero length. | ||
| 115 | + case inputDataNilOrZeroLength | ||
| 116 | + /// The file containing the server response did not exist. | ||
| 117 | + case inputFileNil | ||
| 118 | + /// The file containing the server response could not be read from the associated `URL`. | ||
| 119 | + case inputFileReadFailed(at: URL) | ||
| 120 | + /// String serialization failed using the provided `String.Encoding`. | ||
| 121 | + case stringSerializationFailed(encoding: String.Encoding) | ||
| 122 | + /// JSON serialization failed with an underlying system error. | ||
| 123 | + case jsonSerializationFailed(error: Error) | ||
| 124 | + /// A `DataDecoder` failed to decode the response due to the associated `Error`. | ||
| 125 | + case decodingFailed(error: Error) | ||
| 126 | + /// A custom response serializer failed due to the associated `Error`. | ||
| 127 | + case customSerializationFailed(error: Error) | ||
| 128 | + /// Generic serialization failed for an empty response that wasn't type `Empty` but instead the associated type. | ||
| 129 | + case invalidEmptyResponse(type: String) | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + #if !(os(Linux) || os(Windows)) | ||
| 133 | + /// Underlying reason a server trust evaluation error occurred. | ||
| 134 | + public enum ServerTrustFailureReason { | ||
| 135 | + /// The output of a server trust evaluation. | ||
| 136 | + public struct Output { | ||
| 137 | + /// The host for which the evaluation was performed. | ||
| 138 | + public let host: String | ||
| 139 | + /// The `SecTrust` value which was evaluated. | ||
| 140 | + public let trust: SecTrust | ||
| 141 | + /// The `OSStatus` of evaluation operation. | ||
| 142 | + public let status: OSStatus | ||
| 143 | + /// The result of the evaluation operation. | ||
| 144 | + public let result: SecTrustResultType | ||
| 145 | + | ||
| 146 | + /// Creates an `Output` value from the provided values. | ||
| 147 | + init(_ host: String, _ trust: SecTrust, _ status: OSStatus, _ result: SecTrustResultType) { | ||
| 148 | + self.host = host | ||
| 149 | + self.trust = trust | ||
| 150 | + self.status = status | ||
| 151 | + self.result = result | ||
| 152 | + } | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + /// No `ServerTrustEvaluator` was found for the associated host. | ||
| 156 | + case noRequiredEvaluator(host: String) | ||
| 157 | + /// No certificates were found with which to perform the trust evaluation. | ||
| 158 | + case noCertificatesFound | ||
| 159 | + /// No public keys were found with which to perform the trust evaluation. | ||
| 160 | + case noPublicKeysFound | ||
| 161 | + /// During evaluation, application of the associated `SecPolicy` failed. | ||
| 162 | + case policyApplicationFailed(trust: SecTrust, policy: SecPolicy, status: OSStatus) | ||
| 163 | + /// During evaluation, setting the associated anchor certificates failed. | ||
| 164 | + case settingAnchorCertificatesFailed(status: OSStatus, certificates: [SecCertificate]) | ||
| 165 | + /// During evaluation, creation of the revocation policy failed. | ||
| 166 | + case revocationPolicyCreationFailed | ||
| 167 | + /// `SecTrust` evaluation failed with the associated `Error`, if one was produced. | ||
| 168 | + case trustEvaluationFailed(error: Error?) | ||
| 169 | + /// Default evaluation failed with the associated `Output`. | ||
| 170 | + case defaultEvaluationFailed(output: Output) | ||
| 171 | + /// Host validation failed with the associated `Output`. | ||
| 172 | + case hostValidationFailed(output: Output) | ||
| 173 | + /// Revocation check failed with the associated `Output` and options. | ||
| 174 | + case revocationCheckFailed(output: Output, options: RevocationTrustEvaluator.Options) | ||
| 175 | + /// Certificate pinning failed. | ||
| 176 | + case certificatePinningFailed(host: String, trust: SecTrust, pinnedCertificates: [SecCertificate], serverCertificates: [SecCertificate]) | ||
| 177 | + /// Public key pinning failed. | ||
| 178 | + case publicKeyPinningFailed(host: String, trust: SecTrust, pinnedKeys: [SecKey], serverKeys: [SecKey]) | ||
| 179 | + /// Custom server trust evaluation failed due to the associated `Error`. | ||
| 180 | + case customEvaluationFailed(error: Error) | ||
| 181 | + } | ||
| 182 | + #endif | ||
| 183 | + | ||
| 184 | + /// The underlying reason the `.urlRequestValidationFailed` | ||
| 185 | + public enum URLRequestValidationFailureReason { | ||
| 186 | + /// URLRequest with GET method had body data. | ||
| 187 | + case bodyDataInGETRequest(Data) | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + /// `UploadableConvertible` threw an error in `createUploadable()`. | ||
| 191 | + case createUploadableFailed(error: Error) | ||
| 192 | + /// `URLRequestConvertible` threw an error in `asURLRequest()`. | ||
| 193 | + case createURLRequestFailed(error: Error) | ||
| 194 | + /// `SessionDelegate` threw an error while attempting to move downloaded file to destination URL. | ||
| 195 | + case downloadedFileMoveFailed(error: Error, source: URL, destination: URL) | ||
| 196 | + /// `Request` was explicitly cancelled. | ||
| 197 | + case explicitlyCancelled | ||
| 198 | + /// `URLConvertible` type failed to create a valid `URL`. | ||
| 199 | + case invalidURL(url: URLConvertible) | ||
| 200 | + /// Multipart form encoding failed. | ||
| 201 | + case multipartEncodingFailed(reason: MultipartEncodingFailureReason) | ||
| 202 | + /// `ParameterEncoding` threw an error during the encoding process. | ||
| 203 | + case parameterEncodingFailed(reason: ParameterEncodingFailureReason) | ||
| 204 | + /// `ParameterEncoder` threw an error while running the encoder. | ||
| 205 | + case parameterEncoderFailed(reason: ParameterEncoderFailureReason) | ||
| 206 | + /// `RequestAdapter` threw an error during adaptation. | ||
| 207 | + case requestAdaptationFailed(error: Error) | ||
| 208 | + /// `RequestRetrier` threw an error during the request retry process. | ||
| 209 | + case requestRetryFailed(retryError: Error, originalError: Error) | ||
| 210 | + /// Response validation failed. | ||
| 211 | + case responseValidationFailed(reason: ResponseValidationFailureReason) | ||
| 212 | + /// Response serialization failed. | ||
| 213 | + case responseSerializationFailed(reason: ResponseSerializationFailureReason) | ||
| 214 | + #if !(os(Linux) || os(Windows)) | ||
| 215 | + /// `ServerTrustEvaluating` instance threw an error during trust evaluation. | ||
| 216 | + case serverTrustEvaluationFailed(reason: ServerTrustFailureReason) | ||
| 217 | + #endif | ||
| 218 | + /// `Session` which issued the `Request` was deinitialized, most likely because its reference went out of scope. | ||
| 219 | + case sessionDeinitialized | ||
| 220 | + /// `Session` was explicitly invalidated, possibly with the `Error` produced by the underlying `URLSession`. | ||
| 221 | + case sessionInvalidated(error: Error?) | ||
| 222 | + /// `URLSessionTask` completed with error. | ||
| 223 | + case sessionTaskFailed(error: Error) | ||
| 224 | + /// `URLRequest` failed validation. | ||
| 225 | + case urlRequestValidationFailed(reason: URLRequestValidationFailureReason) | ||
| 226 | +} | ||
| 227 | + | ||
| 228 | +extension Error { | ||
| 229 | + /// Returns the instance cast as an `AFError`. | ||
| 230 | + public var asAFError: AFError? { | ||
| 231 | + self as? AFError | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + /// Returns the instance cast as an `AFError`. If casting fails, a `fatalError` with the specified `message` is thrown. | ||
| 235 | + public func asAFError(orFailWith message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> AFError { | ||
| 236 | + guard let afError = self as? AFError else { | ||
| 237 | + fatalError(message(), file: file, line: line) | ||
| 238 | + } | ||
| 239 | + return afError | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + /// Casts the instance as `AFError` or returns `defaultAFError` | ||
| 243 | + func asAFError(or defaultAFError: @autoclosure () -> AFError) -> AFError { | ||
| 244 | + self as? AFError ?? defaultAFError() | ||
| 245 | + } | ||
| 246 | +} | ||
| 247 | + | ||
| 248 | +// MARK: - Error Booleans | ||
| 249 | + | ||
| 250 | +extension AFError { | ||
| 251 | + /// Returns whether the instance is `.sessionDeinitialized`. | ||
| 252 | + public var isSessionDeinitializedError: Bool { | ||
| 253 | + if case .sessionDeinitialized = self { return true } | ||
| 254 | + return false | ||
| 255 | + } | ||
| 256 | + | ||
| 257 | + /// Returns whether the instance is `.sessionInvalidated`. | ||
| 258 | + public var isSessionInvalidatedError: Bool { | ||
| 259 | + if case .sessionInvalidated = self { return true } | ||
| 260 | + return false | ||
| 261 | + } | ||
| 262 | + | ||
| 263 | + /// Returns whether the instance is `.explicitlyCancelled`. | ||
| 264 | + public var isExplicitlyCancelledError: Bool { | ||
| 265 | + if case .explicitlyCancelled = self { return true } | ||
| 266 | + return false | ||
| 267 | + } | ||
| 268 | + | ||
| 269 | + /// Returns whether the instance is `.invalidURL`. | ||
| 270 | + public var isInvalidURLError: Bool { | ||
| 271 | + if case .invalidURL = self { return true } | ||
| 272 | + return false | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + /// Returns whether the instance is `.parameterEncodingFailed`. When `true`, the `underlyingError` property will | ||
| 276 | + /// contain the associated value. | ||
| 277 | + public var isParameterEncodingError: Bool { | ||
| 278 | + if case .parameterEncodingFailed = self { return true } | ||
| 279 | + return false | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + /// Returns whether the instance is `.parameterEncoderFailed`. When `true`, the `underlyingError` property will | ||
| 283 | + /// contain the associated value. | ||
| 284 | + public var isParameterEncoderError: Bool { | ||
| 285 | + if case .parameterEncoderFailed = self { return true } | ||
| 286 | + return false | ||
| 287 | + } | ||
| 288 | + | ||
| 289 | + /// Returns whether the instance is `.multipartEncodingFailed`. When `true`, the `url` and `underlyingError` | ||
| 290 | + /// properties will contain the associated values. | ||
| 291 | + public var isMultipartEncodingError: Bool { | ||
| 292 | + if case .multipartEncodingFailed = self { return true } | ||
| 293 | + return false | ||
| 294 | + } | ||
| 295 | + | ||
| 296 | + /// Returns whether the instance is `.requestAdaptationFailed`. When `true`, the `underlyingError` property will | ||
| 297 | + /// contain the associated value. | ||
| 298 | + public var isRequestAdaptationError: Bool { | ||
| 299 | + if case .requestAdaptationFailed = self { return true } | ||
| 300 | + return false | ||
| 301 | + } | ||
| 302 | + | ||
| 303 | + /// Returns whether the instance is `.responseValidationFailed`. When `true`, the `acceptableContentTypes`, | ||
| 304 | + /// `responseContentType`, `responseCode`, and `underlyingError` properties will contain the associated values. | ||
| 305 | + public var isResponseValidationError: Bool { | ||
| 306 | + if case .responseValidationFailed = self { return true } | ||
| 307 | + return false | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + /// Returns whether the instance is `.responseSerializationFailed`. When `true`, the `failedStringEncoding` and | ||
| 311 | + /// `underlyingError` properties will contain the associated values. | ||
| 312 | + public var isResponseSerializationError: Bool { | ||
| 313 | + if case .responseSerializationFailed = self { return true } | ||
| 314 | + return false | ||
| 315 | + } | ||
| 316 | + | ||
| 317 | + #if !(os(Linux) || os(Windows)) | ||
| 318 | + /// Returns whether the instance is `.serverTrustEvaluationFailed`. When `true`, the `underlyingError` property will | ||
| 319 | + /// contain the associated value. | ||
| 320 | + public var isServerTrustEvaluationError: Bool { | ||
| 321 | + if case .serverTrustEvaluationFailed = self { return true } | ||
| 322 | + return false | ||
| 323 | + } | ||
| 324 | + #endif | ||
| 325 | + | ||
| 326 | + /// Returns whether the instance is `requestRetryFailed`. When `true`, the `underlyingError` property will | ||
| 327 | + /// contain the associated value. | ||
| 328 | + public var isRequestRetryError: Bool { | ||
| 329 | + if case .requestRetryFailed = self { return true } | ||
| 330 | + return false | ||
| 331 | + } | ||
| 332 | + | ||
| 333 | + /// Returns whether the instance is `createUploadableFailed`. When `true`, the `underlyingError` property will | ||
| 334 | + /// contain the associated value. | ||
| 335 | + public var isCreateUploadableError: Bool { | ||
| 336 | + if case .createUploadableFailed = self { return true } | ||
| 337 | + return false | ||
| 338 | + } | ||
| 339 | + | ||
| 340 | + /// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will | ||
| 341 | + /// contain the associated value. | ||
| 342 | + public var isCreateURLRequestError: Bool { | ||
| 343 | + if case .createURLRequestFailed = self { return true } | ||
| 344 | + return false | ||
| 345 | + } | ||
| 346 | + | ||
| 347 | + /// Returns whether the instance is `downloadedFileMoveFailed`. When `true`, the `destination` and `underlyingError` properties will | ||
| 348 | + /// contain the associated values. | ||
| 349 | + public var isDownloadedFileMoveError: Bool { | ||
| 350 | + if case .downloadedFileMoveFailed = self { return true } | ||
| 351 | + return false | ||
| 352 | + } | ||
| 353 | + | ||
| 354 | + /// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will | ||
| 355 | + /// contain the associated value. | ||
| 356 | + public var isSessionTaskError: Bool { | ||
| 357 | + if case .sessionTaskFailed = self { return true } | ||
| 358 | + return false | ||
| 359 | + } | ||
| 360 | +} | ||
| 361 | + | ||
| 362 | +// MARK: - Convenience Properties | ||
| 363 | + | ||
| 364 | +extension AFError { | ||
| 365 | + /// The `URLConvertible` associated with the error. | ||
| 366 | + public var urlConvertible: URLConvertible? { | ||
| 367 | + guard case let .invalidURL(url) = self else { return nil } | ||
| 368 | + return url | ||
| 369 | + } | ||
| 370 | + | ||
| 371 | + /// The `URL` associated with the error. | ||
| 372 | + public var url: URL? { | ||
| 373 | + guard case let .multipartEncodingFailed(reason) = self else { return nil } | ||
| 374 | + return reason.url | ||
| 375 | + } | ||
| 376 | + | ||
| 377 | + /// The underlying `Error` responsible for generating the failure associated with `.sessionInvalidated`, | ||
| 378 | + /// `.parameterEncodingFailed`, `.parameterEncoderFailed`, `.multipartEncodingFailed`, `.requestAdaptationFailed`, | ||
| 379 | + /// `.responseSerializationFailed`, `.requestRetryFailed` errors. | ||
| 380 | + public var underlyingError: Error? { | ||
| 381 | + switch self { | ||
| 382 | + case let .multipartEncodingFailed(reason): | ||
| 383 | + return reason.underlyingError | ||
| 384 | + case let .parameterEncodingFailed(reason): | ||
| 385 | + return reason.underlyingError | ||
| 386 | + case let .parameterEncoderFailed(reason): | ||
| 387 | + return reason.underlyingError | ||
| 388 | + case let .requestAdaptationFailed(error): | ||
| 389 | + return error | ||
| 390 | + case let .requestRetryFailed(retryError, _): | ||
| 391 | + return retryError | ||
| 392 | + case let .responseValidationFailed(reason): | ||
| 393 | + return reason.underlyingError | ||
| 394 | + case let .responseSerializationFailed(reason): | ||
| 395 | + return reason.underlyingError | ||
| 396 | + #if !(os(Linux) || os(Windows)) | ||
| 397 | + case let .serverTrustEvaluationFailed(reason): | ||
| 398 | + return reason.underlyingError | ||
| 399 | + #endif | ||
| 400 | + case let .sessionInvalidated(error): | ||
| 401 | + return error | ||
| 402 | + case let .createUploadableFailed(error): | ||
| 403 | + return error | ||
| 404 | + case let .createURLRequestFailed(error): | ||
| 405 | + return error | ||
| 406 | + case let .downloadedFileMoveFailed(error, _, _): | ||
| 407 | + return error | ||
| 408 | + case let .sessionTaskFailed(error): | ||
| 409 | + return error | ||
| 410 | + case .explicitlyCancelled, | ||
| 411 | + .invalidURL, | ||
| 412 | + .sessionDeinitialized, | ||
| 413 | + .urlRequestValidationFailed: | ||
| 414 | + return nil | ||
| 415 | + } | ||
| 416 | + } | ||
| 417 | + | ||
| 418 | + /// The acceptable `Content-Type`s of a `.responseValidationFailed` error. | ||
| 419 | + public var acceptableContentTypes: [String]? { | ||
| 420 | + guard case let .responseValidationFailed(reason) = self else { return nil } | ||
| 421 | + return reason.acceptableContentTypes | ||
| 422 | + } | ||
| 423 | + | ||
| 424 | + /// The response `Content-Type` of a `.responseValidationFailed` error. | ||
| 425 | + public var responseContentType: String? { | ||
| 426 | + guard case let .responseValidationFailed(reason) = self else { return nil } | ||
| 427 | + return reason.responseContentType | ||
| 428 | + } | ||
| 429 | + | ||
| 430 | + /// The response code of a `.responseValidationFailed` error. | ||
| 431 | + public var responseCode: Int? { | ||
| 432 | + guard case let .responseValidationFailed(reason) = self else { return nil } | ||
| 433 | + return reason.responseCode | ||
| 434 | + } | ||
| 435 | + | ||
| 436 | + /// The `String.Encoding` associated with a failed `.stringResponse()` call. | ||
| 437 | + public var failedStringEncoding: String.Encoding? { | ||
| 438 | + guard case let .responseSerializationFailed(reason) = self else { return nil } | ||
| 439 | + return reason.failedStringEncoding | ||
| 440 | + } | ||
| 441 | + | ||
| 442 | + /// The `source` URL of a `.downloadedFileMoveFailed` error. | ||
| 443 | + public var sourceURL: URL? { | ||
| 444 | + guard case let .downloadedFileMoveFailed(_, source, _) = self else { return nil } | ||
| 445 | + return source | ||
| 446 | + } | ||
| 447 | + | ||
| 448 | + /// The `destination` URL of a `.downloadedFileMoveFailed` error. | ||
| 449 | + public var destinationURL: URL? { | ||
| 450 | + guard case let .downloadedFileMoveFailed(_, _, destination) = self else { return nil } | ||
| 451 | + return destination | ||
| 452 | + } | ||
| 453 | + | ||
| 454 | + #if !(os(Linux) || os(Windows)) | ||
| 455 | + /// The download resume data of any underlying network error. Only produced by `DownloadRequest`s. | ||
| 456 | + public var downloadResumeData: Data? { | ||
| 457 | + (underlyingError as? URLError)?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data | ||
| 458 | + } | ||
| 459 | + #endif | ||
| 460 | +} | ||
| 461 | + | ||
| 462 | +extension AFError.ParameterEncodingFailureReason { | ||
| 463 | + var underlyingError: Error? { | ||
| 464 | + switch self { | ||
| 465 | + case let .jsonEncodingFailed(error), | ||
| 466 | + let .customEncodingFailed(error): | ||
| 467 | + return error | ||
| 468 | + case .missingURL: | ||
| 469 | + return nil | ||
| 470 | + } | ||
| 471 | + } | ||
| 472 | +} | ||
| 473 | + | ||
| 474 | +extension AFError.ParameterEncoderFailureReason { | ||
| 475 | + var underlyingError: Error? { | ||
| 476 | + switch self { | ||
| 477 | + case let .encoderFailed(error): | ||
| 478 | + return error | ||
| 479 | + case .missingRequiredComponent: | ||
| 480 | + return nil | ||
| 481 | + } | ||
| 482 | + } | ||
| 483 | +} | ||
| 484 | + | ||
| 485 | +extension AFError.MultipartEncodingFailureReason { | ||
| 486 | + var url: URL? { | ||
| 487 | + switch self { | ||
| 488 | + case let .bodyPartURLInvalid(url), | ||
| 489 | + let .bodyPartFilenameInvalid(url), | ||
| 490 | + let .bodyPartFileNotReachable(url), | ||
| 491 | + let .bodyPartFileIsDirectory(url), | ||
| 492 | + let .bodyPartFileSizeNotAvailable(url), | ||
| 493 | + let .bodyPartInputStreamCreationFailed(url), | ||
| 494 | + let .outputStreamCreationFailed(url), | ||
| 495 | + let .outputStreamFileAlreadyExists(url), | ||
| 496 | + let .outputStreamURLInvalid(url), | ||
| 497 | + let .bodyPartFileNotReachableWithError(url, _), | ||
| 498 | + let .bodyPartFileSizeQueryFailedWithError(url, _): | ||
| 499 | + return url | ||
| 500 | + case .outputStreamWriteFailed, | ||
| 501 | + .inputStreamReadFailed: | ||
| 502 | + return nil | ||
| 503 | + } | ||
| 504 | + } | ||
| 505 | + | ||
| 506 | + var underlyingError: Error? { | ||
| 507 | + switch self { | ||
| 508 | + case let .bodyPartFileNotReachableWithError(_, error), | ||
| 509 | + let .bodyPartFileSizeQueryFailedWithError(_, error), | ||
| 510 | + let .outputStreamWriteFailed(error), | ||
| 511 | + let .inputStreamReadFailed(error): | ||
| 512 | + return error | ||
| 513 | + case .bodyPartURLInvalid, | ||
| 514 | + .bodyPartFilenameInvalid, | ||
| 515 | + .bodyPartFileNotReachable, | ||
| 516 | + .bodyPartFileIsDirectory, | ||
| 517 | + .bodyPartFileSizeNotAvailable, | ||
| 518 | + .bodyPartInputStreamCreationFailed, | ||
| 519 | + .outputStreamCreationFailed, | ||
| 520 | + .outputStreamFileAlreadyExists, | ||
| 521 | + .outputStreamURLInvalid: | ||
| 522 | + return nil | ||
| 523 | + } | ||
| 524 | + } | ||
| 525 | +} | ||
| 526 | + | ||
| 527 | +extension AFError.ResponseValidationFailureReason { | ||
| 528 | + var acceptableContentTypes: [String]? { | ||
| 529 | + switch self { | ||
| 530 | + case let .missingContentType(types), | ||
| 531 | + let .unacceptableContentType(types, _): | ||
| 532 | + return types | ||
| 533 | + case .dataFileNil, | ||
| 534 | + .dataFileReadFailed, | ||
| 535 | + .unacceptableStatusCode, | ||
| 536 | + .customValidationFailed: | ||
| 537 | + return nil | ||
| 538 | + } | ||
| 539 | + } | ||
| 540 | + | ||
| 541 | + var responseContentType: String? { | ||
| 542 | + switch self { | ||
| 543 | + case let .unacceptableContentType(_, responseType): | ||
| 544 | + return responseType | ||
| 545 | + case .dataFileNil, | ||
| 546 | + .dataFileReadFailed, | ||
| 547 | + .missingContentType, | ||
| 548 | + .unacceptableStatusCode, | ||
| 549 | + .customValidationFailed: | ||
| 550 | + return nil | ||
| 551 | + } | ||
| 552 | + } | ||
| 553 | + | ||
| 554 | + var responseCode: Int? { | ||
| 555 | + switch self { | ||
| 556 | + case let .unacceptableStatusCode(code): | ||
| 557 | + return code | ||
| 558 | + case .dataFileNil, | ||
| 559 | + .dataFileReadFailed, | ||
| 560 | + .missingContentType, | ||
| 561 | + .unacceptableContentType, | ||
| 562 | + .customValidationFailed: | ||
| 563 | + return nil | ||
| 564 | + } | ||
| 565 | + } | ||
| 566 | + | ||
| 567 | + var underlyingError: Error? { | ||
| 568 | + switch self { | ||
| 569 | + case let .customValidationFailed(error): | ||
| 570 | + return error | ||
| 571 | + case .dataFileNil, | ||
| 572 | + .dataFileReadFailed, | ||
| 573 | + .missingContentType, | ||
| 574 | + .unacceptableContentType, | ||
| 575 | + .unacceptableStatusCode: | ||
| 576 | + return nil | ||
| 577 | + } | ||
| 578 | + } | ||
| 579 | +} | ||
| 580 | + | ||
| 581 | +extension AFError.ResponseSerializationFailureReason { | ||
| 582 | + var failedStringEncoding: String.Encoding? { | ||
| 583 | + switch self { | ||
| 584 | + case let .stringSerializationFailed(encoding): | ||
| 585 | + return encoding | ||
| 586 | + case .inputDataNilOrZeroLength, | ||
| 587 | + .inputFileNil, | ||
| 588 | + .inputFileReadFailed(_), | ||
| 589 | + .jsonSerializationFailed(_), | ||
| 590 | + .decodingFailed(_), | ||
| 591 | + .customSerializationFailed(_), | ||
| 592 | + .invalidEmptyResponse: | ||
| 593 | + return nil | ||
| 594 | + } | ||
| 595 | + } | ||
| 596 | + | ||
| 597 | + var underlyingError: Error? { | ||
| 598 | + switch self { | ||
| 599 | + case let .jsonSerializationFailed(error), | ||
| 600 | + let .decodingFailed(error), | ||
| 601 | + let .customSerializationFailed(error): | ||
| 602 | + return error | ||
| 603 | + case .inputDataNilOrZeroLength, | ||
| 604 | + .inputFileNil, | ||
| 605 | + .inputFileReadFailed, | ||
| 606 | + .stringSerializationFailed, | ||
| 607 | + .invalidEmptyResponse: | ||
| 608 | + return nil | ||
| 609 | + } | ||
| 610 | + } | ||
| 611 | +} | ||
| 612 | + | ||
| 613 | +#if !(os(Linux) || os(Windows)) | ||
| 614 | +extension AFError.ServerTrustFailureReason { | ||
| 615 | + var output: AFError.ServerTrustFailureReason.Output? { | ||
| 616 | + switch self { | ||
| 617 | + case let .defaultEvaluationFailed(output), | ||
| 618 | + let .hostValidationFailed(output), | ||
| 619 | + let .revocationCheckFailed(output, _): | ||
| 620 | + return output | ||
| 621 | + case .noRequiredEvaluator, | ||
| 622 | + .noCertificatesFound, | ||
| 623 | + .noPublicKeysFound, | ||
| 624 | + .policyApplicationFailed, | ||
| 625 | + .settingAnchorCertificatesFailed, | ||
| 626 | + .revocationPolicyCreationFailed, | ||
| 627 | + .trustEvaluationFailed, | ||
| 628 | + .certificatePinningFailed, | ||
| 629 | + .publicKeyPinningFailed, | ||
| 630 | + .customEvaluationFailed: | ||
| 631 | + return nil | ||
| 632 | + } | ||
| 633 | + } | ||
| 634 | + | ||
| 635 | + var underlyingError: Error? { | ||
| 636 | + switch self { | ||
| 637 | + case let .customEvaluationFailed(error): | ||
| 638 | + return error | ||
| 639 | + case let .trustEvaluationFailed(error): | ||
| 640 | + return error | ||
| 641 | + case .noRequiredEvaluator, | ||
| 642 | + .noCertificatesFound, | ||
| 643 | + .noPublicKeysFound, | ||
| 644 | + .policyApplicationFailed, | ||
| 645 | + .settingAnchorCertificatesFailed, | ||
| 646 | + .revocationPolicyCreationFailed, | ||
| 647 | + .defaultEvaluationFailed, | ||
| 648 | + .hostValidationFailed, | ||
| 649 | + .revocationCheckFailed, | ||
| 650 | + .certificatePinningFailed, | ||
| 651 | + .publicKeyPinningFailed: | ||
| 652 | + return nil | ||
| 653 | + } | ||
| 654 | + } | ||
| 655 | +} | ||
| 656 | +#endif | ||
| 657 | + | ||
| 658 | +// MARK: - Error Descriptions | ||
| 659 | + | ||
| 660 | +extension AFError: LocalizedError { | ||
| 661 | + public var errorDescription: String? { | ||
| 662 | + switch self { | ||
| 663 | + case .explicitlyCancelled: | ||
| 664 | + return "Request explicitly cancelled." | ||
| 665 | + case let .invalidURL(url): | ||
| 666 | + return "URL is not valid: \(url)" | ||
| 667 | + case let .parameterEncodingFailed(reason): | ||
| 668 | + return reason.localizedDescription | ||
| 669 | + case let .parameterEncoderFailed(reason): | ||
| 670 | + return reason.localizedDescription | ||
| 671 | + case let .multipartEncodingFailed(reason): | ||
| 672 | + return reason.localizedDescription | ||
| 673 | + case let .requestAdaptationFailed(error): | ||
| 674 | + return "Request adaption failed with error: \(error.localizedDescription)" | ||
| 675 | + case let .responseValidationFailed(reason): | ||
| 676 | + return reason.localizedDescription | ||
| 677 | + case let .responseSerializationFailed(reason): | ||
| 678 | + return reason.localizedDescription | ||
| 679 | + case let .requestRetryFailed(retryError, originalError): | ||
| 680 | + return """ | ||
| 681 | + Request retry failed with retry error: \(retryError.localizedDescription), \ | ||
| 682 | + original error: \(originalError.localizedDescription) | ||
| 683 | + """ | ||
| 684 | + case .sessionDeinitialized: | ||
| 685 | + return """ | ||
| 686 | + Session was invalidated without error, so it was likely deinitialized unexpectedly. \ | ||
| 687 | + Be sure to retain a reference to your Session for the duration of your requests. | ||
| 688 | + """ | ||
| 689 | + case let .sessionInvalidated(error): | ||
| 690 | + return "Session was invalidated with error: \(error?.localizedDescription ?? "No description.")" | ||
| 691 | + #if !(os(Linux) || os(Windows)) | ||
| 692 | + case let .serverTrustEvaluationFailed(reason): | ||
| 693 | + return "Server trust evaluation failed due to reason: \(reason.localizedDescription)" | ||
| 694 | + #endif | ||
| 695 | + case let .urlRequestValidationFailed(reason): | ||
| 696 | + return "URLRequest validation failed due to reason: \(reason.localizedDescription)" | ||
| 697 | + case let .createUploadableFailed(error): | ||
| 698 | + return "Uploadable creation failed with error: \(error.localizedDescription)" | ||
| 699 | + case let .createURLRequestFailed(error): | ||
| 700 | + return "URLRequest creation failed with error: \(error.localizedDescription)" | ||
| 701 | + case let .downloadedFileMoveFailed(error, source, destination): | ||
| 702 | + return "Moving downloaded file from: \(source) to: \(destination) failed with error: \(error.localizedDescription)" | ||
| 703 | + case let .sessionTaskFailed(error): | ||
| 704 | + return "URLSessionTask failed with error: \(error.localizedDescription)" | ||
| 705 | + } | ||
| 706 | + } | ||
| 707 | +} | ||
| 708 | + | ||
| 709 | +extension AFError.ParameterEncodingFailureReason { | ||
| 710 | + var localizedDescription: String { | ||
| 711 | + switch self { | ||
| 712 | + case .missingURL: | ||
| 713 | + return "URL request to encode was missing a URL" | ||
| 714 | + case let .jsonEncodingFailed(error): | ||
| 715 | + return "JSON could not be encoded because of error:\n\(error.localizedDescription)" | ||
| 716 | + case let .customEncodingFailed(error): | ||
| 717 | + return "Custom parameter encoder failed with error: \(error.localizedDescription)" | ||
| 718 | + } | ||
| 719 | + } | ||
| 720 | +} | ||
| 721 | + | ||
| 722 | +extension AFError.ParameterEncoderFailureReason { | ||
| 723 | + var localizedDescription: String { | ||
| 724 | + switch self { | ||
| 725 | + case let .missingRequiredComponent(component): | ||
| 726 | + return "Encoding failed due to a missing request component: \(component)" | ||
| 727 | + case let .encoderFailed(error): | ||
| 728 | + return "The underlying encoder failed with the error: \(error)" | ||
| 729 | + } | ||
| 730 | + } | ||
| 731 | +} | ||
| 732 | + | ||
| 733 | +extension AFError.MultipartEncodingFailureReason { | ||
| 734 | + var localizedDescription: String { | ||
| 735 | + switch self { | ||
| 736 | + case let .bodyPartURLInvalid(url): | ||
| 737 | + return "The URL provided is not a file URL: \(url)" | ||
| 738 | + case let .bodyPartFilenameInvalid(url): | ||
| 739 | + return "The URL provided does not have a valid filename: \(url)" | ||
| 740 | + case let .bodyPartFileNotReachable(url): | ||
| 741 | + return "The URL provided is not reachable: \(url)" | ||
| 742 | + case let .bodyPartFileNotReachableWithError(url, error): | ||
| 743 | + return """ | ||
| 744 | + The system returned an error while checking the provided URL for reachability. | ||
| 745 | + URL: \(url) | ||
| 746 | + Error: \(error) | ||
| 747 | + """ | ||
| 748 | + case let .bodyPartFileIsDirectory(url): | ||
| 749 | + return "The URL provided is a directory: \(url)" | ||
| 750 | + case let .bodyPartFileSizeNotAvailable(url): | ||
| 751 | + return "Could not fetch the file size from the provided URL: \(url)" | ||
| 752 | + case let .bodyPartFileSizeQueryFailedWithError(url, error): | ||
| 753 | + return """ | ||
| 754 | + The system returned an error while attempting to fetch the file size from the provided URL. | ||
| 755 | + URL: \(url) | ||
| 756 | + Error: \(error) | ||
| 757 | + """ | ||
| 758 | + case let .bodyPartInputStreamCreationFailed(url): | ||
| 759 | + return "Failed to create an InputStream for the provided URL: \(url)" | ||
| 760 | + case let .outputStreamCreationFailed(url): | ||
| 761 | + return "Failed to create an OutputStream for URL: \(url)" | ||
| 762 | + case let .outputStreamFileAlreadyExists(url): | ||
| 763 | + return "A file already exists at the provided URL: \(url)" | ||
| 764 | + case let .outputStreamURLInvalid(url): | ||
| 765 | + return "The provided OutputStream URL is invalid: \(url)" | ||
| 766 | + case let .outputStreamWriteFailed(error): | ||
| 767 | + return "OutputStream write failed with error: \(error)" | ||
| 768 | + case let .inputStreamReadFailed(error): | ||
| 769 | + return "InputStream read failed with error: \(error)" | ||
| 770 | + } | ||
| 771 | + } | ||
| 772 | +} | ||
| 773 | + | ||
| 774 | +extension AFError.ResponseSerializationFailureReason { | ||
| 775 | + var localizedDescription: String { | ||
| 776 | + switch self { | ||
| 777 | + case .inputDataNilOrZeroLength: | ||
| 778 | + return "Response could not be serialized, input data was nil or zero length." | ||
| 779 | + case .inputFileNil: | ||
| 780 | + return "Response could not be serialized, input file was nil." | ||
| 781 | + case let .inputFileReadFailed(url): | ||
| 782 | + return "Response could not be serialized, input file could not be read: \(url)." | ||
| 783 | + case let .stringSerializationFailed(encoding): | ||
| 784 | + return "String could not be serialized with encoding: \(encoding)." | ||
| 785 | + case let .jsonSerializationFailed(error): | ||
| 786 | + return "JSON could not be serialized because of error:\n\(error.localizedDescription)" | ||
| 787 | + case let .invalidEmptyResponse(type): | ||
| 788 | + return """ | ||
| 789 | + Empty response could not be serialized to type: \(type). \ | ||
| 790 | + Use Empty as the expected type for such responses. | ||
| 791 | + """ | ||
| 792 | + case let .decodingFailed(error): | ||
| 793 | + return "Response could not be decoded because of error:\n\(error.localizedDescription)" | ||
| 794 | + case let .customSerializationFailed(error): | ||
| 795 | + return "Custom response serializer failed with error:\n\(error.localizedDescription)" | ||
| 796 | + } | ||
| 797 | + } | ||
| 798 | +} | ||
| 799 | + | ||
| 800 | +extension AFError.ResponseValidationFailureReason { | ||
| 801 | + var localizedDescription: String { | ||
| 802 | + switch self { | ||
| 803 | + case .dataFileNil: | ||
| 804 | + return "Response could not be validated, data file was nil." | ||
| 805 | + case let .dataFileReadFailed(url): | ||
| 806 | + return "Response could not be validated, data file could not be read: \(url)." | ||
| 807 | + case let .missingContentType(types): | ||
| 808 | + return """ | ||
| 809 | + Response Content-Type was missing and acceptable content types \ | ||
| 810 | + (\(types.joined(separator: ","))) do not match "*/*". | ||
| 811 | + """ | ||
| 812 | + case let .unacceptableContentType(acceptableTypes, responseType): | ||
| 813 | + return """ | ||
| 814 | + Response Content-Type "\(responseType)" does not match any acceptable types: \ | ||
| 815 | + \(acceptableTypes.joined(separator: ",")). | ||
| 816 | + """ | ||
| 817 | + case let .unacceptableStatusCode(code): | ||
| 818 | + return "Response status code was unacceptable: \(code)." | ||
| 819 | + case let .customValidationFailed(error): | ||
| 820 | + return "Custom response validation failed with error: \(error.localizedDescription)" | ||
| 821 | + } | ||
| 822 | + } | ||
| 823 | +} | ||
| 824 | + | ||
| 825 | +#if !(os(Linux) || os(Windows)) | ||
| 826 | +extension AFError.ServerTrustFailureReason { | ||
| 827 | + var localizedDescription: String { | ||
| 828 | + switch self { | ||
| 829 | + case let .noRequiredEvaluator(host): | ||
| 830 | + return "A ServerTrustEvaluating value is required for host \(host) but none was found." | ||
| 831 | + case .noCertificatesFound: | ||
| 832 | + return "No certificates were found or provided for evaluation." | ||
| 833 | + case .noPublicKeysFound: | ||
| 834 | + return "No public keys were found or provided for evaluation." | ||
| 835 | + case .policyApplicationFailed: | ||
| 836 | + return "Attempting to set a SecPolicy failed." | ||
| 837 | + case .settingAnchorCertificatesFailed: | ||
| 838 | + return "Attempting to set the provided certificates as anchor certificates failed." | ||
| 839 | + case .revocationPolicyCreationFailed: | ||
| 840 | + return "Attempting to create a revocation policy failed." | ||
| 841 | + case let .trustEvaluationFailed(error): | ||
| 842 | + return "SecTrust evaluation failed with error: \(error?.localizedDescription ?? "None")" | ||
| 843 | + case let .defaultEvaluationFailed(output): | ||
| 844 | + return "Default evaluation failed for host \(output.host)." | ||
| 845 | + case let .hostValidationFailed(output): | ||
| 846 | + return "Host validation failed for host \(output.host)." | ||
| 847 | + case let .revocationCheckFailed(output, _): | ||
| 848 | + return "Revocation check failed for host \(output.host)." | ||
| 849 | + case let .certificatePinningFailed(host, _, _, _): | ||
| 850 | + return "Certificate pinning failed for host \(host)." | ||
| 851 | + case let .publicKeyPinningFailed(host, _, _, _): | ||
| 852 | + return "Public key pinning failed for host \(host)." | ||
| 853 | + case let .customEvaluationFailed(error): | ||
| 854 | + return "Custom trust evaluation failed with error: \(error.localizedDescription)" | ||
| 855 | + } | ||
| 856 | + } | ||
| 857 | +} | ||
| 858 | +#endif | ||
| 859 | + | ||
| 860 | +extension AFError.URLRequestValidationFailureReason { | ||
| 861 | + var localizedDescription: String { | ||
| 862 | + switch self { | ||
| 863 | + case let .bodyDataInGETRequest(data): | ||
| 864 | + return """ | ||
| 865 | + Invalid URLRequest: Requests with GET method cannot have body data: | ||
| 866 | + \(String(decoding: data, as: UTF8.self)) | ||
| 867 | + """ | ||
| 868 | + } | ||
| 869 | + } | ||
| 870 | +} |
Pods/Alamofire/Source/Alamofire.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/Alamofire.swift | ||
| 1 | +// | ||
| 2 | +// Alamofire.swift | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2014-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 | +import Dispatch | ||
| 26 | +import Foundation | ||
| 27 | +#if canImport(FoundationNetworking) | ||
| 28 | +@_exported import FoundationNetworking | ||
| 29 | +#endif | ||
| 30 | + | ||
| 31 | +// Enforce minimum Swift version for all platforms and build systems. | ||
| 32 | +#if swift(<5.5) | ||
| 33 | +#error("Alamofire doesn't support Swift versions below 5.5.") | ||
| 34 | +#endif | ||
| 35 | + | ||
| 36 | +/// Reference to `Session.default` for quick bootstrapping and examples. | ||
| 37 | +public let AF = Session.default | ||
| 38 | + | ||
| 39 | +/// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate. | ||
| 40 | +let version = "5.7.1" |
Pods/Alamofire/Source/AlamofireExtended.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/AlamofireExtended.swift | ||
| 1 | +// | ||
| 2 | +// AlamofireExtended.swift | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2019 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 | +/// Type that acts as a generic extension point for all `AlamofireExtended` types. | ||
| 26 | +public struct AlamofireExtension<ExtendedType> { | ||
| 27 | + /// Stores the type or meta-type of any extended type. | ||
| 28 | + public private(set) var type: ExtendedType | ||
| 29 | + | ||
| 30 | + /// Create an instance from the provided value. | ||
| 31 | + /// | ||
| 32 | + /// - Parameter type: Instance being extended. | ||
| 33 | + public init(_ type: ExtendedType) { | ||
| 34 | + self.type = type | ||
| 35 | + } | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +/// Protocol describing the `af` extension points for Alamofire extended types. | ||
| 39 | +public protocol AlamofireExtended { | ||
| 40 | + /// Type being extended. | ||
| 41 | + associatedtype ExtendedType | ||
| 42 | + | ||
| 43 | + /// Static Alamofire extension point. | ||
| 44 | + static var af: AlamofireExtension<ExtendedType>.Type { get set } | ||
| 45 | + /// Instance Alamofire extension point. | ||
| 46 | + var af: AlamofireExtension<ExtendedType> { get set } | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +extension AlamofireExtended { | ||
| 50 | + /// Static Alamofire extension point. | ||
| 51 | + public static var af: AlamofireExtension<Self>.Type { | ||
| 52 | + get { AlamofireExtension<Self>.self } | ||
| 53 | + set {} | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + /// Instance Alamofire extension point. | ||
| 57 | + public var af: AlamofireExtension<Self> { | ||
| 58 | + get { AlamofireExtension(self) } | ||
| 59 | + set {} | ||
| 60 | + } | ||
| 61 | +} |
Pods/Alamofire/Source/AuthenticationInterceptor.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/AuthenticationInterceptor.swift | ||
| 1 | +// | ||
| 2 | +// AuthenticationInterceptor.swift | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2020 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 | +import Foundation | ||
| 26 | + | ||
| 27 | +/// Types adopting the `AuthenticationCredential` protocol can be used to authenticate `URLRequest`s. | ||
| 28 | +/// | ||
| 29 | +/// One common example of an `AuthenticationCredential` is an OAuth2 credential containing an access token used to | ||
| 30 | +/// authenticate all requests on behalf of a user. The access token generally has an expiration window of 60 minutes | ||
| 31 | +/// which will then require a refresh of the credential using the refresh token to generate a new access token. | ||
| 32 | +public protocol AuthenticationCredential { | ||
| 33 | + /// Whether the credential requires a refresh. This property should always return `true` when the credential is | ||
| 34 | + /// expired. It is also wise to consider returning `true` when the credential will expire in several seconds or | ||
| 35 | + /// minutes depending on the expiration window of the credential. | ||
| 36 | + /// | ||
| 37 | + /// For example, if the credential is valid for 60 minutes, then it would be wise to return `true` when the | ||
| 38 | + /// credential is only valid for 5 minutes or less. That ensures the credential will not expire as it is passed | ||
| 39 | + /// around backend services. | ||
| 40 | + var requiresRefresh: Bool { get } | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +// MARK: - | ||
| 44 | + | ||
| 45 | +/// Types adopting the `Authenticator` protocol can be used to authenticate `URLRequest`s with an | ||
| 46 | +/// `AuthenticationCredential` as well as refresh the `AuthenticationCredential` when required. | ||
| 47 | +public protocol Authenticator: AnyObject { | ||
| 48 | + /// The type of credential associated with the `Authenticator` instance. | ||
| 49 | + associatedtype Credential: AuthenticationCredential | ||
| 50 | + | ||
| 51 | + /// Applies the `Credential` to the `URLRequest`. | ||
| 52 | + /// | ||
| 53 | + /// In the case of OAuth2, the access token of the `Credential` would be added to the `URLRequest` as a Bearer | ||
| 54 | + /// token to the `Authorization` header. | ||
| 55 | + /// | ||
| 56 | + /// - Parameters: | ||
| 57 | + /// - credential: The `Credential`. | ||
| 58 | + /// - urlRequest: The `URLRequest`. | ||
| 59 | + func apply(_ credential: Credential, to urlRequest: inout URLRequest) | ||
| 60 | + | ||
| 61 | + /// Refreshes the `Credential` and executes the `completion` closure with the `Result` once complete. | ||
| 62 | + /// | ||
| 63 | + /// Refresh can be called in one of two ways. It can be called before the `Request` is actually executed due to | ||
| 64 | + /// a `requiresRefresh` returning `true` during the adapt portion of the `Request` creation process. It can also | ||
| 65 | + /// be triggered by a failed `Request` where the authentication server denied access due to an expired or | ||
| 66 | + /// invalidated access token. | ||
| 67 | + /// | ||
| 68 | + /// In the case of OAuth2, this method would use the refresh token of the `Credential` to generate a new | ||
| 69 | + /// `Credential` using the authentication service. Once complete, the `completion` closure should be called with | ||
| 70 | + /// the new `Credential`, or the error that occurred. | ||
| 71 | + /// | ||
| 72 | + /// In general, if the refresh call fails with certain status codes from the authentication server (commonly a 401), | ||
| 73 | + /// the refresh token in the `Credential` can no longer be used to generate a valid `Credential`. In these cases, | ||
| 74 | + /// you will need to reauthenticate the user with their username / password. | ||
| 75 | + /// | ||
| 76 | + /// Please note, these are just general examples of common use cases. They are not meant to solve your specific | ||
| 77 | + /// authentication server challenges. Please work with your authentication server team to ensure your | ||
| 78 | + /// `Authenticator` logic matches their expectations. | ||
| 79 | + /// | ||
| 80 | + /// - Parameters: | ||
| 81 | + /// - credential: The `Credential` to refresh. | ||
| 82 | + /// - session: The `Session` requiring the refresh. | ||
| 83 | + /// - completion: The closure to be executed once the refresh is complete. | ||
| 84 | + func refresh(_ credential: Credential, for session: Session, completion: @escaping (Result<Credential, Error>) -> Void) | ||
| 85 | + | ||
| 86 | + /// Determines whether the `URLRequest` failed due to an authentication error based on the `HTTPURLResponse`. | ||
| 87 | + /// | ||
| 88 | + /// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `false` | ||
| 89 | + /// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then you | ||
| 90 | + /// will need to work with your authentication server team to understand how to identify when this occurs. | ||
| 91 | + /// | ||
| 92 | + /// In the case of OAuth2, where an authentication server can invalidate credentials, you will need to inspect the | ||
| 93 | + /// `HTTPURLResponse` or possibly the `Error` for when this occurs. This is commonly handled by the authentication | ||
| 94 | + /// server returning a 401 status code and some additional header to indicate an OAuth2 failure occurred. | ||
| 95 | + /// | ||
| 96 | + /// It is very important to understand how your authentication server works to be able to implement this correctly. | ||
| 97 | + /// For example, if your authentication server returns a 401 when an OAuth2 error occurs, and your downstream | ||
| 98 | + /// service also returns a 401 when you are not authorized to perform that operation, how do you know which layer | ||
| 99 | + /// of the backend returned you a 401? You do not want to trigger a refresh unless you know your authentication | ||
| 100 | + /// server is actually the layer rejecting the request. Again, work with your authentication server team to understand | ||
| 101 | + /// how to identify an OAuth2 401 error vs. a downstream 401 error to avoid endless refresh loops. | ||
| 102 | + /// | ||
| 103 | + /// - Parameters: | ||
| 104 | + /// - urlRequest: The `URLRequest`. | ||
| 105 | + /// - response: The `HTTPURLResponse`. | ||
| 106 | + /// - error: The `Error`. | ||
| 107 | + /// | ||
| 108 | + /// - Returns: `true` if the `URLRequest` failed due to an authentication error, `false` otherwise. | ||
| 109 | + func didRequest(_ urlRequest: URLRequest, with response: HTTPURLResponse, failDueToAuthenticationError error: Error) -> Bool | ||
| 110 | + | ||
| 111 | + /// Determines whether the `URLRequest` is authenticated with the `Credential`. | ||
| 112 | + /// | ||
| 113 | + /// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `true` | ||
| 114 | + /// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then | ||
| 115 | + /// read on. | ||
| 116 | + /// | ||
| 117 | + /// When an authentication server can invalidate credentials, it means that you may have a non-expired credential | ||
| 118 | + /// that appears to be valid, but will be rejected by the authentication server when used. Generally when this | ||
| 119 | + /// happens, a number of requests are all sent when the application is foregrounded, and all of them will be | ||
| 120 | + /// rejected by the authentication server in the order they are received. The first failed request will trigger a | ||
| 121 | + /// refresh internally, which will update the credential, and then retry all the queued requests with the new | ||
| 122 | + /// credential. However, it is possible that some of the original requests will not return from the authentication | ||
| 123 | + /// server until the refresh has completed. This is where this method comes in. | ||
| 124 | + /// | ||
| 125 | + /// When the authentication server rejects a credential, we need to check to make sure we haven't refreshed the | ||
| 126 | + /// credential while the request was in flight. If it has already refreshed, then we don't need to trigger an | ||
| 127 | + /// additional refresh. If it hasn't refreshed, then we need to refresh. | ||
| 128 | + /// | ||
| 129 | + /// Now that it is understood how the result of this method is used in the refresh lifecyle, let's walk through how | ||
| 130 | + /// to implement it. You should return `true` in this method if the `URLRequest` is authenticated in a way that | ||
| 131 | + /// matches the values in the `Credential`. In the case of OAuth2, this would mean that the Bearer token in the | ||
| 132 | + /// `Authorization` header of the `URLRequest` matches the access token in the `Credential`. If it matches, then we | ||
| 133 | + /// know the `Credential` was used to authenticate the `URLRequest` and should return `true`. If the Bearer token | ||
| 134 | + /// did not match the access token, then you should return `false`. | ||
| 135 | + /// | ||
| 136 | + /// - Parameters: | ||
| 137 | + /// - urlRequest: The `URLRequest`. | ||
| 138 | + /// - credential: The `Credential`. | ||
| 139 | + /// | ||
| 140 | + /// - Returns: `true` if the `URLRequest` is authenticated with the `Credential`, `false` otherwise. | ||
| 141 | + func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: Credential) -> Bool | ||
| 142 | +} | ||
| 143 | + | ||
| 144 | +// MARK: - | ||
| 145 | + | ||
| 146 | +/// Represents various authentication failures that occur when using the `AuthenticationInterceptor`. All errors are | ||
| 147 | +/// still vended from Alamofire as `AFError` types. The `AuthenticationError` instances will be embedded within | ||
| 148 | +/// `AFError` `.requestAdaptationFailed` or `.requestRetryFailed` cases. | ||
| 149 | +public enum AuthenticationError: Error { | ||
| 150 | + /// The credential was missing so the request could not be authenticated. | ||
| 151 | + case missingCredential | ||
| 152 | + /// The credential was refreshed too many times within the `RefreshWindow`. | ||
| 153 | + case excessiveRefresh | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +// MARK: - | ||
| 157 | + | ||
| 158 | +/// The `AuthenticationInterceptor` class manages the queuing and threading complexity of authenticating requests. | ||
| 159 | +/// It relies on an `Authenticator` type to handle the actual `URLRequest` authentication and `Credential` refresh. | ||
| 160 | +public class AuthenticationInterceptor<AuthenticatorType>: RequestInterceptor where AuthenticatorType: Authenticator { | ||
| 161 | + // MARK: Typealiases | ||
| 162 | + | ||
| 163 | + /// Type of credential used to authenticate requests. | ||
| 164 | + public typealias Credential = AuthenticatorType.Credential | ||
| 165 | + | ||
| 166 | + // MARK: Helper Types | ||
| 167 | + | ||
| 168 | + /// Type that defines a time window used to identify excessive refresh calls. When enabled, prior to executing a | ||
| 169 | + /// refresh, the `AuthenticationInterceptor` compares the timestamp history of previous refresh calls against the | ||
| 170 | + /// `RefreshWindow`. If more refreshes have occurred within the refresh window than allowed, the refresh is | ||
| 171 | + /// cancelled and an `AuthorizationError.excessiveRefresh` error is thrown. | ||
| 172 | + public struct RefreshWindow { | ||
| 173 | + /// `TimeInterval` defining the duration of the time window before the current time in which the number of | ||
| 174 | + /// refresh attempts is compared against `maximumAttempts`. For example, if `interval` is 30 seconds, then the | ||
| 175 | + /// `RefreshWindow` represents the past 30 seconds. If more attempts occurred in the past 30 seconds than | ||
| 176 | + /// `maximumAttempts`, an `.excessiveRefresh` error will be thrown. | ||
| 177 | + public let interval: TimeInterval | ||
| 178 | + | ||
| 179 | + /// Total refresh attempts allowed within `interval` before throwing an `.excessiveRefresh` error. | ||
| 180 | + public let maximumAttempts: Int | ||
| 181 | + | ||
| 182 | + /// Creates a `RefreshWindow` instance from the specified `interval` and `maximumAttempts`. | ||
| 183 | + /// | ||
| 184 | + /// - Parameters: | ||
| 185 | + /// - interval: `TimeInterval` defining the duration of the time window before the current time. | ||
| 186 | + /// - maximumAttempts: The maximum attempts allowed within the `TimeInterval`. | ||
| 187 | + public init(interval: TimeInterval = 30.0, maximumAttempts: Int = 5) { | ||
| 188 | + self.interval = interval | ||
| 189 | + self.maximumAttempts = maximumAttempts | ||
| 190 | + } | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + private struct AdaptOperation { | ||
| 194 | + let urlRequest: URLRequest | ||
| 195 | + let session: Session | ||
| 196 | + let completion: (Result<URLRequest, Error>) -> Void | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + private enum AdaptResult { | ||
| 200 | + case adapt(Credential) | ||
| 201 | + case doNotAdapt(AuthenticationError) | ||
| 202 | + case adaptDeferred | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + private struct MutableState { | ||
| 206 | + var credential: Credential? | ||
| 207 | + | ||
| 208 | + var isRefreshing = false | ||
| 209 | + var refreshTimestamps: [TimeInterval] = [] | ||
| 210 | + var refreshWindow: RefreshWindow? | ||
| 211 | + | ||
| 212 | + var adaptOperations: [AdaptOperation] = [] | ||
| 213 | + var requestsToRetry: [(RetryResult) -> Void] = [] | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + // MARK: Properties | ||
| 217 | + | ||
| 218 | + /// The `Credential` used to authenticate requests. | ||
| 219 | + public var credential: Credential? { | ||
| 220 | + get { $mutableState.credential } | ||
| 221 | + set { $mutableState.credential = newValue } | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + let authenticator: AuthenticatorType | ||
| 225 | + let queue = DispatchQueue(label: "org.alamofire.authentication.inspector") | ||
| 226 | + | ||
| 227 | + @Protected | ||
| 228 | + private var mutableState: MutableState | ||
| 229 | + | ||
| 230 | + // MARK: Initialization | ||
| 231 | + | ||
| 232 | + /// Creates an `AuthenticationInterceptor` instance from the specified parameters. | ||
| 233 | + /// | ||
| 234 | + /// A `nil` `RefreshWindow` will result in the `AuthenticationInterceptor` not checking for excessive refresh calls. | ||
| 235 | + /// It is recommended to always use a `RefreshWindow` to avoid endless refresh cycles. | ||
| 236 | + /// | ||
| 237 | + /// - Parameters: | ||
| 238 | + /// - authenticator: The `Authenticator` type. | ||
| 239 | + /// - credential: The `Credential` if it exists. `nil` by default. | ||
| 240 | + /// - refreshWindow: The `RefreshWindow` used to identify excessive refresh calls. `RefreshWindow()` by default. | ||
| 241 | + public init(authenticator: AuthenticatorType, | ||
| 242 | + credential: Credential? = nil, | ||
| 243 | + refreshWindow: RefreshWindow? = RefreshWindow()) { | ||
| 244 | + self.authenticator = authenticator | ||
| 245 | + mutableState = MutableState(credential: credential, refreshWindow: refreshWindow) | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + // MARK: Adapt | ||
| 249 | + | ||
| 250 | + public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) { | ||
| 251 | + let adaptResult: AdaptResult = $mutableState.write { mutableState in | ||
| 252 | + // Queue the adapt operation if a refresh is already in place. | ||
| 253 | + guard !mutableState.isRefreshing else { | ||
| 254 | + let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion) | ||
| 255 | + mutableState.adaptOperations.append(operation) | ||
| 256 | + return .adaptDeferred | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + // Throw missing credential error is the credential is missing. | ||
| 260 | + guard let credential = mutableState.credential else { | ||
| 261 | + let error = AuthenticationError.missingCredential | ||
| 262 | + return .doNotAdapt(error) | ||
| 263 | + } | ||
| 264 | + | ||
| 265 | + // Queue the adapt operation and trigger refresh operation if credential requires refresh. | ||
| 266 | + guard !credential.requiresRefresh else { | ||
| 267 | + let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion) | ||
| 268 | + mutableState.adaptOperations.append(operation) | ||
| 269 | + refresh(credential, for: session, insideLock: &mutableState) | ||
| 270 | + return .adaptDeferred | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + return .adapt(credential) | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + switch adaptResult { | ||
| 277 | + case let .adapt(credential): | ||
| 278 | + var authenticatedRequest = urlRequest | ||
| 279 | + authenticator.apply(credential, to: &authenticatedRequest) | ||
| 280 | + completion(.success(authenticatedRequest)) | ||
| 281 | + | ||
| 282 | + case let .doNotAdapt(adaptError): | ||
| 283 | + completion(.failure(adaptError)) | ||
| 284 | + | ||
| 285 | + case .adaptDeferred: | ||
| 286 | + // No-op: adapt operation captured during refresh. | ||
| 287 | + break | ||
| 288 | + } | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + // MARK: Retry | ||
| 292 | + | ||
| 293 | + public func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { | ||
| 294 | + // Do not attempt retry if there was not an original request and response from the server. | ||
| 295 | + guard let urlRequest = request.request, let response = request.response else { | ||
| 296 | + completion(.doNotRetry) | ||
| 297 | + return | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + // Do not attempt retry unless the `Authenticator` verifies failure was due to authentication error (i.e. 401 status code). | ||
| 301 | + guard authenticator.didRequest(urlRequest, with: response, failDueToAuthenticationError: error) else { | ||
| 302 | + completion(.doNotRetry) | ||
| 303 | + return | ||
| 304 | + } | ||
| 305 | + | ||
| 306 | + // Do not attempt retry if there is no credential. | ||
| 307 | + guard let credential = credential else { | ||
| 308 | + let error = AuthenticationError.missingCredential | ||
| 309 | + completion(.doNotRetryWithError(error)) | ||
| 310 | + return | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + // Retry the request if the `Authenticator` verifies it was authenticated with a previous credential. | ||
| 314 | + guard authenticator.isRequest(urlRequest, authenticatedWith: credential) else { | ||
| 315 | + completion(.retry) | ||
| 316 | + return | ||
| 317 | + } | ||
| 318 | + | ||
| 319 | + $mutableState.write { mutableState in | ||
| 320 | + mutableState.requestsToRetry.append(completion) | ||
| 321 | + | ||
| 322 | + guard !mutableState.isRefreshing else { return } | ||
| 323 | + | ||
| 324 | + refresh(credential, for: session, insideLock: &mutableState) | ||
| 325 | + } | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + // MARK: Refresh | ||
| 329 | + | ||
| 330 | + private func refresh(_ credential: Credential, for session: Session, insideLock mutableState: inout MutableState) { | ||
| 331 | + guard !isRefreshExcessive(insideLock: &mutableState) else { | ||
| 332 | + let error = AuthenticationError.excessiveRefresh | ||
| 333 | + handleRefreshFailure(error, insideLock: &mutableState) | ||
| 334 | + return | ||
| 335 | + } | ||
| 336 | + | ||
| 337 | + mutableState.refreshTimestamps.append(ProcessInfo.processInfo.systemUptime) | ||
| 338 | + mutableState.isRefreshing = true | ||
| 339 | + | ||
| 340 | + // Dispatch to queue to hop out of the lock in case authenticator.refresh is implemented synchronously. | ||
| 341 | + queue.async { | ||
| 342 | + self.authenticator.refresh(credential, for: session) { result in | ||
| 343 | + self.$mutableState.write { mutableState in | ||
| 344 | + switch result { | ||
| 345 | + case let .success(credential): | ||
| 346 | + self.handleRefreshSuccess(credential, insideLock: &mutableState) | ||
| 347 | + case let .failure(error): | ||
| 348 | + self.handleRefreshFailure(error, insideLock: &mutableState) | ||
| 349 | + } | ||
| 350 | + } | ||
| 351 | + } | ||
| 352 | + } | ||
| 353 | + } | ||
| 354 | + | ||
| 355 | + private func isRefreshExcessive(insideLock mutableState: inout MutableState) -> Bool { | ||
| 356 | + guard let refreshWindow = mutableState.refreshWindow else { return false } | ||
| 357 | + | ||
| 358 | + let refreshWindowMin = ProcessInfo.processInfo.systemUptime - refreshWindow.interval | ||
| 359 | + | ||
| 360 | + let refreshAttemptsWithinWindow = mutableState.refreshTimestamps.reduce(into: 0) { attempts, refreshTimestamp in | ||
| 361 | + guard refreshWindowMin <= refreshTimestamp else { return } | ||
| 362 | + attempts += 1 | ||
| 363 | + } | ||
| 364 | + | ||
| 365 | + let isRefreshExcessive = refreshAttemptsWithinWindow >= refreshWindow.maximumAttempts | ||
| 366 | + | ||
| 367 | + return isRefreshExcessive | ||
| 368 | + } | ||
| 369 | + | ||
| 370 | + private func handleRefreshSuccess(_ credential: Credential, insideLock mutableState: inout MutableState) { | ||
| 371 | + mutableState.credential = credential | ||
| 372 | + | ||
| 373 | + let adaptOperations = mutableState.adaptOperations | ||
| 374 | + let requestsToRetry = mutableState.requestsToRetry | ||
| 375 | + | ||
| 376 | + mutableState.adaptOperations.removeAll() | ||
| 377 | + mutableState.requestsToRetry.removeAll() | ||
| 378 | + | ||
| 379 | + mutableState.isRefreshing = false | ||
| 380 | + | ||
| 381 | + // Dispatch to queue to hop out of the mutable state lock | ||
| 382 | + queue.async { | ||
| 383 | + adaptOperations.forEach { self.adapt($0.urlRequest, for: $0.session, completion: $0.completion) } | ||
| 384 | + requestsToRetry.forEach { $0(.retry) } | ||
| 385 | + } | ||
| 386 | + } | ||
| 387 | + | ||
| 388 | + private func handleRefreshFailure(_ error: Error, insideLock mutableState: inout MutableState) { | ||
| 389 | + let adaptOperations = mutableState.adaptOperations | ||
| 390 | + let requestsToRetry = mutableState.requestsToRetry | ||
| 391 | + | ||
| 392 | + mutableState.adaptOperations.removeAll() | ||
| 393 | + mutableState.requestsToRetry.removeAll() | ||
| 394 | + | ||
| 395 | + mutableState.isRefreshing = false | ||
| 396 | + | ||
| 397 | + // Dispatch to queue to hop out of the mutable state lock | ||
| 398 | + queue.async { | ||
| 399 | + adaptOperations.forEach { $0.completion(.failure(error)) } | ||
| 400 | + requestsToRetry.forEach { $0(.doNotRetryWithError(error)) } | ||
| 401 | + } | ||
| 402 | + } | ||
| 403 | +} |
Pods/Alamofire/Source/CachedResponseHandler.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/CachedResponseHandler.swift | ||
| 1 | +// | ||
| 2 | +// CachedResponseHandler.swift | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2019 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 | +import Foundation | ||
| 26 | + | ||
| 27 | +/// A type that handles whether the data task should store the HTTP response in the cache. | ||
| 28 | +public protocol CachedResponseHandler { | ||
| 29 | + /// Determines whether the HTTP response should be stored in the cache. | ||
| 30 | + /// | ||
| 31 | + /// The `completion` closure should be passed one of three possible options: | ||
| 32 | + /// | ||
| 33 | + /// 1. The cached response provided by the server (this is the most common use case). | ||
| 34 | + /// 2. A modified version of the cached response (you may want to modify it in some way before caching). | ||
| 35 | + /// 3. A `nil` value to prevent the cached response from being stored in the cache. | ||
| 36 | + /// | ||
| 37 | + /// - Parameters: | ||
| 38 | + /// - task: The data task whose request resulted in the cached response. | ||
| 39 | + /// - response: The cached response to potentially store in the cache. | ||
| 40 | + /// - completion: The closure to execute containing cached response, a modified response, or `nil`. | ||
| 41 | + func dataTask(_ task: URLSessionDataTask, | ||
| 42 | + willCacheResponse response: CachedURLResponse, | ||
| 43 | + completion: @escaping (CachedURLResponse?) -> Void) | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +// MARK: - | ||
| 47 | + | ||
| 48 | +/// `ResponseCacher` is a convenience `CachedResponseHandler` making it easy to cache, not cache, or modify a cached | ||
| 49 | +/// response. | ||
| 50 | +public struct ResponseCacher { | ||
| 51 | + /// Defines the behavior of the `ResponseCacher` type. | ||
| 52 | + public enum Behavior { | ||
| 53 | + /// Stores the cached response in the cache. | ||
| 54 | + case cache | ||
| 55 | + /// Prevents the cached response from being stored in the cache. | ||
| 56 | + case doNotCache | ||
| 57 | + /// Modifies the cached response before storing it in the cache. | ||
| 58 | + case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?) | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /// Returns a `ResponseCacher` with a `.cache` `Behavior`. | ||
| 62 | + public static let cache = ResponseCacher(behavior: .cache) | ||
| 63 | + /// Returns a `ResponseCacher` with a `.doNotCache` `Behavior`. | ||
| 64 | + public static let doNotCache = ResponseCacher(behavior: .doNotCache) | ||
| 65 | + | ||
| 66 | + /// The `Behavior` of the `ResponseCacher`. | ||
| 67 | + public let behavior: Behavior | ||
| 68 | + | ||
| 69 | + /// Creates a `ResponseCacher` instance from the `Behavior`. | ||
| 70 | + /// | ||
| 71 | + /// - Parameter behavior: The `Behavior`. | ||
| 72 | + public init(behavior: Behavior) { | ||
| 73 | + self.behavior = behavior | ||
| 74 | + } | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +extension ResponseCacher: CachedResponseHandler { | ||
| 78 | + public func dataTask(_ task: URLSessionDataTask, | ||
| 79 | + willCacheResponse response: CachedURLResponse, | ||
| 80 | + completion: @escaping (CachedURLResponse?) -> Void) { | ||
| 81 | + switch behavior { | ||
| 82 | + case .cache: | ||
| 83 | + completion(response) | ||
| 84 | + case .doNotCache: | ||
| 85 | + completion(nil) | ||
| 86 | + case let .modify(closure): | ||
| 87 | + let response = closure(task, response) | ||
| 88 | + completion(response) | ||
| 89 | + } | ||
| 90 | + } | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +extension CachedResponseHandler where Self == ResponseCacher { | ||
| 94 | + /// Provides a `ResponseCacher` which caches the response, if allowed. Equivalent to `ResponseCacher.cache`. | ||
| 95 | + public static var cache: ResponseCacher { .cache } | ||
| 96 | + | ||
| 97 | + /// Provides a `ResponseCacher` which does not cache the response. Equivalent to `ResponseCacher.doNotCache`. | ||
| 98 | + public static var doNotCache: ResponseCacher { .doNotCache } | ||
| 99 | + | ||
| 100 | + /// Creates a `ResponseCacher` which modifies the proposed `CachedURLResponse` using the provided closure. | ||
| 101 | + /// | ||
| 102 | + /// - Parameter closure: Closure used to modify the `CachedURLResponse`. | ||
| 103 | + /// - Returns: The `ResponseCacher`. | ||
| 104 | + public static func modify(using closure: @escaping ((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)) -> ResponseCacher { | ||
| 105 | + ResponseCacher(behavior: .modify(closure)) | ||
| 106 | + } | ||
| 107 | +} |
Pods/Alamofire/Source/Combine.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/Combine.swift | ||
| 1 | +// | ||
| 2 | +// Combine.swift | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2020 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 !((os(iOS) && (arch(i386) || arch(arm))) || os(Windows) || os(Linux)) | ||
| 26 | + | ||
| 27 | +import Combine | ||
| 28 | +import Dispatch | ||
| 29 | +import Foundation | ||
| 30 | + | ||
| 31 | +// MARK: - DataRequest / UploadRequest | ||
| 32 | + | ||
| 33 | +/// A Combine `Publisher` that publishes the `DataResponse<Value, AFError>` of the provided `DataRequest`. | ||
| 34 | +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 35 | +public struct DataResponsePublisher<Value>: Publisher { | ||
| 36 | + public typealias Output = DataResponse<Value, AFError> | ||
| 37 | + public typealias Failure = Never | ||
| 38 | + | ||
| 39 | + private typealias Handler = (@escaping (_ response: DataResponse<Value, AFError>) -> Void) -> DataRequest | ||
| 40 | + | ||
| 41 | + private let request: DataRequest | ||
| 42 | + private let responseHandler: Handler | ||
| 43 | + | ||
| 44 | + /// Creates an instance which will serialize responses using the provided `ResponseSerializer`. | ||
| 45 | + /// | ||
| 46 | + /// - Parameters: | ||
| 47 | + /// - request: `DataRequest` for which to publish the response. | ||
| 48 | + /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. | ||
| 49 | + /// - serializer: `ResponseSerializer` used to produce the published `DataResponse`. | ||
| 50 | + public init<Serializer: ResponseSerializer>(_ request: DataRequest, queue: DispatchQueue, serializer: Serializer) | ||
| 51 | + where Value == Serializer.SerializedObject { | ||
| 52 | + self.request = request | ||
| 53 | + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + /// Creates an instance which will serialize responses using the provided `DataResponseSerializerProtocol`. | ||
| 57 | + /// | ||
| 58 | + /// - Parameters: | ||
| 59 | + /// - request: `DataRequest` for which to publish the response. | ||
| 60 | + /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. | ||
| 61 | + /// - serializer: `DataResponseSerializerProtocol` used to produce the published `DataResponse`. | ||
| 62 | + public init<Serializer: DataResponseSerializerProtocol>(_ request: DataRequest, | ||
| 63 | + queue: DispatchQueue, | ||
| 64 | + serializer: Serializer) | ||
| 65 | + where Value == Serializer.SerializedObject { | ||
| 66 | + self.request = request | ||
| 67 | + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + /// Publishes only the `Result` of the `DataResponse` value. | ||
| 71 | + /// | ||
| 72 | + /// - Returns: The `AnyPublisher` publishing the `Result<Value, AFError>` value. | ||
| 73 | + public func result() -> AnyPublisher<Result<Value, AFError>, Never> { | ||
| 74 | + map(\.result).eraseToAnyPublisher() | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + /// Publishes the `Result` of the `DataResponse` as a single `Value` or fail with the `AFError` instance. | ||
| 78 | + /// | ||
| 79 | + /// - Returns: The `AnyPublisher<Value, AFError>` publishing the stream. | ||
| 80 | + public func value() -> AnyPublisher<Value, AFError> { | ||
| 81 | + setFailureType(to: AFError.self).flatMap(\.result.publisher).eraseToAnyPublisher() | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + public func receive<S>(subscriber: S) where S: Subscriber, DataResponsePublisher.Failure == S.Failure, DataResponsePublisher.Output == S.Input { | ||
| 85 | + subscriber.receive(subscription: Inner(request: request, | ||
| 86 | + responseHandler: responseHandler, | ||
| 87 | + downstream: subscriber)) | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + private final class Inner<Downstream: Subscriber>: Subscription | ||
| 91 | + where Downstream.Input == Output { | ||
| 92 | + typealias Failure = Downstream.Failure | ||
| 93 | + | ||
| 94 | + @Protected | ||
| 95 | + private var downstream: Downstream? | ||
| 96 | + private let request: DataRequest | ||
| 97 | + private let responseHandler: Handler | ||
| 98 | + | ||
| 99 | + init(request: DataRequest, responseHandler: @escaping Handler, downstream: Downstream) { | ||
| 100 | + self.request = request | ||
| 101 | + self.responseHandler = responseHandler | ||
| 102 | + self.downstream = downstream | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + func request(_ demand: Subscribers.Demand) { | ||
| 106 | + assert(demand > 0) | ||
| 107 | + | ||
| 108 | + guard let downstream = downstream else { return } | ||
| 109 | + | ||
| 110 | + self.downstream = nil | ||
| 111 | + responseHandler { response in | ||
| 112 | + _ = downstream.receive(response) | ||
| 113 | + downstream.receive(completion: .finished) | ||
| 114 | + }.resume() | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + func cancel() { | ||
| 118 | + request.cancel() | ||
| 119 | + downstream = nil | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 125 | +extension DataResponsePublisher where Value == Data? { | ||
| 126 | + /// Creates an instance which publishes a `DataResponse<Data?, AFError>` value without serialization. | ||
| 127 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 128 | + public init(_ request: DataRequest, queue: DispatchQueue) { | ||
| 129 | + self.request = request | ||
| 130 | + responseHandler = { request.response(queue: queue, completionHandler: $0) } | ||
| 131 | + } | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +extension DataRequest { | ||
| 135 | + /// Creates a `DataResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`. | ||
| 136 | + /// | ||
| 137 | + /// - Parameters: | ||
| 138 | + /// - serializer: `ResponseSerializer` used to serialize response `Data`. | ||
| 139 | + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. | ||
| 140 | + /// | ||
| 141 | + /// - Returns: The `DataResponsePublisher`. | ||
| 142 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 143 | + public func publishResponse<Serializer: ResponseSerializer, T>(using serializer: Serializer, on queue: DispatchQueue = .main) -> DataResponsePublisher<T> | ||
| 144 | + where Serializer.SerializedObject == T { | ||
| 145 | + DataResponsePublisher(self, queue: queue, serializer: serializer) | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + /// Creates a `DataResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the | ||
| 149 | + /// response. | ||
| 150 | + /// | ||
| 151 | + /// - Parameters: | ||
| 152 | + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. | ||
| 153 | + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` | ||
| 154 | + /// by default. | ||
| 155 | + /// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by | ||
| 156 | + /// default. | ||
| 157 | + /// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of | ||
| 158 | + /// status code. `[.head]` by default. | ||
| 159 | + /// - Returns: The `DataResponsePublisher`. | ||
| 160 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 161 | + public func publishData(queue: DispatchQueue = .main, | ||
| 162 | + preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, | ||
| 163 | + emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes, | ||
| 164 | + emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher<Data> { | ||
| 165 | + publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor, | ||
| 166 | + emptyResponseCodes: emptyResponseCodes, | ||
| 167 | + emptyRequestMethods: emptyRequestMethods), | ||
| 168 | + on: queue) | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + /// Creates a `DataResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the | ||
| 172 | + /// response. | ||
| 173 | + /// | ||
| 174 | + /// - Parameters: | ||
| 175 | + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. | ||
| 176 | + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` | ||
| 177 | + /// by default. | ||
| 178 | + /// - encoding: `String.Encoding` to parse the response. `nil` by default, in which case the encoding | ||
| 179 | + /// will be determined by the server response, falling back to the default HTTP character | ||
| 180 | + /// set, `ISO-8859-1`. | ||
| 181 | + /// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by | ||
| 182 | + /// default. | ||
| 183 | + /// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of | ||
| 184 | + /// status code. `[.head]` by default. | ||
| 185 | + /// | ||
| 186 | + /// - Returns: The `DataResponsePublisher`. | ||
| 187 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 188 | + public func publishString(queue: DispatchQueue = .main, | ||
| 189 | + preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, | ||
| 190 | + encoding: String.Encoding? = nil, | ||
| 191 | + emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes, | ||
| 192 | + emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher<String> { | ||
| 193 | + publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor, | ||
| 194 | + encoding: encoding, | ||
| 195 | + emptyResponseCodes: emptyResponseCodes, | ||
| 196 | + emptyRequestMethods: emptyRequestMethods), | ||
| 197 | + on: queue) | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + @_disfavoredOverload | ||
| 201 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 202 | + @available(*, deprecated, message: "Renamed publishDecodable(type:queue:preprocessor:decoder:emptyResponseCodes:emptyRequestMethods).") | ||
| 203 | + public func publishDecodable<T: Decodable>(type: T.Type = T.self, | ||
| 204 | + queue: DispatchQueue = .main, | ||
| 205 | + preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor, | ||
| 206 | + decoder: DataDecoder = JSONDecoder(), | ||
| 207 | + emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes, | ||
| 208 | + emptyResponseMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DataResponsePublisher<T> { | ||
| 209 | + publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, | ||
| 210 | + decoder: decoder, | ||
| 211 | + emptyResponseCodes: emptyResponseCodes, | ||
| 212 | + emptyRequestMethods: emptyResponseMethods), | ||
| 213 | + on: queue) | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + /// Creates a `DataResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize the | ||
| 217 | + /// response. | ||
| 218 | + /// | ||
| 219 | + /// - Parameters: | ||
| 220 | + /// - type: `Decodable` type to which to decode response `Data`. Inferred from the context by | ||
| 221 | + /// default. | ||
| 222 | + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. | ||
| 223 | + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. | ||
| 224 | + /// `PassthroughPreprocessor()` by default. | ||
| 225 | + /// - decoder: `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default. | ||
| 226 | + /// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by | ||
| 227 | + /// default. | ||
| 228 | + /// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of | ||
| 229 | + /// status code. `[.head]` by default. | ||
| 230 | + /// | ||
| 231 | + /// - Returns: The `DataResponsePublisher`. | ||
| 232 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 233 | + public func publishDecodable<T: Decodable>(type: T.Type = T.self, | ||
| 234 | + queue: DispatchQueue = .main, | ||
| 235 | + preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor, | ||
| 236 | + decoder: DataDecoder = JSONDecoder(), | ||
| 237 | + emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes, | ||
| 238 | + emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DataResponsePublisher<T> { | ||
| 239 | + publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, | ||
| 240 | + decoder: decoder, | ||
| 241 | + emptyResponseCodes: emptyResponseCodes, | ||
| 242 | + emptyRequestMethods: emptyRequestMethods), | ||
| 243 | + on: queue) | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + /// Creates a `DataResponsePublisher` for this instance which does not serialize the response before publishing. | ||
| 247 | + /// | ||
| 248 | + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. | ||
| 249 | + /// | ||
| 250 | + /// - Returns: The `DataResponsePublisher`. | ||
| 251 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 252 | + public func publishUnserialized(queue: DispatchQueue = .main) -> DataResponsePublisher<Data?> { | ||
| 253 | + DataResponsePublisher(self, queue: queue) | ||
| 254 | + } | ||
| 255 | +} | ||
| 256 | + | ||
| 257 | +// A Combine `Publisher` that publishes a sequence of `Stream<Value, AFError>` values received by the provided `DataStreamRequest`. | ||
| 258 | +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 259 | +public struct DataStreamPublisher<Value>: Publisher { | ||
| 260 | + public typealias Output = DataStreamRequest.Stream<Value, AFError> | ||
| 261 | + public typealias Failure = Never | ||
| 262 | + | ||
| 263 | + private typealias Handler = (@escaping DataStreamRequest.Handler<Value, AFError>) -> DataStreamRequest | ||
| 264 | + | ||
| 265 | + private let request: DataStreamRequest | ||
| 266 | + private let streamHandler: Handler | ||
| 267 | + | ||
| 268 | + /// Creates an instance which will serialize responses using the provided `DataStreamSerializer`. | ||
| 269 | + /// | ||
| 270 | + /// - Parameters: | ||
| 271 | + /// - request: `DataStreamRequest` for which to publish the response. | ||
| 272 | + /// - queue: `DispatchQueue` on which the `Stream<Value, AFError>` values will be published. `.main` by | ||
| 273 | + /// default. | ||
| 274 | + /// - serializer: `DataStreamSerializer` used to produce the published `Stream<Value, AFError>` values. | ||
| 275 | + public init<Serializer: DataStreamSerializer>(_ request: DataStreamRequest, queue: DispatchQueue, serializer: Serializer) | ||
| 276 | + where Value == Serializer.SerializedObject { | ||
| 277 | + self.request = request | ||
| 278 | + streamHandler = { request.responseStream(using: serializer, on: queue, stream: $0) } | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + /// Publishes only the `Result` of the `DataStreamRequest.Stream`'s `Event`s. | ||
| 282 | + /// | ||
| 283 | + /// - Returns: The `AnyPublisher` publishing the `Result<Value, AFError>` value. | ||
| 284 | + public func result() -> AnyPublisher<Result<Value, AFError>, Never> { | ||
| 285 | + compactMap { stream in | ||
| 286 | + switch stream.event { | ||
| 287 | + case let .stream(result): | ||
| 288 | + return result | ||
| 289 | + // If the stream has completed with an error, send the error value downstream as a `.failure`. | ||
| 290 | + case let .complete(completion): | ||
| 291 | + return completion.error.map(Result.failure) | ||
| 292 | + } | ||
| 293 | + } | ||
| 294 | + .eraseToAnyPublisher() | ||
| 295 | + } | ||
| 296 | + | ||
| 297 | + /// Publishes the streamed values of the `DataStreamRequest.Stream` as a sequence of `Value` or fail with the | ||
| 298 | + /// `AFError` instance. | ||
| 299 | + /// | ||
| 300 | + /// - Returns: The `AnyPublisher<Value, AFError>` publishing the stream. | ||
| 301 | + public func value() -> AnyPublisher<Value, AFError> { | ||
| 302 | + result().setFailureType(to: AFError.self).flatMap(\.publisher).eraseToAnyPublisher() | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + public func receive<S>(subscriber: S) where S: Subscriber, DataStreamPublisher.Failure == S.Failure, DataStreamPublisher.Output == S.Input { | ||
| 306 | + subscriber.receive(subscription: Inner(request: request, | ||
| 307 | + streamHandler: streamHandler, | ||
| 308 | + downstream: subscriber)) | ||
| 309 | + } | ||
| 310 | + | ||
| 311 | + private final class Inner<Downstream: Subscriber>: Subscription | ||
| 312 | + where Downstream.Input == Output { | ||
| 313 | + typealias Failure = Downstream.Failure | ||
| 314 | + | ||
| 315 | + @Protected | ||
| 316 | + private var downstream: Downstream? | ||
| 317 | + private let request: DataStreamRequest | ||
| 318 | + private let streamHandler: Handler | ||
| 319 | + | ||
| 320 | + init(request: DataStreamRequest, streamHandler: @escaping Handler, downstream: Downstream) { | ||
| 321 | + self.request = request | ||
| 322 | + self.streamHandler = streamHandler | ||
| 323 | + self.downstream = downstream | ||
| 324 | + } | ||
| 325 | + | ||
| 326 | + func request(_ demand: Subscribers.Demand) { | ||
| 327 | + assert(demand > 0) | ||
| 328 | + | ||
| 329 | + guard let downstream = downstream else { return } | ||
| 330 | + | ||
| 331 | + self.downstream = nil | ||
| 332 | + streamHandler { stream in | ||
| 333 | + _ = downstream.receive(stream) | ||
| 334 | + if case .complete = stream.event { | ||
| 335 | + downstream.receive(completion: .finished) | ||
| 336 | + } | ||
| 337 | + }.resume() | ||
| 338 | + } | ||
| 339 | + | ||
| 340 | + func cancel() { | ||
| 341 | + request.cancel() | ||
| 342 | + downstream = nil | ||
| 343 | + } | ||
| 344 | + } | ||
| 345 | +} | ||
| 346 | + | ||
| 347 | +extension DataStreamRequest { | ||
| 348 | + /// Creates a `DataStreamPublisher` for this instance using the given `DataStreamSerializer` and `DispatchQueue`. | ||
| 349 | + /// | ||
| 350 | + /// - Parameters: | ||
| 351 | + /// - serializer: `DataStreamSerializer` used to serialize the streamed `Data`. | ||
| 352 | + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. | ||
| 353 | + /// - Returns: The `DataStreamPublisher`. | ||
| 354 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 355 | + public func publishStream<Serializer: DataStreamSerializer>(using serializer: Serializer, | ||
| 356 | + on queue: DispatchQueue = .main) -> DataStreamPublisher<Serializer.SerializedObject> { | ||
| 357 | + DataStreamPublisher(self, queue: queue, serializer: serializer) | ||
| 358 | + } | ||
| 359 | + | ||
| 360 | + /// Creates a `DataStreamPublisher` for this instance which uses a `PassthroughStreamSerializer` to stream `Data` | ||
| 361 | + /// unserialized. | ||
| 362 | + /// | ||
| 363 | + /// - Parameters: | ||
| 364 | + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. | ||
| 365 | + /// - Returns: The `DataStreamPublisher`. | ||
| 366 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 367 | + public func publishData(queue: DispatchQueue = .main) -> DataStreamPublisher<Data> { | ||
| 368 | + publishStream(using: PassthroughStreamSerializer(), on: queue) | ||
| 369 | + } | ||
| 370 | + | ||
| 371 | + /// Creates a `DataStreamPublisher` for this instance which uses a `StringStreamSerializer` to serialize stream | ||
| 372 | + /// `Data` values into `String` values. | ||
| 373 | + /// | ||
| 374 | + /// - Parameters: | ||
| 375 | + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. | ||
| 376 | + /// - Returns: The `DataStreamPublisher`. | ||
| 377 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 378 | + public func publishString(queue: DispatchQueue = .main) -> DataStreamPublisher<String> { | ||
| 379 | + publishStream(using: StringStreamSerializer(), on: queue) | ||
| 380 | + } | ||
| 381 | + | ||
| 382 | + /// Creates a `DataStreamPublisher` for this instance which uses a `DecodableStreamSerializer` with the provided | ||
| 383 | + /// parameters to serialize stream `Data` values into the provided type. | ||
| 384 | + /// | ||
| 385 | + /// - Parameters: | ||
| 386 | + /// - type: `Decodable` type to which to decode stream `Data`. Inferred from the context by default. | ||
| 387 | + /// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default. | ||
| 388 | + /// - decoder: `DataDecoder` instance used to decode stream `Data`. `JSONDecoder()` by default. | ||
| 389 | + /// - preprocessor: `DataPreprocessor` which filters incoming stream `Data` before serialization. | ||
| 390 | + /// `PassthroughPreprocessor()` by default. | ||
| 391 | + /// - Returns: The `DataStreamPublisher`. | ||
| 392 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 393 | + public func publishDecodable<T: Decodable>(type: T.Type = T.self, | ||
| 394 | + queue: DispatchQueue = .main, | ||
| 395 | + decoder: DataDecoder = JSONDecoder(), | ||
| 396 | + preprocessor: DataPreprocessor = PassthroughPreprocessor()) -> DataStreamPublisher<T> { | ||
| 397 | + publishStream(using: DecodableStreamSerializer(decoder: decoder, | ||
| 398 | + dataPreprocessor: preprocessor), | ||
| 399 | + on: queue) | ||
| 400 | + } | ||
| 401 | +} | ||
| 402 | + | ||
| 403 | +/// A Combine `Publisher` that publishes the `DownloadResponse<Value, AFError>` of the provided `DownloadRequest`. | ||
| 404 | +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 405 | +public struct DownloadResponsePublisher<Value>: Publisher { | ||
| 406 | + public typealias Output = DownloadResponse<Value, AFError> | ||
| 407 | + public typealias Failure = Never | ||
| 408 | + | ||
| 409 | + private typealias Handler = (@escaping (_ response: DownloadResponse<Value, AFError>) -> Void) -> DownloadRequest | ||
| 410 | + | ||
| 411 | + private let request: DownloadRequest | ||
| 412 | + private let responseHandler: Handler | ||
| 413 | + | ||
| 414 | + /// Creates an instance which will serialize responses using the provided `ResponseSerializer`. | ||
| 415 | + /// | ||
| 416 | + /// - Parameters: | ||
| 417 | + /// - request: `DownloadRequest` for which to publish the response. | ||
| 418 | + /// - queue: `DispatchQueue` on which the `DownloadResponse` value will be published. `.main` by default. | ||
| 419 | + /// - serializer: `ResponseSerializer` used to produce the published `DownloadResponse`. | ||
| 420 | + public init<Serializer: ResponseSerializer>(_ request: DownloadRequest, queue: DispatchQueue, serializer: Serializer) | ||
| 421 | + where Value == Serializer.SerializedObject { | ||
| 422 | + self.request = request | ||
| 423 | + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } | ||
| 424 | + } | ||
| 425 | + | ||
| 426 | + /// Creates an instance which will serialize responses using the provided `DownloadResponseSerializerProtocol` value. | ||
| 427 | + /// | ||
| 428 | + /// - Parameters: | ||
| 429 | + /// - request: `DownloadRequest` for which to publish the response. | ||
| 430 | + /// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default. | ||
| 431 | + /// - serializer: `DownloadResponseSerializerProtocol` used to produce the published `DownloadResponse`. | ||
| 432 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 433 | + public init<Serializer: DownloadResponseSerializerProtocol>(_ request: DownloadRequest, | ||
| 434 | + queue: DispatchQueue, | ||
| 435 | + serializer: Serializer) | ||
| 436 | + where Value == Serializer.SerializedObject { | ||
| 437 | + self.request = request | ||
| 438 | + responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) } | ||
| 439 | + } | ||
| 440 | + | ||
| 441 | + /// Publishes only the `Result` of the `DownloadResponse` value. | ||
| 442 | + /// | ||
| 443 | + /// - Returns: The `AnyPublisher` publishing the `Result<Value, AFError>` value. | ||
| 444 | + public func result() -> AnyPublisher<Result<Value, AFError>, Never> { | ||
| 445 | + map(\.result).eraseToAnyPublisher() | ||
| 446 | + } | ||
| 447 | + | ||
| 448 | + /// Publishes the `Result` of the `DownloadResponse` as a single `Value` or fail with the `AFError` instance. | ||
| 449 | + /// | ||
| 450 | + /// - Returns: The `AnyPublisher<Value, AFError>` publishing the stream. | ||
| 451 | + public func value() -> AnyPublisher<Value, AFError> { | ||
| 452 | + setFailureType(to: AFError.self).flatMap(\.result.publisher).eraseToAnyPublisher() | ||
| 453 | + } | ||
| 454 | + | ||
| 455 | + public func receive<S>(subscriber: S) where S: Subscriber, DownloadResponsePublisher.Failure == S.Failure, DownloadResponsePublisher.Output == S.Input { | ||
| 456 | + subscriber.receive(subscription: Inner(request: request, | ||
| 457 | + responseHandler: responseHandler, | ||
| 458 | + downstream: subscriber)) | ||
| 459 | + } | ||
| 460 | + | ||
| 461 | + private final class Inner<Downstream: Subscriber>: Subscription | ||
| 462 | + where Downstream.Input == Output { | ||
| 463 | + typealias Failure = Downstream.Failure | ||
| 464 | + | ||
| 465 | + @Protected | ||
| 466 | + private var downstream: Downstream? | ||
| 467 | + private let request: DownloadRequest | ||
| 468 | + private let responseHandler: Handler | ||
| 469 | + | ||
| 470 | + init(request: DownloadRequest, responseHandler: @escaping Handler, downstream: Downstream) { | ||
| 471 | + self.request = request | ||
| 472 | + self.responseHandler = responseHandler | ||
| 473 | + self.downstream = downstream | ||
| 474 | + } | ||
| 475 | + | ||
| 476 | + func request(_ demand: Subscribers.Demand) { | ||
| 477 | + assert(demand > 0) | ||
| 478 | + | ||
| 479 | + guard let downstream = downstream else { return } | ||
| 480 | + | ||
| 481 | + self.downstream = nil | ||
| 482 | + responseHandler { response in | ||
| 483 | + _ = downstream.receive(response) | ||
| 484 | + downstream.receive(completion: .finished) | ||
| 485 | + }.resume() | ||
| 486 | + } | ||
| 487 | + | ||
| 488 | + func cancel() { | ||
| 489 | + request.cancel() | ||
| 490 | + downstream = nil | ||
| 491 | + } | ||
| 492 | + } | ||
| 493 | +} | ||
| 494 | + | ||
| 495 | +extension DownloadRequest { | ||
| 496 | + /// Creates a `DownloadResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`. | ||
| 497 | + /// | ||
| 498 | + /// - Parameters: | ||
| 499 | + /// - serializer: `ResponseSerializer` used to serialize the response `Data` from disk. | ||
| 500 | + /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default. | ||
| 501 | + /// | ||
| 502 | + /// - Returns: The `DownloadResponsePublisher`. | ||
| 503 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 504 | + public func publishResponse<Serializer: ResponseSerializer, T>(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher<T> | ||
| 505 | + where Serializer.SerializedObject == T { | ||
| 506 | + DownloadResponsePublisher(self, queue: queue, serializer: serializer) | ||
| 507 | + } | ||
| 508 | + | ||
| 509 | + /// Creates a `DownloadResponsePublisher` for this instance using the given `DownloadResponseSerializerProtocol` and | ||
| 510 | + /// `DispatchQueue`. | ||
| 511 | + /// | ||
| 512 | + /// - Parameters: | ||
| 513 | + /// - serializer: `DownloadResponseSerializer` used to serialize the response `Data` from disk. | ||
| 514 | + /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default. | ||
| 515 | + /// | ||
| 516 | + /// - Returns: The `DownloadResponsePublisher`. | ||
| 517 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 518 | + public func publishResponse<Serializer: DownloadResponseSerializerProtocol, T>(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher<T> | ||
| 519 | + where Serializer.SerializedObject == T { | ||
| 520 | + DownloadResponsePublisher(self, queue: queue, serializer: serializer) | ||
| 521 | + } | ||
| 522 | + | ||
| 523 | + /// Creates a `DownloadResponsePublisher` for this instance and uses a `URLResponseSerializer` to serialize the | ||
| 524 | + /// response. | ||
| 525 | + /// | ||
| 526 | + /// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. | ||
| 527 | + /// | ||
| 528 | + /// - Returns: The `DownloadResponsePublisher`. | ||
| 529 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 530 | + public func publishURL(queue: DispatchQueue = .main) -> DownloadResponsePublisher<URL> { | ||
| 531 | + publishResponse(using: URLResponseSerializer(), on: queue) | ||
| 532 | + } | ||
| 533 | + | ||
| 534 | + /// Creates a `DownloadResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the | ||
| 535 | + /// response. | ||
| 536 | + /// | ||
| 537 | + /// - Parameters: | ||
| 538 | + /// - queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. | ||
| 539 | + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` | ||
| 540 | + /// by default. | ||
| 541 | + /// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by | ||
| 542 | + /// default. | ||
| 543 | + /// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of | ||
| 544 | + /// status code. `[.head]` by default. | ||
| 545 | + /// | ||
| 546 | + /// - Returns: The `DownloadResponsePublisher`. | ||
| 547 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 548 | + public func publishData(queue: DispatchQueue = .main, | ||
| 549 | + preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor, | ||
| 550 | + emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes, | ||
| 551 | + emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher<Data> { | ||
| 552 | + publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor, | ||
| 553 | + emptyResponseCodes: emptyResponseCodes, | ||
| 554 | + emptyRequestMethods: emptyRequestMethods), | ||
| 555 | + on: queue) | ||
| 556 | + } | ||
| 557 | + | ||
| 558 | + /// Creates a `DownloadResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the | ||
| 559 | + /// response. | ||
| 560 | + /// | ||
| 561 | + /// - Parameters: | ||
| 562 | + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. | ||
| 563 | + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()` | ||
| 564 | + /// by default. | ||
| 565 | + /// - encoding: `String.Encoding` to parse the response. `nil` by default, in which case the encoding | ||
| 566 | + /// will be determined by the server response, falling back to the default HTTP character | ||
| 567 | + /// set, `ISO-8859-1`. | ||
| 568 | + /// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by | ||
| 569 | + /// default. | ||
| 570 | + /// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of | ||
| 571 | + /// status code. `[.head]` by default. | ||
| 572 | + /// | ||
| 573 | + /// - Returns: The `DownloadResponsePublisher`. | ||
| 574 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 575 | + public func publishString(queue: DispatchQueue = .main, | ||
| 576 | + preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor, | ||
| 577 | + encoding: String.Encoding? = nil, | ||
| 578 | + emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes, | ||
| 579 | + emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher<String> { | ||
| 580 | + publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor, | ||
| 581 | + encoding: encoding, | ||
| 582 | + emptyResponseCodes: emptyResponseCodes, | ||
| 583 | + emptyRequestMethods: emptyRequestMethods), | ||
| 584 | + on: queue) | ||
| 585 | + } | ||
| 586 | + | ||
| 587 | + @_disfavoredOverload | ||
| 588 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 589 | + @available(*, deprecated, message: "Renamed publishDecodable(type:queue:preprocessor:decoder:emptyResponseCodes:emptyRequestMethods).") | ||
| 590 | + public func publishDecodable<T: Decodable>(type: T.Type = T.self, | ||
| 591 | + queue: DispatchQueue = .main, | ||
| 592 | + preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor, | ||
| 593 | + decoder: DataDecoder = JSONDecoder(), | ||
| 594 | + emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes, | ||
| 595 | + emptyResponseMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DownloadResponsePublisher<T> { | ||
| 596 | + publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, | ||
| 597 | + decoder: decoder, | ||
| 598 | + emptyResponseCodes: emptyResponseCodes, | ||
| 599 | + emptyRequestMethods: emptyResponseMethods), | ||
| 600 | + on: queue) | ||
| 601 | + } | ||
| 602 | + | ||
| 603 | + /// Creates a `DownloadResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize | ||
| 604 | + /// the response. | ||
| 605 | + /// | ||
| 606 | + /// - Parameters: | ||
| 607 | + /// - type: `Decodable` type to which to decode response `Data`. Inferred from the context by default. | ||
| 608 | + /// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default. | ||
| 609 | + /// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. | ||
| 610 | + /// `PassthroughPreprocessor()` by default. | ||
| 611 | + /// - decoder: `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default. | ||
| 612 | + /// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by | ||
| 613 | + /// default. | ||
| 614 | + /// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless | ||
| 615 | + /// of status code. `[.head]` by default. | ||
| 616 | + /// | ||
| 617 | + /// - Returns: The `DownloadResponsePublisher`. | ||
| 618 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 619 | + public func publishDecodable<T: Decodable>(type: T.Type = T.self, | ||
| 620 | + queue: DispatchQueue = .main, | ||
| 621 | + preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor, | ||
| 622 | + decoder: DataDecoder = JSONDecoder(), | ||
| 623 | + emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes, | ||
| 624 | + emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DownloadResponsePublisher<T> { | ||
| 625 | + publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor, | ||
| 626 | + decoder: decoder, | ||
| 627 | + emptyResponseCodes: emptyResponseCodes, | ||
| 628 | + emptyRequestMethods: emptyRequestMethods), | ||
| 629 | + on: queue) | ||
| 630 | + } | ||
| 631 | +} | ||
| 632 | + | ||
| 633 | +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 634 | +extension DownloadResponsePublisher where Value == URL? { | ||
| 635 | + /// Creates an instance which publishes a `DownloadResponse<URL?, AFError>` value without serialization. | ||
| 636 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 637 | + public init(_ request: DownloadRequest, queue: DispatchQueue) { | ||
| 638 | + self.request = request | ||
| 639 | + responseHandler = { request.response(queue: queue, completionHandler: $0) } | ||
| 640 | + } | ||
| 641 | +} | ||
| 642 | + | ||
| 643 | +extension DownloadRequest { | ||
| 644 | + /// Creates a `DownloadResponsePublisher` for this instance which does not serialize the response before publishing. | ||
| 645 | + /// | ||
| 646 | + /// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default. | ||
| 647 | + /// | ||
| 648 | + /// - Returns: The `DownloadResponsePublisher`. | ||
| 649 | + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) | ||
| 650 | + public func publishUnserialized(on queue: DispatchQueue = .main) -> DownloadResponsePublisher<URL?> { | ||
| 651 | + DownloadResponsePublisher(self, queue: queue) | ||
| 652 | + } | ||
| 653 | +} | ||
| 654 | + | ||
| 655 | +#endif |
Pods/Alamofire/Source/Concurrency.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/Concurrency.swift | ||
| 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 | + await task.value | ||
| 122 | + } onCancel: { | ||
| 123 | + cancel() | ||
| 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 | + await withCheckedContinuation { continuation in | ||
| 291 | + onResponse { | ||
| 292 | + continuation.resume(returning: $0) | ||
| 293 | + } | ||
| 294 | + } | ||
| 295 | + } onCancel: { | ||
| 296 | + self.cancel() | ||
| 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 | + await task.value | ||
| 315 | + } onCancel: { | ||
| 316 | + cancel() | ||
| 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 | + await withCheckedContinuation { continuation in | ||
| 500 | + onResponse { | ||
| 501 | + continuation.resume(returning: $0) | ||
| 502 | + } | ||
| 503 | + } | ||
| 504 | + } onCancel: { | ||
| 505 | + self.cancel() | ||
| 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 | + 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 | + 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 | + 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>(bufferingPolicy: bufferingPolicy) { innerContinuation in | ||
| 667 | + continuation = innerContinuation | ||
| 668 | + builder(innerContinuation) | ||
| 669 | + } | ||
| 670 | + | ||
| 671 | + return Iterator(iterator: stream.makeAsyncIterator()) { | ||
| 672 | + continuation?.finish() | ||
| 673 | + 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 |
Pods/Alamofire/Source/DispatchQueue+Alamofire.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift | ||
| 1 | +// | ||
| 2 | +// DispatchQueue+Alamofire.swift | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2014-2018 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 | +import Dispatch | ||
| 26 | +import Foundation | ||
| 27 | + | ||
| 28 | +extension DispatchQueue { | ||
| 29 | + /// Execute the provided closure after a `TimeInterval`. | ||
| 30 | + /// | ||
| 31 | + /// - Parameters: | ||
| 32 | + /// - delay: `TimeInterval` to delay execution. | ||
| 33 | + /// - closure: Closure to execute. | ||
| 34 | + func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { | ||
| 35 | + asyncAfter(deadline: .now() + delay, execute: closure) | ||
| 36 | + } | ||
| 37 | +} |
Pods/Alamofire/Source/EventMonitor.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/EventMonitor.swift | ||
| 1 | +// | ||
| 2 | +// EventMonitor.swift | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2014-2018 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 | +import Foundation | ||
| 26 | + | ||
| 27 | +/// Protocol outlining the lifetime events inside Alamofire. It includes both events received from the various | ||
| 28 | +/// `URLSession` delegate protocols as well as various events from the lifetime of `Request` and its subclasses. | ||
| 29 | +public protocol EventMonitor { | ||
| 30 | + /// The `DispatchQueue` onto which Alamofire's root `CompositeEventMonitor` will dispatch events. `.main` by default. | ||
| 31 | + var queue: DispatchQueue { get } | ||
| 32 | + | ||
| 33 | + // MARK: - URLSession Events | ||
| 34 | + | ||
| 35 | + // MARK: URLSessionDelegate Events | ||
| 36 | + | ||
| 37 | + /// Event called during `URLSessionDelegate`'s `urlSession(_:didBecomeInvalidWithError:)` method. | ||
| 38 | + func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) | ||
| 39 | + | ||
| 40 | + // MARK: URLSessionTaskDelegate Events | ||
| 41 | + | ||
| 42 | + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didReceive:completionHandler:)` method. | ||
| 43 | + func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) | ||
| 44 | + | ||
| 45 | + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` method. | ||
| 46 | + func urlSession(_ session: URLSession, | ||
| 47 | + task: URLSessionTask, | ||
| 48 | + didSendBodyData bytesSent: Int64, | ||
| 49 | + totalBytesSent: Int64, | ||
| 50 | + totalBytesExpectedToSend: Int64) | ||
| 51 | + | ||
| 52 | + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:needNewBodyStream:)` method. | ||
| 53 | + func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) | ||
| 54 | + | ||
| 55 | + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` method. | ||
| 56 | + func urlSession(_ session: URLSession, | ||
| 57 | + task: URLSessionTask, | ||
| 58 | + willPerformHTTPRedirection response: HTTPURLResponse, | ||
| 59 | + newRequest request: URLRequest) | ||
| 60 | + | ||
| 61 | + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didFinishCollecting:)` method. | ||
| 62 | + func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) | ||
| 63 | + | ||
| 64 | + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didCompleteWithError:)` method. | ||
| 65 | + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) | ||
| 66 | + | ||
| 67 | + /// Event called during `URLSessionTaskDelegate`'s `urlSession(_:taskIsWaitingForConnectivity:)` method. | ||
| 68 | + func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) | ||
| 69 | + | ||
| 70 | + // MARK: URLSessionDataDelegate Events | ||
| 71 | + | ||
| 72 | + /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:)` method. | ||
| 73 | + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) | ||
| 74 | + | ||
| 75 | + /// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:willCacheResponse:completionHandler:)` method. | ||
| 76 | + func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) | ||
| 77 | + | ||
| 78 | + // MARK: URLSessionDownloadDelegate Events | ||
| 79 | + | ||
| 80 | + /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` method. | ||
| 81 | + func urlSession(_ session: URLSession, | ||
| 82 | + downloadTask: URLSessionDownloadTask, | ||
| 83 | + didResumeAtOffset fileOffset: Int64, | ||
| 84 | + expectedTotalBytes: Int64) | ||
| 85 | + | ||
| 86 | + /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` method. | ||
| 87 | + func urlSession(_ session: URLSession, | ||
| 88 | + downloadTask: URLSessionDownloadTask, | ||
| 89 | + didWriteData bytesWritten: Int64, | ||
| 90 | + totalBytesWritten: Int64, | ||
| 91 | + totalBytesExpectedToWrite: Int64) | ||
| 92 | + | ||
| 93 | + /// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didFinishDownloadingTo:)` method. | ||
| 94 | + func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) | ||
| 95 | + | ||
| 96 | + // MARK: - Request Events | ||
| 97 | + | ||
| 98 | + /// Event called when a `URLRequest` is first created for a `Request`. If a `RequestAdapter` is active, the | ||
| 99 | + /// `URLRequest` will be adapted before being issued. | ||
| 100 | + func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) | ||
| 101 | + | ||
| 102 | + /// Event called when the attempt to create a `URLRequest` from a `Request`'s original `URLRequestConvertible` value fails. | ||
| 103 | + func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) | ||
| 104 | + | ||
| 105 | + /// Event called when a `RequestAdapter` adapts the `Request`'s initial `URLRequest`. | ||
| 106 | + func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) | ||
| 107 | + | ||
| 108 | + /// Event called when a `RequestAdapter` fails to adapt the `Request`'s initial `URLRequest`. | ||
| 109 | + func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) | ||
| 110 | + | ||
| 111 | + /// Event called when a final `URLRequest` is created for a `Request`. | ||
| 112 | + func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) | ||
| 113 | + | ||
| 114 | + /// Event called when a `URLSessionTask` subclass instance is created for a `Request`. | ||
| 115 | + func request(_ request: Request, didCreateTask task: URLSessionTask) | ||
| 116 | + | ||
| 117 | + /// Event called when a `Request` receives a `URLSessionTaskMetrics` value. | ||
| 118 | + func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) | ||
| 119 | + | ||
| 120 | + /// Event called when a `Request` fails due to an error created by Alamofire. e.g. When certificate pinning fails. | ||
| 121 | + func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) | ||
| 122 | + | ||
| 123 | + /// Event called when a `Request`'s task completes, possibly with an error. A `Request` may receive this event | ||
| 124 | + /// multiple times if it is retried. | ||
| 125 | + func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) | ||
| 126 | + | ||
| 127 | + /// Event called when a `Request` is about to be retried. | ||
| 128 | + func requestIsRetrying(_ request: Request) | ||
| 129 | + | ||
| 130 | + /// Event called when a `Request` finishes and response serializers are being called. | ||
| 131 | + func requestDidFinish(_ request: Request) | ||
| 132 | + | ||
| 133 | + /// Event called when a `Request` receives a `resume` call. | ||
| 134 | + func requestDidResume(_ request: Request) | ||
| 135 | + | ||
| 136 | + /// Event called when a `Request`'s associated `URLSessionTask` is resumed. | ||
| 137 | + func request(_ request: Request, didResumeTask task: URLSessionTask) | ||
| 138 | + | ||
| 139 | + /// Event called when a `Request` receives a `suspend` call. | ||
| 140 | + func requestDidSuspend(_ request: Request) | ||
| 141 | + | ||
| 142 | + /// Event called when a `Request`'s associated `URLSessionTask` is suspended. | ||
| 143 | + func request(_ request: Request, didSuspendTask task: URLSessionTask) | ||
| 144 | + | ||
| 145 | + /// Event called when a `Request` receives a `cancel` call. | ||
| 146 | + func requestDidCancel(_ request: Request) | ||
| 147 | + | ||
| 148 | + /// Event called when a `Request`'s associated `URLSessionTask` is cancelled. | ||
| 149 | + func request(_ request: Request, didCancelTask task: URLSessionTask) | ||
| 150 | + | ||
| 151 | + // MARK: DataRequest Events | ||
| 152 | + | ||
| 153 | + /// Event called when a `DataRequest` calls a `Validation`. | ||
| 154 | + func request(_ request: DataRequest, | ||
| 155 | + didValidateRequest urlRequest: URLRequest?, | ||
| 156 | + response: HTTPURLResponse, | ||
| 157 | + data: Data?, | ||
| 158 | + withResult result: Request.ValidationResult) | ||
| 159 | + | ||
| 160 | + /// Event called when a `DataRequest` creates a `DataResponse<Data?>` value without calling a `ResponseSerializer`. | ||
| 161 | + func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>) | ||
| 162 | + | ||
| 163 | + /// Event called when a `DataRequest` calls a `ResponseSerializer` and creates a generic `DataResponse<Value, AFError>`. | ||
| 164 | + func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) | ||
| 165 | + | ||
| 166 | + // MARK: DataStreamRequest Events | ||
| 167 | + | ||
| 168 | + /// Event called when a `DataStreamRequest` calls a `Validation` closure. | ||
| 169 | + /// | ||
| 170 | + /// - Parameters: | ||
| 171 | + /// - request: `DataStreamRequest` which is calling the `Validation`. | ||
| 172 | + /// - urlRequest: `URLRequest` of the request being validated. | ||
| 173 | + /// - response: `HTTPURLResponse` of the request being validated. | ||
| 174 | + /// - result: Produced `ValidationResult`. | ||
| 175 | + func request(_ request: DataStreamRequest, | ||
| 176 | + didValidateRequest urlRequest: URLRequest?, | ||
| 177 | + response: HTTPURLResponse, | ||
| 178 | + withResult result: Request.ValidationResult) | ||
| 179 | + | ||
| 180 | + /// Event called when a `DataStreamSerializer` produces a value from streamed `Data`. | ||
| 181 | + /// | ||
| 182 | + /// - Parameters: | ||
| 183 | + /// - request: `DataStreamRequest` for which the value was serialized. | ||
| 184 | + /// - result: `Result` of the serialization attempt. | ||
| 185 | + func request<Value>(_ request: DataStreamRequest, didParseStream result: Result<Value, AFError>) | ||
| 186 | + | ||
| 187 | + // MARK: UploadRequest Events | ||
| 188 | + | ||
| 189 | + /// Event called when an `UploadRequest` creates its `Uploadable` value, indicating the type of upload it represents. | ||
| 190 | + func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) | ||
| 191 | + | ||
| 192 | + /// Event called when an `UploadRequest` failed to create its `Uploadable` value due to an error. | ||
| 193 | + func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) | ||
| 194 | + | ||
| 195 | + /// Event called when an `UploadRequest` provides the `InputStream` from its `Uploadable` value. This only occurs if | ||
| 196 | + /// the `InputStream` does not wrap a `Data` value or file `URL`. | ||
| 197 | + func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) | ||
| 198 | + | ||
| 199 | + // MARK: DownloadRequest Events | ||
| 200 | + | ||
| 201 | + /// Event called when a `DownloadRequest`'s `URLSessionDownloadTask` finishes and the temporary file has been moved. | ||
| 202 | + func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>) | ||
| 203 | + | ||
| 204 | + /// Event called when a `DownloadRequest`'s `Destination` closure is called and creates the destination URL the | ||
| 205 | + /// downloaded file will be moved to. | ||
| 206 | + func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) | ||
| 207 | + | ||
| 208 | + /// Event called when a `DownloadRequest` calls a `Validation`. | ||
| 209 | + func request(_ request: DownloadRequest, | ||
| 210 | + didValidateRequest urlRequest: URLRequest?, | ||
| 211 | + response: HTTPURLResponse, | ||
| 212 | + fileURL: URL?, | ||
| 213 | + withResult result: Request.ValidationResult) | ||
| 214 | + | ||
| 215 | + /// Event called when a `DownloadRequest` creates a `DownloadResponse<URL?, AFError>` without calling a `ResponseSerializer`. | ||
| 216 | + func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>) | ||
| 217 | + | ||
| 218 | + /// Event called when a `DownloadRequest` calls a `DownloadResponseSerializer` and creates a generic `DownloadResponse<Value, AFError>` | ||
| 219 | + func request<Value>(_ request: DownloadRequest, didParseResponse response: DownloadResponse<Value, AFError>) | ||
| 220 | +} | ||
| 221 | + | ||
| 222 | +extension EventMonitor { | ||
| 223 | + /// The default queue on which `CompositeEventMonitor`s will call the `EventMonitor` methods. `.main` by default. | ||
| 224 | + public var queue: DispatchQueue { .main } | ||
| 225 | + | ||
| 226 | + // MARK: Default Implementations | ||
| 227 | + | ||
| 228 | + public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {} | ||
| 229 | + public func urlSession(_ session: URLSession, | ||
| 230 | + task: URLSessionTask, | ||
| 231 | + didReceive challenge: URLAuthenticationChallenge) {} | ||
| 232 | + public func urlSession(_ session: URLSession, | ||
| 233 | + task: URLSessionTask, | ||
| 234 | + didSendBodyData bytesSent: Int64, | ||
| 235 | + totalBytesSent: Int64, | ||
| 236 | + totalBytesExpectedToSend: Int64) {} | ||
| 237 | + public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) {} | ||
| 238 | + public func urlSession(_ session: URLSession, | ||
| 239 | + task: URLSessionTask, | ||
| 240 | + willPerformHTTPRedirection response: HTTPURLResponse, | ||
| 241 | + newRequest request: URLRequest) {} | ||
| 242 | + public func urlSession(_ session: URLSession, | ||
| 243 | + task: URLSessionTask, | ||
| 244 | + didFinishCollecting metrics: URLSessionTaskMetrics) {} | ||
| 245 | + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {} | ||
| 246 | + public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {} | ||
| 247 | + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {} | ||
| 248 | + public func urlSession(_ session: URLSession, | ||
| 249 | + dataTask: URLSessionDataTask, | ||
| 250 | + willCacheResponse proposedResponse: CachedURLResponse) {} | ||
| 251 | + public func urlSession(_ session: URLSession, | ||
| 252 | + downloadTask: URLSessionDownloadTask, | ||
| 253 | + didResumeAtOffset fileOffset: Int64, | ||
| 254 | + expectedTotalBytes: Int64) {} | ||
| 255 | + public func urlSession(_ session: URLSession, | ||
| 256 | + downloadTask: URLSessionDownloadTask, | ||
| 257 | + didWriteData bytesWritten: Int64, | ||
| 258 | + totalBytesWritten: Int64, | ||
| 259 | + totalBytesExpectedToWrite: Int64) {} | ||
| 260 | + public func urlSession(_ session: URLSession, | ||
| 261 | + downloadTask: URLSessionDownloadTask, | ||
| 262 | + didFinishDownloadingTo location: URL) {} | ||
| 263 | + public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {} | ||
| 264 | + public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {} | ||
| 265 | + public func request(_ request: Request, | ||
| 266 | + didAdaptInitialRequest initialRequest: URLRequest, | ||
| 267 | + to adaptedRequest: URLRequest) {} | ||
| 268 | + public func request(_ request: Request, | ||
| 269 | + didFailToAdaptURLRequest initialRequest: URLRequest, | ||
| 270 | + withError error: AFError) {} | ||
| 271 | + public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {} | ||
| 272 | + public func request(_ request: Request, didCreateTask task: URLSessionTask) {} | ||
| 273 | + public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) {} | ||
| 274 | + public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {} | ||
| 275 | + public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {} | ||
| 276 | + public func requestIsRetrying(_ request: Request) {} | ||
| 277 | + public func requestDidFinish(_ request: Request) {} | ||
| 278 | + public func requestDidResume(_ request: Request) {} | ||
| 279 | + public func request(_ request: Request, didResumeTask task: URLSessionTask) {} | ||
| 280 | + public func requestDidSuspend(_ request: Request) {} | ||
| 281 | + public func request(_ request: Request, didSuspendTask task: URLSessionTask) {} | ||
| 282 | + public func requestDidCancel(_ request: Request) {} | ||
| 283 | + public func request(_ request: Request, didCancelTask task: URLSessionTask) {} | ||
| 284 | + public func request(_ request: DataRequest, | ||
| 285 | + didValidateRequest urlRequest: URLRequest?, | ||
| 286 | + response: HTTPURLResponse, | ||
| 287 | + data: Data?, | ||
| 288 | + withResult result: Request.ValidationResult) {} | ||
| 289 | + public func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>) {} | ||
| 290 | + public func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {} | ||
| 291 | + public func request(_ request: DataStreamRequest, | ||
| 292 | + didValidateRequest urlRequest: URLRequest?, | ||
| 293 | + response: HTTPURLResponse, | ||
| 294 | + withResult result: Request.ValidationResult) {} | ||
| 295 | + public func request<Value>(_ request: DataStreamRequest, didParseStream result: Result<Value, AFError>) {} | ||
| 296 | + public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) {} | ||
| 297 | + public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) {} | ||
| 298 | + public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) {} | ||
| 299 | + public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>) {} | ||
| 300 | + public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) {} | ||
| 301 | + public func request(_ request: DownloadRequest, | ||
| 302 | + didValidateRequest urlRequest: URLRequest?, | ||
| 303 | + response: HTTPURLResponse, | ||
| 304 | + fileURL: URL?, | ||
| 305 | + withResult result: Request.ValidationResult) {} | ||
| 306 | + public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>) {} | ||
| 307 | + public func request<Value>(_ request: DownloadRequest, didParseResponse response: DownloadResponse<Value, AFError>) {} | ||
| 308 | +} | ||
| 309 | + | ||
| 310 | +/// An `EventMonitor` which can contain multiple `EventMonitor`s and calls their methods on their queues. | ||
| 311 | +public final class CompositeEventMonitor: EventMonitor { | ||
| 312 | + public let queue = DispatchQueue(label: "org.alamofire.compositeEventMonitor", qos: .utility) | ||
| 313 | + | ||
| 314 | + let monitors: [EventMonitor] | ||
| 315 | + | ||
| 316 | + init(monitors: [EventMonitor]) { | ||
| 317 | + self.monitors = monitors | ||
| 318 | + } | ||
| 319 | + | ||
| 320 | + func performEvent(_ event: @escaping (EventMonitor) -> Void) { | ||
| 321 | + queue.async { | ||
| 322 | + for monitor in self.monitors { | ||
| 323 | + monitor.queue.async { event(monitor) } | ||
| 324 | + } | ||
| 325 | + } | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { | ||
| 329 | + performEvent { $0.urlSession(session, didBecomeInvalidWithError: error) } | ||
| 330 | + } | ||
| 331 | + | ||
| 332 | + public func urlSession(_ session: URLSession, | ||
| 333 | + task: URLSessionTask, | ||
| 334 | + didReceive challenge: URLAuthenticationChallenge) { | ||
| 335 | + performEvent { $0.urlSession(session, task: task, didReceive: challenge) } | ||
| 336 | + } | ||
| 337 | + | ||
| 338 | + public func urlSession(_ session: URLSession, | ||
| 339 | + task: URLSessionTask, | ||
| 340 | + didSendBodyData bytesSent: Int64, | ||
| 341 | + totalBytesSent: Int64, | ||
| 342 | + totalBytesExpectedToSend: Int64) { | ||
| 343 | + performEvent { | ||
| 344 | + $0.urlSession(session, | ||
| 345 | + task: task, | ||
| 346 | + didSendBodyData: bytesSent, | ||
| 347 | + totalBytesSent: totalBytesSent, | ||
| 348 | + totalBytesExpectedToSend: totalBytesExpectedToSend) | ||
| 349 | + } | ||
| 350 | + } | ||
| 351 | + | ||
| 352 | + public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) { | ||
| 353 | + performEvent { | ||
| 354 | + $0.urlSession(session, taskNeedsNewBodyStream: task) | ||
| 355 | + } | ||
| 356 | + } | ||
| 357 | + | ||
| 358 | + public func urlSession(_ session: URLSession, | ||
| 359 | + task: URLSessionTask, | ||
| 360 | + willPerformHTTPRedirection response: HTTPURLResponse, | ||
| 361 | + newRequest request: URLRequest) { | ||
| 362 | + performEvent { | ||
| 363 | + $0.urlSession(session, | ||
| 364 | + task: task, | ||
| 365 | + willPerformHTTPRedirection: response, | ||
| 366 | + newRequest: request) | ||
| 367 | + } | ||
| 368 | + } | ||
| 369 | + | ||
| 370 | + public func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { | ||
| 371 | + performEvent { $0.urlSession(session, task: task, didFinishCollecting: metrics) } | ||
| 372 | + } | ||
| 373 | + | ||
| 374 | + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { | ||
| 375 | + performEvent { $0.urlSession(session, task: task, didCompleteWithError: error) } | ||
| 376 | + } | ||
| 377 | + | ||
| 378 | + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) | ||
| 379 | + public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { | ||
| 380 | + performEvent { $0.urlSession(session, taskIsWaitingForConnectivity: task) } | ||
| 381 | + } | ||
| 382 | + | ||
| 383 | + public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { | ||
| 384 | + performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: data) } | ||
| 385 | + } | ||
| 386 | + | ||
| 387 | + public func urlSession(_ session: URLSession, | ||
| 388 | + dataTask: URLSessionDataTask, | ||
| 389 | + willCacheResponse proposedResponse: CachedURLResponse) { | ||
| 390 | + performEvent { $0.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse) } | ||
| 391 | + } | ||
| 392 | + | ||
| 393 | + public func urlSession(_ session: URLSession, | ||
| 394 | + downloadTask: URLSessionDownloadTask, | ||
| 395 | + didResumeAtOffset fileOffset: Int64, | ||
| 396 | + expectedTotalBytes: Int64) { | ||
| 397 | + performEvent { | ||
| 398 | + $0.urlSession(session, | ||
| 399 | + downloadTask: downloadTask, | ||
| 400 | + didResumeAtOffset: fileOffset, | ||
| 401 | + expectedTotalBytes: expectedTotalBytes) | ||
| 402 | + } | ||
| 403 | + } | ||
| 404 | + | ||
| 405 | + public func urlSession(_ session: URLSession, | ||
| 406 | + downloadTask: URLSessionDownloadTask, | ||
| 407 | + didWriteData bytesWritten: Int64, | ||
| 408 | + totalBytesWritten: Int64, | ||
| 409 | + totalBytesExpectedToWrite: Int64) { | ||
| 410 | + performEvent { | ||
| 411 | + $0.urlSession(session, | ||
| 412 | + downloadTask: downloadTask, | ||
| 413 | + didWriteData: bytesWritten, | ||
| 414 | + totalBytesWritten: totalBytesWritten, | ||
| 415 | + totalBytesExpectedToWrite: totalBytesExpectedToWrite) | ||
| 416 | + } | ||
| 417 | + } | ||
| 418 | + | ||
| 419 | + public func urlSession(_ session: URLSession, | ||
| 420 | + downloadTask: URLSessionDownloadTask, | ||
| 421 | + didFinishDownloadingTo location: URL) { | ||
| 422 | + performEvent { $0.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) } | ||
| 423 | + } | ||
| 424 | + | ||
| 425 | + public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) { | ||
| 426 | + performEvent { $0.request(request, didCreateInitialURLRequest: urlRequest) } | ||
| 427 | + } | ||
| 428 | + | ||
| 429 | + public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) { | ||
| 430 | + performEvent { $0.request(request, didFailToCreateURLRequestWithError: error) } | ||
| 431 | + } | ||
| 432 | + | ||
| 433 | + public func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) { | ||
| 434 | + performEvent { $0.request(request, didAdaptInitialRequest: initialRequest, to: adaptedRequest) } | ||
| 435 | + } | ||
| 436 | + | ||
| 437 | + public func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) { | ||
| 438 | + performEvent { $0.request(request, didFailToAdaptURLRequest: initialRequest, withError: error) } | ||
| 439 | + } | ||
| 440 | + | ||
| 441 | + public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) { | ||
| 442 | + performEvent { $0.request(request, didCreateURLRequest: urlRequest) } | ||
| 443 | + } | ||
| 444 | + | ||
| 445 | + public func request(_ request: Request, didCreateTask task: URLSessionTask) { | ||
| 446 | + performEvent { $0.request(request, didCreateTask: task) } | ||
| 447 | + } | ||
| 448 | + | ||
| 449 | + public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) { | ||
| 450 | + performEvent { $0.request(request, didGatherMetrics: metrics) } | ||
| 451 | + } | ||
| 452 | + | ||
| 453 | + public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) { | ||
| 454 | + performEvent { $0.request(request, didFailTask: task, earlyWithError: error) } | ||
| 455 | + } | ||
| 456 | + | ||
| 457 | + public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { | ||
| 458 | + performEvent { $0.request(request, didCompleteTask: task, with: error) } | ||
| 459 | + } | ||
| 460 | + | ||
| 461 | + public func requestIsRetrying(_ request: Request) { | ||
| 462 | + performEvent { $0.requestIsRetrying(request) } | ||
| 463 | + } | ||
| 464 | + | ||
| 465 | + public func requestDidFinish(_ request: Request) { | ||
| 466 | + performEvent { $0.requestDidFinish(request) } | ||
| 467 | + } | ||
| 468 | + | ||
| 469 | + public func requestDidResume(_ request: Request) { | ||
| 470 | + performEvent { $0.requestDidResume(request) } | ||
| 471 | + } | ||
| 472 | + | ||
| 473 | + public func request(_ request: Request, didResumeTask task: URLSessionTask) { | ||
| 474 | + performEvent { $0.request(request, didResumeTask: task) } | ||
| 475 | + } | ||
| 476 | + | ||
| 477 | + public func requestDidSuspend(_ request: Request) { | ||
| 478 | + performEvent { $0.requestDidSuspend(request) } | ||
| 479 | + } | ||
| 480 | + | ||
| 481 | + public func request(_ request: Request, didSuspendTask task: URLSessionTask) { | ||
| 482 | + performEvent { $0.request(request, didSuspendTask: task) } | ||
| 483 | + } | ||
| 484 | + | ||
| 485 | + public func requestDidCancel(_ request: Request) { | ||
| 486 | + performEvent { $0.requestDidCancel(request) } | ||
| 487 | + } | ||
| 488 | + | ||
| 489 | + public func request(_ request: Request, didCancelTask task: URLSessionTask) { | ||
| 490 | + performEvent { $0.request(request, didCancelTask: task) } | ||
| 491 | + } | ||
| 492 | + | ||
| 493 | + public func request(_ request: DataRequest, | ||
| 494 | + didValidateRequest urlRequest: URLRequest?, | ||
| 495 | + response: HTTPURLResponse, | ||
| 496 | + data: Data?, | ||
| 497 | + withResult result: Request.ValidationResult) { | ||
| 498 | + performEvent { $0.request(request, | ||
| 499 | + didValidateRequest: urlRequest, | ||
| 500 | + response: response, | ||
| 501 | + data: data, | ||
| 502 | + withResult: result) | ||
| 503 | + } | ||
| 504 | + } | ||
| 505 | + | ||
| 506 | + public func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>) { | ||
| 507 | + performEvent { $0.request(request, didParseResponse: response) } | ||
| 508 | + } | ||
| 509 | + | ||
| 510 | + public func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) { | ||
| 511 | + performEvent { $0.request(request, didParseResponse: response) } | ||
| 512 | + } | ||
| 513 | + | ||
| 514 | + public func request(_ request: DataStreamRequest, | ||
| 515 | + didValidateRequest urlRequest: URLRequest?, | ||
| 516 | + response: HTTPURLResponse, | ||
| 517 | + withResult result: Request.ValidationResult) { | ||
| 518 | + performEvent { $0.request(request, | ||
| 519 | + didValidateRequest: urlRequest, | ||
| 520 | + response: response, | ||
| 521 | + withResult: result) | ||
| 522 | + } | ||
| 523 | + } | ||
| 524 | + | ||
| 525 | + public func request<Value>(_ request: DataStreamRequest, didParseStream result: Result<Value, AFError>) { | ||
| 526 | + performEvent { $0.request(request, didParseStream: result) } | ||
| 527 | + } | ||
| 528 | + | ||
| 529 | + public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) { | ||
| 530 | + performEvent { $0.request(request, didCreateUploadable: uploadable) } | ||
| 531 | + } | ||
| 532 | + | ||
| 533 | + public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) { | ||
| 534 | + performEvent { $0.request(request, didFailToCreateUploadableWithError: error) } | ||
| 535 | + } | ||
| 536 | + | ||
| 537 | + public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) { | ||
| 538 | + performEvent { $0.request(request, didProvideInputStream: stream) } | ||
| 539 | + } | ||
| 540 | + | ||
| 541 | + public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>) { | ||
| 542 | + performEvent { $0.request(request, didFinishDownloadingUsing: task, with: result) } | ||
| 543 | + } | ||
| 544 | + | ||
| 545 | + public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) { | ||
| 546 | + performEvent { $0.request(request, didCreateDestinationURL: url) } | ||
| 547 | + } | ||
| 548 | + | ||
| 549 | + public func request(_ request: DownloadRequest, | ||
| 550 | + didValidateRequest urlRequest: URLRequest?, | ||
| 551 | + response: HTTPURLResponse, | ||
| 552 | + fileURL: URL?, | ||
| 553 | + withResult result: Request.ValidationResult) { | ||
| 554 | + performEvent { $0.request(request, | ||
| 555 | + didValidateRequest: urlRequest, | ||
| 556 | + response: response, | ||
| 557 | + fileURL: fileURL, | ||
| 558 | + withResult: result) } | ||
| 559 | + } | ||
| 560 | + | ||
| 561 | + public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>) { | ||
| 562 | + performEvent { $0.request(request, didParseResponse: response) } | ||
| 563 | + } | ||
| 564 | + | ||
| 565 | + public func request<Value>(_ request: DownloadRequest, didParseResponse response: DownloadResponse<Value, AFError>) { | ||
| 566 | + performEvent { $0.request(request, didParseResponse: response) } | ||
| 567 | + } | ||
| 568 | +} | ||
| 569 | + | ||
| 570 | +/// `EventMonitor` that allows optional closures to be set to receive events. | ||
| 571 | +open class ClosureEventMonitor: EventMonitor { | ||
| 572 | + /// Closure called on the `urlSession(_:didBecomeInvalidWithError:)` event. | ||
| 573 | + open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)? | ||
| 574 | + | ||
| 575 | + /// Closure called on the `urlSession(_:task:didReceive:completionHandler:)`. | ||
| 576 | + open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> Void)? | ||
| 577 | + | ||
| 578 | + /// Closure that receives `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` event. | ||
| 579 | + open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? | ||
| 580 | + | ||
| 581 | + /// Closure called on the `urlSession(_:task:needNewBodyStream:)` event. | ||
| 582 | + open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> Void)? | ||
| 583 | + | ||
| 584 | + /// Closure called on the `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` event. | ||
| 585 | + open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> Void)? | ||
| 586 | + | ||
| 587 | + /// Closure called on the `urlSession(_:task:didFinishCollecting:)` event. | ||
| 588 | + open var taskDidFinishCollectingMetrics: ((URLSession, URLSessionTask, URLSessionTaskMetrics) -> Void)? | ||
| 589 | + | ||
| 590 | + /// Closure called on the `urlSession(_:task:didCompleteWithError:)` event. | ||
| 591 | + open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)? | ||
| 592 | + | ||
| 593 | + /// Closure called on the `urlSession(_:taskIsWaitingForConnectivity:)` event. | ||
| 594 | + open var taskIsWaitingForConnectivity: ((URLSession, URLSessionTask) -> Void)? | ||
| 595 | + | ||
| 596 | + /// Closure that receives the `urlSession(_:dataTask:didReceive:)` event. | ||
| 597 | + open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? | ||
| 598 | + | ||
| 599 | + /// Closure called on the `urlSession(_:dataTask:willCacheResponse:completionHandler:)` event. | ||
| 600 | + open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> Void)? | ||
| 601 | + | ||
| 602 | + /// Closure called on the `urlSession(_:downloadTask:didFinishDownloadingTo:)` event. | ||
| 603 | + open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)? | ||
| 604 | + | ||
| 605 | + /// Closure called on the `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` | ||
| 606 | + /// event. | ||
| 607 | + open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? | ||
| 608 | + | ||
| 609 | + /// Closure called on the `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` event. | ||
| 610 | + open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? | ||
| 611 | + | ||
| 612 | + // MARK: - Request Events | ||
| 613 | + | ||
| 614 | + /// Closure called on the `request(_:didCreateInitialURLRequest:)` event. | ||
| 615 | + open var requestDidCreateInitialURLRequest: ((Request, URLRequest) -> Void)? | ||
| 616 | + | ||
| 617 | + /// Closure called on the `request(_:didFailToCreateURLRequestWithError:)` event. | ||
| 618 | + open var requestDidFailToCreateURLRequestWithError: ((Request, AFError) -> Void)? | ||
| 619 | + | ||
| 620 | + /// Closure called on the `request(_:didAdaptInitialRequest:to:)` event. | ||
| 621 | + open var requestDidAdaptInitialRequestToAdaptedRequest: ((Request, URLRequest, URLRequest) -> Void)? | ||
| 622 | + | ||
| 623 | + /// Closure called on the `request(_:didFailToAdaptURLRequest:withError:)` event. | ||
| 624 | + open var requestDidFailToAdaptURLRequestWithError: ((Request, URLRequest, AFError) -> Void)? | ||
| 625 | + | ||
| 626 | + /// Closure called on the `request(_:didCreateURLRequest:)` event. | ||
| 627 | + open var requestDidCreateURLRequest: ((Request, URLRequest) -> Void)? | ||
| 628 | + | ||
| 629 | + /// Closure called on the `request(_:didCreateTask:)` event. | ||
| 630 | + open var requestDidCreateTask: ((Request, URLSessionTask) -> Void)? | ||
| 631 | + | ||
| 632 | + /// Closure called on the `request(_:didGatherMetrics:)` event. | ||
| 633 | + open var requestDidGatherMetrics: ((Request, URLSessionTaskMetrics) -> Void)? | ||
| 634 | + | ||
| 635 | + /// Closure called on the `request(_:didFailTask:earlyWithError:)` event. | ||
| 636 | + open var requestDidFailTaskEarlyWithError: ((Request, URLSessionTask, AFError) -> Void)? | ||
| 637 | + | ||
| 638 | + /// Closure called on the `request(_:didCompleteTask:with:)` event. | ||
| 639 | + open var requestDidCompleteTaskWithError: ((Request, URLSessionTask, AFError?) -> Void)? | ||
| 640 | + | ||
| 641 | + /// Closure called on the `requestIsRetrying(_:)` event. | ||
| 642 | + open var requestIsRetrying: ((Request) -> Void)? | ||
| 643 | + | ||
| 644 | + /// Closure called on the `requestDidFinish(_:)` event. | ||
| 645 | + open var requestDidFinish: ((Request) -> Void)? | ||
| 646 | + | ||
| 647 | + /// Closure called on the `requestDidResume(_:)` event. | ||
| 648 | + open var requestDidResume: ((Request) -> Void)? | ||
| 649 | + | ||
| 650 | + /// Closure called on the `request(_:didResumeTask:)` event. | ||
| 651 | + open var requestDidResumeTask: ((Request, URLSessionTask) -> Void)? | ||
| 652 | + | ||
| 653 | + /// Closure called on the `requestDidSuspend(_:)` event. | ||
| 654 | + open var requestDidSuspend: ((Request) -> Void)? | ||
| 655 | + | ||
| 656 | + /// Closure called on the `request(_:didSuspendTask:)` event. | ||
| 657 | + open var requestDidSuspendTask: ((Request, URLSessionTask) -> Void)? | ||
| 658 | + | ||
| 659 | + /// Closure called on the `requestDidCancel(_:)` event. | ||
| 660 | + open var requestDidCancel: ((Request) -> Void)? | ||
| 661 | + | ||
| 662 | + /// Closure called on the `request(_:didCancelTask:)` event. | ||
| 663 | + open var requestDidCancelTask: ((Request, URLSessionTask) -> Void)? | ||
| 664 | + | ||
| 665 | + /// Closure called on the `request(_:didValidateRequest:response:data:withResult:)` event. | ||
| 666 | + open var requestDidValidateRequestResponseDataWithResult: ((DataRequest, URLRequest?, HTTPURLResponse, Data?, Request.ValidationResult) -> Void)? | ||
| 667 | + | ||
| 668 | + /// Closure called on the `request(_:didParseResponse:)` event. | ||
| 669 | + open var requestDidParseResponse: ((DataRequest, DataResponse<Data?, AFError>) -> Void)? | ||
| 670 | + | ||
| 671 | + /// Closure called on the `request(_:didValidateRequest:response:withResult:)` event. | ||
| 672 | + open var requestDidValidateRequestResponseWithResult: ((DataStreamRequest, URLRequest?, HTTPURLResponse, Request.ValidationResult) -> Void)? | ||
| 673 | + | ||
| 674 | + /// Closure called on the `request(_:didCreateUploadable:)` event. | ||
| 675 | + open var requestDidCreateUploadable: ((UploadRequest, UploadRequest.Uploadable) -> Void)? | ||
| 676 | + | ||
| 677 | + /// Closure called on the `request(_:didFailToCreateUploadableWithError:)` event. | ||
| 678 | + open var requestDidFailToCreateUploadableWithError: ((UploadRequest, AFError) -> Void)? | ||
| 679 | + | ||
| 680 | + /// Closure called on the `request(_:didProvideInputStream:)` event. | ||
| 681 | + open var requestDidProvideInputStream: ((UploadRequest, InputStream) -> Void)? | ||
| 682 | + | ||
| 683 | + /// Closure called on the `request(_:didFinishDownloadingUsing:with:)` event. | ||
| 684 | + open var requestDidFinishDownloadingUsingTaskWithResult: ((DownloadRequest, URLSessionTask, Result<URL, AFError>) -> Void)? | ||
| 685 | + | ||
| 686 | + /// Closure called on the `request(_:didCreateDestinationURL:)` event. | ||
| 687 | + open var requestDidCreateDestinationURL: ((DownloadRequest, URL) -> Void)? | ||
| 688 | + | ||
| 689 | + /// Closure called on the `request(_:didValidateRequest:response:temporaryURL:destinationURL:withResult:)` event. | ||
| 690 | + open var requestDidValidateRequestResponseFileURLWithResult: ((DownloadRequest, URLRequest?, HTTPURLResponse, URL?, Request.ValidationResult) -> Void)? | ||
| 691 | + | ||
| 692 | + /// Closure called on the `request(_:didParseResponse:)` event. | ||
| 693 | + open var requestDidParseDownloadResponse: ((DownloadRequest, DownloadResponse<URL?, AFError>) -> Void)? | ||
| 694 | + | ||
| 695 | + public let queue: DispatchQueue | ||
| 696 | + | ||
| 697 | + /// Creates an instance using the provided queue. | ||
| 698 | + /// | ||
| 699 | + /// - Parameter queue: `DispatchQueue` on which events will fired. `.main` by default. | ||
| 700 | + public init(queue: DispatchQueue = .main) { | ||
| 701 | + self.queue = queue | ||
| 702 | + } | ||
| 703 | + | ||
| 704 | + open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { | ||
| 705 | + sessionDidBecomeInvalidWithError?(session, error) | ||
| 706 | + } | ||
| 707 | + | ||
| 708 | + open func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) { | ||
| 709 | + taskDidReceiveChallenge?(session, task, challenge) | ||
| 710 | + } | ||
| 711 | + | ||
| 712 | + open func urlSession(_ session: URLSession, | ||
| 713 | + task: URLSessionTask, | ||
| 714 | + didSendBodyData bytesSent: Int64, | ||
| 715 | + totalBytesSent: Int64, | ||
| 716 | + totalBytesExpectedToSend: Int64) { | ||
| 717 | + taskDidSendBodyData?(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) | ||
| 718 | + } | ||
| 719 | + | ||
| 720 | + open func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) { | ||
| 721 | + taskNeedNewBodyStream?(session, task) | ||
| 722 | + } | ||
| 723 | + | ||
| 724 | + open func urlSession(_ session: URLSession, | ||
| 725 | + task: URLSessionTask, | ||
| 726 | + willPerformHTTPRedirection response: HTTPURLResponse, | ||
| 727 | + newRequest request: URLRequest) { | ||
| 728 | + taskWillPerformHTTPRedirection?(session, task, response, request) | ||
| 729 | + } | ||
| 730 | + | ||
| 731 | + open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { | ||
| 732 | + taskDidFinishCollectingMetrics?(session, task, metrics) | ||
| 733 | + } | ||
| 734 | + | ||
| 735 | + open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { | ||
| 736 | + taskDidComplete?(session, task, error) | ||
| 737 | + } | ||
| 738 | + | ||
| 739 | + @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) | ||
| 740 | + open func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { | ||
| 741 | + taskIsWaitingForConnectivity?(session, task) | ||
| 742 | + } | ||
| 743 | + | ||
| 744 | + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { | ||
| 745 | + dataTaskDidReceiveData?(session, dataTask, data) | ||
| 746 | + } | ||
| 747 | + | ||
| 748 | + open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) { | ||
| 749 | + dataTaskWillCacheResponse?(session, dataTask, proposedResponse) | ||
| 750 | + } | ||
| 751 | + | ||
| 752 | + open func urlSession(_ session: URLSession, | ||
| 753 | + downloadTask: URLSessionDownloadTask, | ||
| 754 | + didResumeAtOffset fileOffset: Int64, | ||
| 755 | + expectedTotalBytes: Int64) { | ||
| 756 | + downloadTaskDidResumeAtOffset?(session, downloadTask, fileOffset, expectedTotalBytes) | ||
| 757 | + } | ||
| 758 | + | ||
| 759 | + open func urlSession(_ session: URLSession, | ||
| 760 | + downloadTask: URLSessionDownloadTask, | ||
| 761 | + didWriteData bytesWritten: Int64, | ||
| 762 | + totalBytesWritten: Int64, | ||
| 763 | + totalBytesExpectedToWrite: Int64) { | ||
| 764 | + downloadTaskDidWriteData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) | ||
| 765 | + } | ||
| 766 | + | ||
| 767 | + open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { | ||
| 768 | + downloadTaskDidFinishDownloadingToURL?(session, downloadTask, location) | ||
| 769 | + } | ||
| 770 | + | ||
| 771 | + // MARK: Request Events | ||
| 772 | + | ||
| 773 | + open func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) { | ||
| 774 | + requestDidCreateInitialURLRequest?(request, urlRequest) | ||
| 775 | + } | ||
| 776 | + | ||
| 777 | + open func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) { | ||
| 778 | + requestDidFailToCreateURLRequestWithError?(request, error) | ||
| 779 | + } | ||
| 780 | + | ||
| 781 | + open func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) { | ||
| 782 | + requestDidAdaptInitialRequestToAdaptedRequest?(request, initialRequest, adaptedRequest) | ||
| 783 | + } | ||
| 784 | + | ||
| 785 | + open func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) { | ||
| 786 | + requestDidFailToAdaptURLRequestWithError?(request, initialRequest, error) | ||
| 787 | + } | ||
| 788 | + | ||
| 789 | + open func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) { | ||
| 790 | + requestDidCreateURLRequest?(request, urlRequest) | ||
| 791 | + } | ||
| 792 | + | ||
| 793 | + open func request(_ request: Request, didCreateTask task: URLSessionTask) { | ||
| 794 | + requestDidCreateTask?(request, task) | ||
| 795 | + } | ||
| 796 | + | ||
| 797 | + open func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) { | ||
| 798 | + requestDidGatherMetrics?(request, metrics) | ||
| 799 | + } | ||
| 800 | + | ||
| 801 | + open func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) { | ||
| 802 | + requestDidFailTaskEarlyWithError?(request, task, error) | ||
| 803 | + } | ||
| 804 | + | ||
| 805 | + open func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) { | ||
| 806 | + requestDidCompleteTaskWithError?(request, task, error) | ||
| 807 | + } | ||
| 808 | + | ||
| 809 | + open func requestIsRetrying(_ request: Request) { | ||
| 810 | + requestIsRetrying?(request) | ||
| 811 | + } | ||
| 812 | + | ||
| 813 | + open func requestDidFinish(_ request: Request) { | ||
| 814 | + requestDidFinish?(request) | ||
| 815 | + } | ||
| 816 | + | ||
| 817 | + open func requestDidResume(_ request: Request) { | ||
| 818 | + requestDidResume?(request) | ||
| 819 | + } | ||
| 820 | + | ||
| 821 | + public func request(_ request: Request, didResumeTask task: URLSessionTask) { | ||
| 822 | + requestDidResumeTask?(request, task) | ||
| 823 | + } | ||
| 824 | + | ||
| 825 | + open func requestDidSuspend(_ request: Request) { | ||
| 826 | + requestDidSuspend?(request) | ||
| 827 | + } | ||
| 828 | + | ||
| 829 | + public func request(_ request: Request, didSuspendTask task: URLSessionTask) { | ||
| 830 | + requestDidSuspendTask?(request, task) | ||
| 831 | + } | ||
| 832 | + | ||
| 833 | + open func requestDidCancel(_ request: Request) { | ||
| 834 | + requestDidCancel?(request) | ||
| 835 | + } | ||
| 836 | + | ||
| 837 | + public func request(_ request: Request, didCancelTask task: URLSessionTask) { | ||
| 838 | + requestDidCancelTask?(request, task) | ||
| 839 | + } | ||
| 840 | + | ||
| 841 | + open func request(_ request: DataRequest, | ||
| 842 | + didValidateRequest urlRequest: URLRequest?, | ||
| 843 | + response: HTTPURLResponse, | ||
| 844 | + data: Data?, | ||
| 845 | + withResult result: Request.ValidationResult) { | ||
| 846 | + requestDidValidateRequestResponseDataWithResult?(request, urlRequest, response, data, result) | ||
| 847 | + } | ||
| 848 | + | ||
| 849 | + open func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>) { | ||
| 850 | + requestDidParseResponse?(request, response) | ||
| 851 | + } | ||
| 852 | + | ||
| 853 | + public func request(_ request: DataStreamRequest, didValidateRequest urlRequest: URLRequest?, response: HTTPURLResponse, withResult result: Request.ValidationResult) { | ||
| 854 | + requestDidValidateRequestResponseWithResult?(request, urlRequest, response, result) | ||
| 855 | + } | ||
| 856 | + | ||
| 857 | + open func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) { | ||
| 858 | + requestDidCreateUploadable?(request, uploadable) | ||
| 859 | + } | ||
| 860 | + | ||
| 861 | + open func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) { | ||
| 862 | + requestDidFailToCreateUploadableWithError?(request, error) | ||
| 863 | + } | ||
| 864 | + | ||
| 865 | + open func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) { | ||
| 866 | + requestDidProvideInputStream?(request, stream) | ||
| 867 | + } | ||
| 868 | + | ||
| 869 | + open func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>) { | ||
| 870 | + requestDidFinishDownloadingUsingTaskWithResult?(request, task, result) | ||
| 871 | + } | ||
| 872 | + | ||
| 873 | + open func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) { | ||
| 874 | + requestDidCreateDestinationURL?(request, url) | ||
| 875 | + } | ||
| 876 | + | ||
| 877 | + open func request(_ request: DownloadRequest, | ||
| 878 | + didValidateRequest urlRequest: URLRequest?, | ||
| 879 | + response: HTTPURLResponse, | ||
| 880 | + fileURL: URL?, | ||
| 881 | + withResult result: Request.ValidationResult) { | ||
| 882 | + requestDidValidateRequestResponseFileURLWithResult?(request, | ||
| 883 | + urlRequest, | ||
| 884 | + response, | ||
| 885 | + fileURL, | ||
| 886 | + result) | ||
| 887 | + } | ||
| 888 | + | ||
| 889 | + open func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>) { | ||
| 890 | + requestDidParseDownloadResponse?(request, response) | ||
| 891 | + } | ||
| 892 | +} |
Pods/Alamofire/Source/HTTPHeaders.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/HTTPHeaders.swift | ||
| 1 | +// | ||
| 2 | +// HTTPHeaders.swift | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2014-2018 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 | +import Foundation | ||
| 26 | + | ||
| 27 | +/// An order-preserving and case-insensitive representation of HTTP headers. | ||
| 28 | +public struct HTTPHeaders { | ||
| 29 | + private var headers: [HTTPHeader] = [] | ||
| 30 | + | ||
| 31 | + /// Creates an empty instance. | ||
| 32 | + public init() {} | ||
| 33 | + | ||
| 34 | + /// Creates an instance from an array of `HTTPHeader`s. Duplicate case-insensitive names are collapsed into the last | ||
| 35 | + /// name and value encountered. | ||
| 36 | + public init(_ headers: [HTTPHeader]) { | ||
| 37 | + self.init() | ||
| 38 | + | ||
| 39 | + headers.forEach { update($0) } | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + /// Creates an instance from a `[String: String]`. Duplicate case-insensitive names are collapsed into the last name | ||
| 43 | + /// and value encountered. | ||
| 44 | + public init(_ dictionary: [String: String]) { | ||
| 45 | + self.init() | ||
| 46 | + | ||
| 47 | + dictionary.forEach { update(HTTPHeader(name: $0.key, value: $0.value)) } | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`. | ||
| 51 | + /// | ||
| 52 | + /// - Parameters: | ||
| 53 | + /// - name: The `HTTPHeader` name. | ||
| 54 | + /// - value: The `HTTPHeader value. | ||
| 55 | + public mutating func add(name: String, value: String) { | ||
| 56 | + update(HTTPHeader(name: name, value: value)) | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance. | ||
| 60 | + /// | ||
| 61 | + /// - Parameter header: The `HTTPHeader` to update or append. | ||
| 62 | + public mutating func add(_ header: HTTPHeader) { | ||
| 63 | + update(header) | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`. | ||
| 67 | + /// | ||
| 68 | + /// - Parameters: | ||
| 69 | + /// - name: The `HTTPHeader` name. | ||
| 70 | + /// - value: The `HTTPHeader value. | ||
| 71 | + public mutating func update(name: String, value: String) { | ||
| 72 | + update(HTTPHeader(name: name, value: value)) | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance. | ||
| 76 | + /// | ||
| 77 | + /// - Parameter header: The `HTTPHeader` to update or append. | ||
| 78 | + public mutating func update(_ header: HTTPHeader) { | ||
| 79 | + guard let index = headers.index(of: header.name) else { | ||
| 80 | + headers.append(header) | ||
| 81 | + return | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + headers.replaceSubrange(index...index, with: [header]) | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + /// Case-insensitively removes an `HTTPHeader`, if it exists, from the instance. | ||
| 88 | + /// | ||
| 89 | + /// - Parameter name: The name of the `HTTPHeader` to remove. | ||
| 90 | + public mutating func remove(name: String) { | ||
| 91 | + guard let index = headers.index(of: name) else { return } | ||
| 92 | + | ||
| 93 | + headers.remove(at: index) | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + /// Sort the current instance by header name, case insensitively. | ||
| 97 | + public mutating func sort() { | ||
| 98 | + headers.sort { $0.name.lowercased() < $1.name.lowercased() } | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + /// Returns an instance sorted by header name. | ||
| 102 | + /// | ||
| 103 | + /// - Returns: A copy of the current instance sorted by name. | ||
| 104 | + public func sorted() -> HTTPHeaders { | ||
| 105 | + var headers = self | ||
| 106 | + headers.sort() | ||
| 107 | + | ||
| 108 | + return headers | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + /// Case-insensitively find a header's value by name. | ||
| 112 | + /// | ||
| 113 | + /// - Parameter name: The name of the header to search for, case-insensitively. | ||
| 114 | + /// | ||
| 115 | + /// - Returns: The value of header, if it exists. | ||
| 116 | + public func value(for name: String) -> String? { | ||
| 117 | + guard let index = headers.index(of: name) else { return nil } | ||
| 118 | + | ||
| 119 | + return headers[index].value | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + /// Case-insensitively access the header with the given name. | ||
| 123 | + /// | ||
| 124 | + /// - Parameter name: The name of the header. | ||
| 125 | + public subscript(_ name: String) -> String? { | ||
| 126 | + get { value(for: name) } | ||
| 127 | + set { | ||
| 128 | + if let value = newValue { | ||
| 129 | + update(name: name, value: value) | ||
| 130 | + } else { | ||
| 131 | + remove(name: name) | ||
| 132 | + } | ||
| 133 | + } | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + /// The dictionary representation of all headers. | ||
| 137 | + /// | ||
| 138 | + /// This representation does not preserve the current order of the instance. | ||
| 139 | + public var dictionary: [String: String] { | ||
| 140 | + let namesAndValues = headers.map { ($0.name, $0.value) } | ||
| 141 | + | ||
| 142 | + return Dictionary(namesAndValues, uniquingKeysWith: { _, last in last }) | ||
| 143 | + } | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +extension HTTPHeaders: ExpressibleByDictionaryLiteral { | ||
| 147 | + public init(dictionaryLiteral elements: (String, String)...) { | ||
| 148 | + self.init() | ||
| 149 | + | ||
| 150 | + elements.forEach { update(name: $0.0, value: $0.1) } | ||
| 151 | + } | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +extension HTTPHeaders: ExpressibleByArrayLiteral { | ||
| 155 | + public init(arrayLiteral elements: HTTPHeader...) { | ||
| 156 | + self.init(elements) | ||
| 157 | + } | ||
| 158 | +} | ||
| 159 | + | ||
| 160 | +extension HTTPHeaders: Sequence { | ||
| 161 | + public func makeIterator() -> IndexingIterator<[HTTPHeader]> { | ||
| 162 | + headers.makeIterator() | ||
| 163 | + } | ||
| 164 | +} | ||
| 165 | + | ||
| 166 | +extension HTTPHeaders: Collection { | ||
| 167 | + public var startIndex: Int { | ||
| 168 | + headers.startIndex | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + public var endIndex: Int { | ||
| 172 | + headers.endIndex | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + public subscript(position: Int) -> HTTPHeader { | ||
| 176 | + headers[position] | ||
| 177 | + } | ||
| 178 | + | ||
| 179 | + public func index(after i: Int) -> Int { | ||
| 180 | + headers.index(after: i) | ||
| 181 | + } | ||
| 182 | +} | ||
| 183 | + | ||
| 184 | +extension HTTPHeaders: CustomStringConvertible { | ||
| 185 | + public var description: String { | ||
| 186 | + headers.map(\.description) | ||
| 187 | + .joined(separator: "\n") | ||
| 188 | + } | ||
| 189 | +} | ||
| 190 | + | ||
| 191 | +// MARK: - HTTPHeader | ||
| 192 | + | ||
| 193 | +/// A representation of a single HTTP header's name / value pair. | ||
| 194 | +public struct HTTPHeader: Hashable { | ||
| 195 | + /// Name of the header. | ||
| 196 | + public let name: String | ||
| 197 | + | ||
| 198 | + /// Value of the header. | ||
| 199 | + public let value: String | ||
| 200 | + | ||
| 201 | + /// Creates an instance from the given `name` and `value`. | ||
| 202 | + /// | ||
| 203 | + /// - Parameters: | ||
| 204 | + /// - name: The name of the header. | ||
| 205 | + /// - value: The value of the header. | ||
| 206 | + public init(name: String, value: String) { | ||
| 207 | + self.name = name | ||
| 208 | + self.value = value | ||
| 209 | + } | ||
| 210 | +} | ||
| 211 | + | ||
| 212 | +extension HTTPHeader: CustomStringConvertible { | ||
| 213 | + public var description: String { | ||
| 214 | + "\(name): \(value)" | ||
| 215 | + } | ||
| 216 | +} | ||
| 217 | + | ||
| 218 | +extension HTTPHeader { | ||
| 219 | + /// Returns an `Accept` header. | ||
| 220 | + /// | ||
| 221 | + /// - Parameter value: The `Accept` value. | ||
| 222 | + /// - Returns: The header. | ||
| 223 | + public static func accept(_ value: String) -> HTTPHeader { | ||
| 224 | + HTTPHeader(name: "Accept", value: value) | ||
| 225 | + } | ||
| 226 | + | ||
| 227 | + /// Returns an `Accept-Charset` header. | ||
| 228 | + /// | ||
| 229 | + /// - Parameter value: The `Accept-Charset` value. | ||
| 230 | + /// - Returns: The header. | ||
| 231 | + public static func acceptCharset(_ value: String) -> HTTPHeader { | ||
| 232 | + HTTPHeader(name: "Accept-Charset", value: value) | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + /// Returns an `Accept-Language` header. | ||
| 236 | + /// | ||
| 237 | + /// Alamofire offers a default Accept-Language header that accumulates and encodes the system's preferred languages. | ||
| 238 | + /// Use `HTTPHeader.defaultAcceptLanguage`. | ||
| 239 | + /// | ||
| 240 | + /// - Parameter value: The `Accept-Language` value. | ||
| 241 | + /// | ||
| 242 | + /// - Returns: The header. | ||
| 243 | + public static func acceptLanguage(_ value: String) -> HTTPHeader { | ||
| 244 | + HTTPHeader(name: "Accept-Language", value: value) | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + /// Returns an `Accept-Encoding` header. | ||
| 248 | + /// | ||
| 249 | + /// Alamofire offers a default accept encoding value that provides the most common values. Use | ||
| 250 | + /// `HTTPHeader.defaultAcceptEncoding`. | ||
| 251 | + /// | ||
| 252 | + /// - Parameter value: The `Accept-Encoding` value. | ||
| 253 | + /// | ||
| 254 | + /// - Returns: The header | ||
| 255 | + public static func acceptEncoding(_ value: String) -> HTTPHeader { | ||
| 256 | + HTTPHeader(name: "Accept-Encoding", value: value) | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + /// Returns a `Basic` `Authorization` header using the `username` and `password` provided. | ||
| 260 | + /// | ||
| 261 | + /// - Parameters: | ||
| 262 | + /// - username: The username of the header. | ||
| 263 | + /// - password: The password of the header. | ||
| 264 | + /// | ||
| 265 | + /// - Returns: The header. | ||
| 266 | + public static func authorization(username: String, password: String) -> HTTPHeader { | ||
| 267 | + let credential = Data("\(username):\(password)".utf8).base64EncodedString() | ||
| 268 | + | ||
| 269 | + return authorization("Basic \(credential)") | ||
| 270 | + } | ||
| 271 | + | ||
| 272 | + /// Returns a `Bearer` `Authorization` header using the `bearerToken` provided | ||
| 273 | + /// | ||
| 274 | + /// - Parameter bearerToken: The bearer token. | ||
| 275 | + /// | ||
| 276 | + /// - Returns: The header. | ||
| 277 | + public static func authorization(bearerToken: String) -> HTTPHeader { | ||
| 278 | + authorization("Bearer \(bearerToken)") | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + /// Returns an `Authorization` header. | ||
| 282 | + /// | ||
| 283 | + /// Alamofire provides built-in methods to produce `Authorization` headers. For a Basic `Authorization` header use | ||
| 284 | + /// `HTTPHeader.authorization(username:password:)`. For a Bearer `Authorization` header, use | ||
| 285 | + /// `HTTPHeader.authorization(bearerToken:)`. | ||
| 286 | + /// | ||
| 287 | + /// - Parameter value: The `Authorization` value. | ||
| 288 | + /// | ||
| 289 | + /// - Returns: The header. | ||
| 290 | + public static func authorization(_ value: String) -> HTTPHeader { | ||
| 291 | + HTTPHeader(name: "Authorization", value: value) | ||
| 292 | + } | ||
| 293 | + | ||
| 294 | + /// Returns a `Content-Disposition` header. | ||
| 295 | + /// | ||
| 296 | + /// - Parameter value: The `Content-Disposition` value. | ||
| 297 | + /// | ||
| 298 | + /// - Returns: The header. | ||
| 299 | + public static func contentDisposition(_ value: String) -> HTTPHeader { | ||
| 300 | + HTTPHeader(name: "Content-Disposition", value: value) | ||
| 301 | + } | ||
| 302 | + | ||
| 303 | + /// Returns a `Content-Encoding` header. | ||
| 304 | + /// | ||
| 305 | + /// - Parameter value: The `Content-Encoding`. | ||
| 306 | + /// | ||
| 307 | + /// - Returns: The header. | ||
| 308 | + public static func contentEncoding(_ value: String) -> HTTPHeader { | ||
| 309 | + HTTPHeader(name: "Content-Encoding", value: value) | ||
| 310 | + } | ||
| 311 | + | ||
| 312 | + /// Returns a `Content-Type` header. | ||
| 313 | + /// | ||
| 314 | + /// All Alamofire `ParameterEncoding`s and `ParameterEncoder`s set the `Content-Type` of the request, so it may not | ||
| 315 | + /// be necessary to manually set this value. | ||
| 316 | + /// | ||
| 317 | + /// - Parameter value: The `Content-Type` value. | ||
| 318 | + /// | ||
| 319 | + /// - Returns: The header. | ||
| 320 | + public static func contentType(_ value: String) -> HTTPHeader { | ||
| 321 | + HTTPHeader(name: "Content-Type", value: value) | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + /// Returns a `User-Agent` header. | ||
| 325 | + /// | ||
| 326 | + /// - Parameter value: The `User-Agent` value. | ||
| 327 | + /// | ||
| 328 | + /// - Returns: The header. | ||
| 329 | + public static func userAgent(_ value: String) -> HTTPHeader { | ||
| 330 | + HTTPHeader(name: "User-Agent", value: value) | ||
| 331 | + } | ||
| 332 | +} | ||
| 333 | + | ||
| 334 | +extension Array where Element == HTTPHeader { | ||
| 335 | + /// Case-insensitively finds the index of an `HTTPHeader` with the provided name, if it exists. | ||
| 336 | + func index(of name: String) -> Int? { | ||
| 337 | + let lowercasedName = name.lowercased() | ||
| 338 | + return firstIndex { $0.name.lowercased() == lowercasedName } | ||
| 339 | + } | ||
| 340 | +} | ||
| 341 | + | ||
| 342 | +// MARK: - Defaults | ||
| 343 | + | ||
| 344 | +extension HTTPHeaders { | ||
| 345 | + /// The default set of `HTTPHeaders` used by Alamofire. Includes `Accept-Encoding`, `Accept-Language`, and | ||
| 346 | + /// `User-Agent`. | ||
| 347 | + public static let `default`: HTTPHeaders = [.defaultAcceptEncoding, | ||
| 348 | + .defaultAcceptLanguage, | ||
| 349 | + .defaultUserAgent] | ||
| 350 | +} | ||
| 351 | + | ||
| 352 | +extension HTTPHeader { | ||
| 353 | + /// Returns Alamofire's default `Accept-Encoding` header, appropriate for the encodings supported by particular OS | ||
| 354 | + /// versions. | ||
| 355 | + /// | ||
| 356 | + /// See the [Accept-Encoding HTTP header documentation](https://tools.ietf.org/html/rfc7230#section-4.2.3) . | ||
| 357 | + public static let defaultAcceptEncoding: HTTPHeader = { | ||
| 358 | + let encodings: [String] | ||
| 359 | + if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) { | ||
| 360 | + encodings = ["br", "gzip", "deflate"] | ||
| 361 | + } else { | ||
| 362 | + encodings = ["gzip", "deflate"] | ||
| 363 | + } | ||
| 364 | + | ||
| 365 | + return .acceptEncoding(encodings.qualityEncoded()) | ||
| 366 | + }() | ||
| 367 | + | ||
| 368 | + /// Returns Alamofire's default `Accept-Language` header, generated by querying `Locale` for the user's | ||
| 369 | + /// `preferredLanguages`. | ||
| 370 | + /// | ||
| 371 | + /// See the [Accept-Language HTTP header documentation](https://tools.ietf.org/html/rfc7231#section-5.3.5). | ||
| 372 | + public static let defaultAcceptLanguage: HTTPHeader = .acceptLanguage(Locale.preferredLanguages.prefix(6).qualityEncoded()) | ||
| 373 | + | ||
| 374 | + /// Returns Alamofire's default `User-Agent` header. | ||
| 375 | + /// | ||
| 376 | + /// See the [User-Agent header documentation](https://tools.ietf.org/html/rfc7231#section-5.5.3). | ||
| 377 | + /// | ||
| 378 | + /// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0` | ||
| 379 | + public static let defaultUserAgent: HTTPHeader = { | ||
| 380 | + let info = Bundle.main.infoDictionary | ||
| 381 | + let executable = (info?["CFBundleExecutable"] as? String) ?? | ||
| 382 | + (ProcessInfo.processInfo.arguments.first?.split(separator: "/").last.map(String.init)) ?? | ||
| 383 | + "Unknown" | ||
| 384 | + let bundle = info?["CFBundleIdentifier"] as? String ?? "Unknown" | ||
| 385 | + let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown" | ||
| 386 | + let appBuild = info?["CFBundleVersion"] as? String ?? "Unknown" | ||
| 387 | + | ||
| 388 | + let osNameVersion: String = { | ||
| 389 | + let version = ProcessInfo.processInfo.operatingSystemVersion | ||
| 390 | + let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)" | ||
| 391 | + let osName: String = { | ||
| 392 | + #if os(iOS) | ||
| 393 | + #if targetEnvironment(macCatalyst) | ||
| 394 | + return "macOS(Catalyst)" | ||
| 395 | + #else | ||
| 396 | + return "iOS" | ||
| 397 | + #endif | ||
| 398 | + #elseif os(watchOS) | ||
| 399 | + return "watchOS" | ||
| 400 | + #elseif os(tvOS) | ||
| 401 | + return "tvOS" | ||
| 402 | + #elseif os(macOS) | ||
| 403 | + return "macOS" | ||
| 404 | + #elseif os(Linux) | ||
| 405 | + return "Linux" | ||
| 406 | + #elseif os(Windows) | ||
| 407 | + return "Windows" | ||
| 408 | + #else | ||
| 409 | + return "Unknown" | ||
| 410 | + #endif | ||
| 411 | + }() | ||
| 412 | + | ||
| 413 | + return "\(osName) \(versionString)" | ||
| 414 | + }() | ||
| 415 | + | ||
| 416 | + let alamofireVersion = "Alamofire/\(version)" | ||
| 417 | + | ||
| 418 | + let userAgent = "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)" | ||
| 419 | + | ||
| 420 | + return .userAgent(userAgent) | ||
| 421 | + }() | ||
| 422 | +} | ||
| 423 | + | ||
| 424 | +extension Collection where Element == String { | ||
| 425 | + func qualityEncoded() -> String { | ||
| 426 | + enumerated().map { index, encoding in | ||
| 427 | + let quality = 1.0 - (Double(index) * 0.1) | ||
| 428 | + return "\(encoding);q=\(quality)" | ||
| 429 | + }.joined(separator: ", ") | ||
| 430 | + } | ||
| 431 | +} | ||
| 432 | + | ||
| 433 | +// MARK: - System Type Extensions | ||
| 434 | + | ||
| 435 | +extension URLRequest { | ||
| 436 | + /// Returns `allHTTPHeaderFields` as `HTTPHeaders`. | ||
| 437 | + public var headers: HTTPHeaders { | ||
| 438 | + get { allHTTPHeaderFields.map(HTTPHeaders.init) ?? HTTPHeaders() } | ||
| 439 | + set { allHTTPHeaderFields = newValue.dictionary } | ||
| 440 | + } | ||
| 441 | +} | ||
| 442 | + | ||
| 443 | +extension HTTPURLResponse { | ||
| 444 | + /// Returns `allHeaderFields` as `HTTPHeaders`. | ||
| 445 | + public var headers: HTTPHeaders { | ||
| 446 | + (allHeaderFields as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() | ||
| 447 | + } | ||
| 448 | +} | ||
| 449 | + | ||
| 450 | +extension URLSessionConfiguration { | ||
| 451 | + /// Returns `httpAdditionalHeaders` as `HTTPHeaders`. | ||
| 452 | + public var headers: HTTPHeaders { | ||
| 453 | + get { (httpAdditionalHeaders as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() } | ||
| 454 | + set { httpAdditionalHeaders = newValue.dictionary } | ||
| 455 | + } | ||
| 456 | +} |
Pods/Alamofire/Source/HTTPMethod.swift
0 → 100644
| 1 | +++ a/Pods/Alamofire/Source/HTTPMethod.swift | ||
| 1 | +// | ||
| 2 | +// HTTPMethod.swift | ||
| 3 | +// | ||
| 4 | +// Copyright (c) 2014-2018 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 | +/// Type representing HTTP methods. Raw `String` value is stored and compared case-sensitively, so | ||
| 26 | +/// `HTTPMethod.get != HTTPMethod(rawValue: "get")`. | ||
| 27 | +/// | ||
| 28 | +/// See https://tools.ietf.org/html/rfc7231#section-4.3 | ||
| 29 | +public struct HTTPMethod: RawRepresentable, Equatable, Hashable { | ||
| 30 | + /// `CONNECT` method. | ||
| 31 | + public static let connect = HTTPMethod(rawValue: "CONNECT") | ||
| 32 | + /// `DELETE` method. | ||
| 33 | + public static let delete = HTTPMethod(rawValue: "DELETE") | ||
| 34 | + /// `GET` method. | ||
| 35 | + public static let get = HTTPMethod(rawValue: "GET") | ||
| 36 | + /// `HEAD` method. | ||
| 37 | + public static let head = HTTPMethod(rawValue: "HEAD") | ||
| 38 | + /// `OPTIONS` method. | ||
| 39 | + public static let options = HTTPMethod(rawValue: "OPTIONS") | ||
| 40 | + /// `PATCH` method. | ||
| 41 | + public static let patch = HTTPMethod(rawValue: "PATCH") | ||
| 42 | + /// `POST` method. | ||
| 43 | + public static let post = HTTPMethod(rawValue: "POST") | ||
| 44 | + /// `PUT` method. | ||
| 45 | + public static let put = HTTPMethod(rawValue: "PUT") | ||
| 46 | + /// `QUERY` method. | ||
| 47 | + public static let query = HTTPMethod(rawValue: "QUERY") | ||
| 48 | + /// `TRACE` method. | ||
| 49 | + public static let trace = HTTPMethod(rawValue: "TRACE") | ||
| 50 | + | ||
| 51 | + public let rawValue: String | ||
| 52 | + | ||
| 53 | + public init(rawValue: String) { | ||
| 54 | + self.rawValue = rawValue | ||
| 55 | + } | ||
| 56 | +} |