去掉不适用的库
This commit is contained in:
parent
d8edd35d83
commit
d590ddb4b2
@ -29,7 +29,7 @@ pod 'LLCycleScrollView'
|
|||||||
#媒体缓存
|
#媒体缓存
|
||||||
#pod 'KTVHTTPCache'
|
#pod 'KTVHTTPCache'
|
||||||
pod 'SnapKit'
|
pod 'SnapKit'
|
||||||
pod 'Alamofire'
|
#pod 'Alamofire'
|
||||||
pod 'DeviceKit' # 设备信息
|
pod 'DeviceKit' # 设备信息
|
||||||
pod 'SVProgressHUD'
|
pod 'SVProgressHUD'
|
||||||
pod 'TZImagePickerController' #图片视频选择
|
pod 'TZImagePickerController' #图片视频选择
|
||||||
|
|||||||
@ -815,7 +815,6 @@ PODS:
|
|||||||
- abseil/base/base_internal
|
- abseil/base/base_internal
|
||||||
- abseil/base/config
|
- abseil/base/config
|
||||||
- abseil/meta/type_traits
|
- abseil/meta/type_traits
|
||||||
- Alamofire (5.8.1)
|
|
||||||
- BoringSSL-GRPC (0.0.32):
|
- BoringSSL-GRPC (0.0.32):
|
||||||
- BoringSSL-GRPC/Implementation (= 0.0.32)
|
- BoringSSL-GRPC/Implementation (= 0.0.32)
|
||||||
- BoringSSL-GRPC/Interface (= 0.0.32)
|
- BoringSSL-GRPC/Interface (= 0.0.32)
|
||||||
@ -1058,7 +1057,6 @@ PODS:
|
|||||||
- TZImagePickerController/Location (3.8.4)
|
- TZImagePickerController/Location (3.8.4)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Alamofire
|
|
||||||
- DeviceKit
|
- DeviceKit
|
||||||
- FacebookCore
|
- FacebookCore
|
||||||
- Firebase/AnalyticsWithoutAdIdSupport
|
- Firebase/AnalyticsWithoutAdIdSupport
|
||||||
@ -1076,7 +1074,6 @@ DEPENDENCIES:
|
|||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- abseil
|
- abseil
|
||||||
- Alamofire
|
|
||||||
- BoringSSL-GRPC
|
- BoringSSL-GRPC
|
||||||
- DeviceKit
|
- DeviceKit
|
||||||
- FacebookCore
|
- FacebookCore
|
||||||
@ -1118,7 +1115,6 @@ SPEC REPOS:
|
|||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
abseil: ebec4f56469dd7ce9ab08683c0319a68aa0ad86e
|
abseil: ebec4f56469dd7ce9ab08683c0319a68aa0ad86e
|
||||||
Alamofire: 3ca42e259043ee0dc5c0cdd76c4bc568b8e42af7
|
|
||||||
BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6
|
BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6
|
||||||
DeviceKit: e36aaf2a0d142ef0b4fac2007649a4414af234be
|
DeviceKit: e36aaf2a0d142ef0b4fac2007649a4414af234be
|
||||||
FacebookCore: ba86524b66cfa86d0f8e65d08faa8504a9f732dd
|
FacebookCore: ba86524b66cfa86d0f8e65d08faa8504a9f732dd
|
||||||
@ -1158,6 +1154,6 @@ SPEC CHECKSUMS:
|
|||||||
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
|
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
|
||||||
TZImagePickerController: f1c9f1cae6ac0e30b31aaa9698f9bf4a7cf5b84f
|
TZImagePickerController: f1c9f1cae6ac0e30b31aaa9698f9bf4a7cf5b84f
|
||||||
|
|
||||||
PODFILE CHECKSUM: ff22192ba29fe62575b8eaf5e55d1e1892047ee0
|
PODFILE CHECKSUM: cdc4cc3bc05e95dd4ba77e4128af08fd8e18aa1f
|
||||||
|
|
||||||
COCOAPODS: 1.15.2
|
COCOAPODS: 1.15.2
|
||||||
|
|||||||
19
SwiftProject/Pods/Alamofire/LICENSE
generated
19
SwiftProject/Pods/Alamofire/LICENSE
generated
@ -1,19 +0,0 @@
|
|||||||
Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
256
SwiftProject/Pods/Alamofire/README.md
generated
256
SwiftProject/Pods/Alamofire/README.md
generated
@ -1,256 +0,0 @@
|
|||||||

|
|
||||||
|
|
||||||
[](https://img.shields.io/badge/Swift-5.6_5.7_5.8_5.9-Orange?style=flat-square)
|
|
||||||
[](https://img.shields.io/badge/Platforms-macOS_iOS_tvOS_watchOS_vision_OS_Linux_Windows_Android-Green?style=flat-square)
|
|
||||||
[](https://img.shields.io/cocoapods/v/Alamofire.svg)
|
|
||||||
[](https://github.com/Carthage/Carthage)
|
|
||||||
[](https://img.shields.io/badge/Swift_Package_Manager-compatible-orange?style=flat-square)
|
|
||||||
[](https://forums.swift.org/c/related-projects/alamofire/37)
|
|
||||||
|
|
||||||
Alamofire is an HTTP networking library written in Swift.
|
|
||||||
|
|
||||||
- [Features](#features)
|
|
||||||
- [Component Libraries](#component-libraries)
|
|
||||||
- [Requirements](#requirements)
|
|
||||||
- [Migration Guides](#migration-guides)
|
|
||||||
- [Communication](#communication)
|
|
||||||
- [Installation](#installation)
|
|
||||||
- [Contributing](#contributing)
|
|
||||||
- [Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#using-alamofire)
|
|
||||||
- [**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)
|
|
||||||
- **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)
|
|
||||||
- **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)
|
|
||||||
- **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)
|
|
||||||
- [Advanced Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md)
|
|
||||||
- **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)
|
|
||||||
- **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)
|
|
||||||
- **Model Objects -** [Custom Response Handlers](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#customizing-response-handlers)
|
|
||||||
- **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)
|
|
||||||
- **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)
|
|
||||||
- [Open Radars](#open-radars)
|
|
||||||
- [FAQ](#faq)
|
|
||||||
- [Credits](#credits)
|
|
||||||
- [Donations](#donations)
|
|
||||||
- [License](#license)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- [x] Chainable Request / Response Methods
|
|
||||||
- [x] Swift Concurrency Support Back to iOS 13, macOS 10.15, tvOS 13, and watchOS 6.
|
|
||||||
- [x] Combine Support
|
|
||||||
- [x] URL / JSON Parameter Encoding
|
|
||||||
- [x] Upload File / Data / Stream / MultipartFormData
|
|
||||||
- [x] Download File using Request or Resume Data
|
|
||||||
- [x] Authentication with `URLCredential`
|
|
||||||
- [x] HTTP Response Validation
|
|
||||||
- [x] Upload and Download Progress Closures with Progress
|
|
||||||
- [x] cURL Command Output
|
|
||||||
- [x] Dynamically Adapt and Retry Requests
|
|
||||||
- [x] TLS Certificate and Public Key Pinning
|
|
||||||
- [x] Network Reachability
|
|
||||||
- [x] Comprehensive Unit and Integration Test Coverage
|
|
||||||
- [x] [Complete Documentation](https://alamofire.github.io/Alamofire)
|
|
||||||
|
|
||||||
## Write Requests Fast!
|
|
||||||
|
|
||||||
Alamofire's compact syntax and extensive feature set allow requests with powerful features like automatic retry to be written in just a few lines of code.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
// Automatic String to URL conversion, Swift concurrency support, and automatic retry.
|
|
||||||
let response = await AF.request("https://httpbin.org/get", interceptor: .retryPolicy)
|
|
||||||
// Automatic HTTP Basic Auth.
|
|
||||||
.authenticate(username: "user", password: "pass")
|
|
||||||
// Caching customization.
|
|
||||||
.cacheResponse(using: .cache)
|
|
||||||
// Redirect customization.
|
|
||||||
.redirect(using: .follow)
|
|
||||||
// Validate response code and Content-Type.
|
|
||||||
.validate()
|
|
||||||
// Produce a cURL command for the request.
|
|
||||||
.cURLDescription { description in
|
|
||||||
print(description)
|
|
||||||
}
|
|
||||||
// Automatic Decodable support with background parsing.
|
|
||||||
.serializingDecodable(DecodableType.self)
|
|
||||||
// Await the full response with metrics and a parsed body.
|
|
||||||
.response
|
|
||||||
// Detailed response description for easy debugging.
|
|
||||||
debugPrint(response)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Component Libraries
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
- [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.
|
|
||||||
- [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.
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
| Platform | Minimum Swift Version | Installation | Status |
|
|
||||||
| ---------------------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
||||||
| iOS 10.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+ | 5.6 | [CocoaPods](#cocoapods), [Carthage](#carthage), [Swift Package Manager](#swift-package-manager), [Manual](#manually) | Fully Tested |
|
|
||||||
| Linux | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported |
|
|
||||||
| Windows | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported |
|
|
||||||
| Android | Latest Only | [Swift Package Manager](#swift-package-manager) | Building But Unsupported |
|
|
||||||
|
|
||||||
#### Known Issues on Linux and Windows
|
|
||||||
|
|
||||||
Alamofire builds on Linux, Windows, and Android but there are missing features and many issues in the underlying `swift-corelibs-foundation` that prevent full functionality and may cause crashes. These include:
|
|
||||||
|
|
||||||
- `ServerTrustManager` and associated certificate functionality is unavailable, so there is no certificate pinning and no client certificate support.
|
|
||||||
- Various methods of HTTP authentication may crash, including HTTP Basic and HTTP Digest. Crashes may occur if responses contain server challenges.
|
|
||||||
- Cache control through `CachedResponseHandler` and associated APIs is unavailable, as the underlying delegate methods aren't called.
|
|
||||||
- `URLSessionTaskMetrics` are never gathered.
|
|
||||||
|
|
||||||
Due to these issues, Alamofire is unsupported on Linux, Windows, and Android. Please report any crashes to the [Swift bug reporter](https://bugs.swift.org).
|
|
||||||
|
|
||||||
## Migration Guides
|
|
||||||
|
|
||||||
- [Alamofire 5.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%205.0%20Migration%20Guide.md)
|
|
||||||
- [Alamofire 4.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md)
|
|
||||||
- [Alamofire 3.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md)
|
|
||||||
- [Alamofire 2.0 Migration Guide](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%202.0%20Migration%20Guide.md)
|
|
||||||
|
|
||||||
## Communication
|
|
||||||
|
|
||||||
- If you **need help with making network requests** using Alamofire, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire) and tag `alamofire`.
|
|
||||||
- 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.
|
|
||||||
- If you need **help with an Alamofire feature**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire).
|
|
||||||
- If you'd like to **discuss Alamofire best practices**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire).
|
|
||||||
- If you'd like to **discuss a feature request**, use [our forum on swift.org](https://forums.swift.org/c/related-projects/alamofire).
|
|
||||||
- If you **found a bug**, open an issue here on GitHub and follow the guide. The more detail the better!
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### CocoaPods
|
|
||||||
|
|
||||||
[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`:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
pod 'Alamofire'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Carthage
|
|
||||||
|
|
||||||
[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`:
|
|
||||||
|
|
||||||
```ogdl
|
|
||||||
github "Alamofire/Alamofire"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Swift Package Manager
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
dependencies: [
|
|
||||||
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.8.1"))
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manually
|
|
||||||
|
|
||||||
If you prefer not to use any of the aforementioned dependency managers, you can integrate Alamofire into your project manually.
|
|
||||||
|
|
||||||
#### Embedded Framework
|
|
||||||
|
|
||||||
- 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:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git init
|
|
||||||
```
|
|
||||||
|
|
||||||
- Add Alamofire as a git [submodule](https://git-scm.com/docs/git-submodule) by running the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git submodule add https://github.com/Alamofire/Alamofire.git
|
|
||||||
```
|
|
||||||
|
|
||||||
- Open the new `Alamofire` folder, and drag the `Alamofire.xcodeproj` into the Project Navigator of your application's Xcode project.
|
|
||||||
|
|
||||||
> 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.
|
|
||||||
|
|
||||||
- Select the `Alamofire.xcodeproj` in the Project Navigator and verify the deployment target matches that of your application target.
|
|
||||||
- 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.
|
|
||||||
- In the tab bar at the top of that window, open the "General" panel.
|
|
||||||
- Click on the `+` button under the "Embedded Binaries" section.
|
|
||||||
- You will see two different `Alamofire.xcodeproj` folders each with two different versions of the `Alamofire.framework` nested inside a `Products` folder.
|
|
||||||
|
|
||||||
> It does not matter which `Products` folder you choose from, but it does matter whether you choose the top or bottom `Alamofire.framework`.
|
|
||||||
|
|
||||||
- Select the top `Alamofire.framework` for iOS and the bottom one for macOS.
|
|
||||||
|
|
||||||
> 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`.
|
|
||||||
|
|
||||||
- And that's it!
|
|
||||||
|
|
||||||
> 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.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Before contributing to Alamofire, please read the instructions detailed in our [contribution guide](https://github.com/Alamofire/Alamofire/blob/master/CONTRIBUTING.md).
|
|
||||||
|
|
||||||
## Open Radars
|
|
||||||
|
|
||||||
The following radars have some effect on the current implementation of Alamofire.
|
|
||||||
|
|
||||||
- [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in the test case
|
|
||||||
- `rdar://26870455` - Background URL Session Configurations do not work in the simulator
|
|
||||||
- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest`
|
|
||||||
|
|
||||||
## Resolved Radars
|
|
||||||
|
|
||||||
The following radars have been resolved over time after being filed against the Alamofire project.
|
|
||||||
|
|
||||||
- [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage.
|
|
||||||
- (Resolved): 9/1/17 in Xcode 9 beta 6.
|
|
||||||
- [`rdar://36082113`](http://openradar.appspot.com/radar?id=4942308441063424) - `URLSessionTaskMetrics` failing to link on watchOS 3.0+
|
|
||||||
- (Resolved): Just add `CFNetwork` to your linked frameworks.
|
|
||||||
- `FB7624529` - `urlSession(_:task:didFinishCollecting:)` never called on watchOS
|
|
||||||
- (Resolved): Metrics now collected on watchOS 7+.
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
### What's the origin of the name Alamofire?
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
### Security Disclosure
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Sponsorship
|
|
||||||
|
|
||||||
The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise money to officially stay registered as a federal non-profit organization.
|
|
||||||
Registering will allow Foundation members to gain some legal protections and also allow us to put donations to use, tax-free.
|
|
||||||
Sponsoring the ASF will enable us to:
|
|
||||||
|
|
||||||
- Pay our yearly legal fees to keep the non-profit in good status
|
|
||||||
- Pay for our mail servers to help us stay on top of all questions and security issues
|
|
||||||
- Potentially fund test servers to make it easier for us to test the edge cases
|
|
||||||
- Potentially fund developers to work on one of our projects full-time
|
|
||||||
|
|
||||||
The community adoption of the ASF libraries has been amazing.
|
|
||||||
We are greatly humbled by your enthusiasm around the projects and want to continue to do everything we can to move the needle forward.
|
|
||||||
With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members.
|
|
||||||
If you use any of our libraries for work, see if your employers would be interested in donating.
|
|
||||||
Any amount you can donate, whether once or monthly, to help us reach our goal would be greatly appreciated.
|
|
||||||
|
|
||||||
[Sponsor Alamofire](https://github.com/sponsors/Alamofire)
|
|
||||||
|
|
||||||
## Supporters
|
|
||||||
|
|
||||||
[MacStadium](https://macstadium.com) provides Alamofire with a free, hosted Mac mini.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Alamofire is released under the MIT license. [See LICENSE](https://github.com/Alamofire/Alamofire/blob/master/LICENSE) for details.
|
|
||||||
874
SwiftProject/Pods/Alamofire/Source/AFError.swift
generated
874
SwiftProject/Pods/Alamofire/Source/AFError.swift
generated
@ -1,874 +0,0 @@
|
|||||||
//
|
|
||||||
// AFError.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
#if canImport(Security)
|
|
||||||
import Security
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with
|
|
||||||
/// their own associated reasons.
|
|
||||||
public enum AFError: Error {
|
|
||||||
/// The underlying reason the `.multipartEncodingFailed` error occurred.
|
|
||||||
public enum MultipartEncodingFailureReason {
|
|
||||||
/// The `fileURL` provided for reading an encodable body part isn't a file `URL`.
|
|
||||||
case bodyPartURLInvalid(url: URL)
|
|
||||||
/// The filename of the `fileURL` provided has either an empty `lastPathComponent` or `pathExtension.
|
|
||||||
case bodyPartFilenameInvalid(in: URL)
|
|
||||||
/// The file at the `fileURL` provided was not reachable.
|
|
||||||
case bodyPartFileNotReachable(at: URL)
|
|
||||||
/// Attempting to check the reachability of the `fileURL` provided threw an error.
|
|
||||||
case bodyPartFileNotReachableWithError(atURL: URL, error: Error)
|
|
||||||
/// The file at the `fileURL` provided is actually a directory.
|
|
||||||
case bodyPartFileIsDirectory(at: URL)
|
|
||||||
/// The size of the file at the `fileURL` provided was not returned by the system.
|
|
||||||
case bodyPartFileSizeNotAvailable(at: URL)
|
|
||||||
/// The attempt to find the size of the file at the `fileURL` provided threw an error.
|
|
||||||
case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error)
|
|
||||||
/// An `InputStream` could not be created for the provided `fileURL`.
|
|
||||||
case bodyPartInputStreamCreationFailed(for: URL)
|
|
||||||
/// An `OutputStream` could not be created when attempting to write the encoded data to disk.
|
|
||||||
case outputStreamCreationFailed(for: URL)
|
|
||||||
/// The encoded body data could not be written to disk because a file already exists at the provided `fileURL`.
|
|
||||||
case outputStreamFileAlreadyExists(at: URL)
|
|
||||||
/// The `fileURL` provided for writing the encoded body data to disk is not a file `URL`.
|
|
||||||
case outputStreamURLInvalid(url: URL)
|
|
||||||
/// The attempt to write the encoded body data to disk failed with an underlying error.
|
|
||||||
case outputStreamWriteFailed(error: Error)
|
|
||||||
/// The attempt to read an encoded body part `InputStream` failed with underlying system error.
|
|
||||||
case inputStreamReadFailed(error: Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents unexpected input stream length that occur when encoding the `MultipartFormData`. Instances will be
|
|
||||||
/// embedded within an `AFError.multipartEncodingFailed` `.inputStreamReadFailed` case.
|
|
||||||
public struct UnexpectedInputStreamLength: Error {
|
|
||||||
/// The expected byte count to read.
|
|
||||||
public var bytesExpected: UInt64
|
|
||||||
/// The actual byte count read.
|
|
||||||
public var bytesRead: UInt64
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The underlying reason the `.parameterEncodingFailed` error occurred.
|
|
||||||
public enum ParameterEncodingFailureReason {
|
|
||||||
/// The `URLRequest` did not have a `URL` to encode.
|
|
||||||
case missingURL
|
|
||||||
/// JSON serialization failed with an underlying system error during the encoding process.
|
|
||||||
case jsonEncodingFailed(error: Error)
|
|
||||||
/// Custom parameter encoding failed due to the associated `Error`.
|
|
||||||
case customEncodingFailed(error: Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The underlying reason the `.parameterEncoderFailed` error occurred.
|
|
||||||
public enum ParameterEncoderFailureReason {
|
|
||||||
/// Possible missing components.
|
|
||||||
public enum RequiredComponent {
|
|
||||||
/// The `URL` was missing or unable to be extracted from the passed `URLRequest` or during encoding.
|
|
||||||
case url
|
|
||||||
/// The `HTTPMethod` could not be extracted from the passed `URLRequest`.
|
|
||||||
case httpMethod(rawValue: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `RequiredComponent` was missing during encoding.
|
|
||||||
case missingRequiredComponent(RequiredComponent)
|
|
||||||
/// The underlying encoder failed with the associated error.
|
|
||||||
case encoderFailed(error: Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The underlying reason the `.responseValidationFailed` error occurred.
|
|
||||||
public enum ResponseValidationFailureReason {
|
|
||||||
/// The data file containing the server response did not exist.
|
|
||||||
case dataFileNil
|
|
||||||
/// The data file containing the server response at the associated `URL` could not be read.
|
|
||||||
case dataFileReadFailed(at: URL)
|
|
||||||
/// The response did not contain a `Content-Type` and the `acceptableContentTypes` provided did not contain a
|
|
||||||
/// wildcard type.
|
|
||||||
case missingContentType(acceptableContentTypes: [String])
|
|
||||||
/// The response `Content-Type` did not match any type in the provided `acceptableContentTypes`.
|
|
||||||
case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String)
|
|
||||||
/// The response status code was not acceptable.
|
|
||||||
case unacceptableStatusCode(code: Int)
|
|
||||||
/// Custom response validation failed due to the associated `Error`.
|
|
||||||
case customValidationFailed(error: Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The underlying reason the response serialization error occurred.
|
|
||||||
public enum ResponseSerializationFailureReason {
|
|
||||||
/// The server response contained no data or the data was zero length.
|
|
||||||
case inputDataNilOrZeroLength
|
|
||||||
/// The file containing the server response did not exist.
|
|
||||||
case inputFileNil
|
|
||||||
/// The file containing the server response could not be read from the associated `URL`.
|
|
||||||
case inputFileReadFailed(at: URL)
|
|
||||||
/// String serialization failed using the provided `String.Encoding`.
|
|
||||||
case stringSerializationFailed(encoding: String.Encoding)
|
|
||||||
/// JSON serialization failed with an underlying system error.
|
|
||||||
case jsonSerializationFailed(error: Error)
|
|
||||||
/// A `DataDecoder` failed to decode the response due to the associated `Error`.
|
|
||||||
case decodingFailed(error: Error)
|
|
||||||
/// A custom response serializer failed due to the associated `Error`.
|
|
||||||
case customSerializationFailed(error: Error)
|
|
||||||
/// Generic serialization failed for an empty response that wasn't type `Empty` but instead the associated type.
|
|
||||||
case invalidEmptyResponse(type: String)
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(Security)
|
|
||||||
/// Underlying reason a server trust evaluation error occurred.
|
|
||||||
public enum ServerTrustFailureReason {
|
|
||||||
/// The output of a server trust evaluation.
|
|
||||||
public struct Output {
|
|
||||||
/// The host for which the evaluation was performed.
|
|
||||||
public let host: String
|
|
||||||
/// The `SecTrust` value which was evaluated.
|
|
||||||
public let trust: SecTrust
|
|
||||||
/// The `OSStatus` of evaluation operation.
|
|
||||||
public let status: OSStatus
|
|
||||||
/// The result of the evaluation operation.
|
|
||||||
public let result: SecTrustResultType
|
|
||||||
|
|
||||||
/// Creates an `Output` value from the provided values.
|
|
||||||
init(_ host: String, _ trust: SecTrust, _ status: OSStatus, _ result: SecTrustResultType) {
|
|
||||||
self.host = host
|
|
||||||
self.trust = trust
|
|
||||||
self.status = status
|
|
||||||
self.result = result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// No `ServerTrustEvaluator` was found for the associated host.
|
|
||||||
case noRequiredEvaluator(host: String)
|
|
||||||
/// No certificates were found with which to perform the trust evaluation.
|
|
||||||
case noCertificatesFound
|
|
||||||
/// No public keys were found with which to perform the trust evaluation.
|
|
||||||
case noPublicKeysFound
|
|
||||||
/// During evaluation, application of the associated `SecPolicy` failed.
|
|
||||||
case policyApplicationFailed(trust: SecTrust, policy: SecPolicy, status: OSStatus)
|
|
||||||
/// During evaluation, setting the associated anchor certificates failed.
|
|
||||||
case settingAnchorCertificatesFailed(status: OSStatus, certificates: [SecCertificate])
|
|
||||||
/// During evaluation, creation of the revocation policy failed.
|
|
||||||
case revocationPolicyCreationFailed
|
|
||||||
/// `SecTrust` evaluation failed with the associated `Error`, if one was produced.
|
|
||||||
case trustEvaluationFailed(error: Error?)
|
|
||||||
/// Default evaluation failed with the associated `Output`.
|
|
||||||
case defaultEvaluationFailed(output: Output)
|
|
||||||
/// Host validation failed with the associated `Output`.
|
|
||||||
case hostValidationFailed(output: Output)
|
|
||||||
/// Revocation check failed with the associated `Output` and options.
|
|
||||||
case revocationCheckFailed(output: Output, options: RevocationTrustEvaluator.Options)
|
|
||||||
/// Certificate pinning failed.
|
|
||||||
case certificatePinningFailed(host: String, trust: SecTrust, pinnedCertificates: [SecCertificate], serverCertificates: [SecCertificate])
|
|
||||||
/// Public key pinning failed.
|
|
||||||
case publicKeyPinningFailed(host: String, trust: SecTrust, pinnedKeys: [SecKey], serverKeys: [SecKey])
|
|
||||||
/// Custom server trust evaluation failed due to the associated `Error`.
|
|
||||||
case customEvaluationFailed(error: Error)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// The underlying reason the `.urlRequestValidationFailed`
|
|
||||||
public enum URLRequestValidationFailureReason {
|
|
||||||
/// URLRequest with GET method had body data.
|
|
||||||
case bodyDataInGETRequest(Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `UploadableConvertible` threw an error in `createUploadable()`.
|
|
||||||
case createUploadableFailed(error: Error)
|
|
||||||
/// `URLRequestConvertible` threw an error in `asURLRequest()`.
|
|
||||||
case createURLRequestFailed(error: Error)
|
|
||||||
/// `SessionDelegate` threw an error while attempting to move downloaded file to destination URL.
|
|
||||||
case downloadedFileMoveFailed(error: Error, source: URL, destination: URL)
|
|
||||||
/// `Request` was explicitly cancelled.
|
|
||||||
case explicitlyCancelled
|
|
||||||
/// `URLConvertible` type failed to create a valid `URL`.
|
|
||||||
case invalidURL(url: URLConvertible)
|
|
||||||
/// Multipart form encoding failed.
|
|
||||||
case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
|
|
||||||
/// `ParameterEncoding` threw an error during the encoding process.
|
|
||||||
case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
|
|
||||||
/// `ParameterEncoder` threw an error while running the encoder.
|
|
||||||
case parameterEncoderFailed(reason: ParameterEncoderFailureReason)
|
|
||||||
/// `RequestAdapter` threw an error during adaptation.
|
|
||||||
case requestAdaptationFailed(error: Error)
|
|
||||||
/// `RequestRetrier` threw an error during the request retry process.
|
|
||||||
case requestRetryFailed(retryError: Error, originalError: Error)
|
|
||||||
/// Response validation failed.
|
|
||||||
case responseValidationFailed(reason: ResponseValidationFailureReason)
|
|
||||||
/// Response serialization failed.
|
|
||||||
case responseSerializationFailed(reason: ResponseSerializationFailureReason)
|
|
||||||
#if canImport(Security)
|
|
||||||
/// `ServerTrustEvaluating` instance threw an error during trust evaluation.
|
|
||||||
case serverTrustEvaluationFailed(reason: ServerTrustFailureReason)
|
|
||||||
#endif
|
|
||||||
/// `Session` which issued the `Request` was deinitialized, most likely because its reference went out of scope.
|
|
||||||
case sessionDeinitialized
|
|
||||||
/// `Session` was explicitly invalidated, possibly with the `Error` produced by the underlying `URLSession`.
|
|
||||||
case sessionInvalidated(error: Error?)
|
|
||||||
/// `URLSessionTask` completed with error.
|
|
||||||
case sessionTaskFailed(error: Error)
|
|
||||||
/// `URLRequest` failed validation.
|
|
||||||
case urlRequestValidationFailed(reason: URLRequestValidationFailureReason)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Error {
|
|
||||||
/// Returns the instance cast as an `AFError`.
|
|
||||||
public var asAFError: AFError? {
|
|
||||||
self as? AFError
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the instance cast as an `AFError`. If casting fails, a `fatalError` with the specified `message` is thrown.
|
|
||||||
public func asAFError(orFailWith message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> AFError {
|
|
||||||
guard let afError = self as? AFError else {
|
|
||||||
fatalError(message(), file: file, line: line)
|
|
||||||
}
|
|
||||||
return afError
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Casts the instance as `AFError` or returns `defaultAFError`
|
|
||||||
func asAFError(or defaultAFError: @autoclosure () -> AFError) -> AFError {
|
|
||||||
self as? AFError ?? defaultAFError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Error Booleans
|
|
||||||
|
|
||||||
extension AFError {
|
|
||||||
/// Returns whether the instance is `.sessionDeinitialized`.
|
|
||||||
public var isSessionDeinitializedError: Bool {
|
|
||||||
if case .sessionDeinitialized = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `.sessionInvalidated`.
|
|
||||||
public var isSessionInvalidatedError: Bool {
|
|
||||||
if case .sessionInvalidated = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `.explicitlyCancelled`.
|
|
||||||
public var isExplicitlyCancelledError: Bool {
|
|
||||||
if case .explicitlyCancelled = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `.invalidURL`.
|
|
||||||
public var isInvalidURLError: Bool {
|
|
||||||
if case .invalidURL = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `.parameterEncodingFailed`. When `true`, the `underlyingError` property will
|
|
||||||
/// contain the associated value.
|
|
||||||
public var isParameterEncodingError: Bool {
|
|
||||||
if case .parameterEncodingFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `.parameterEncoderFailed`. When `true`, the `underlyingError` property will
|
|
||||||
/// contain the associated value.
|
|
||||||
public var isParameterEncoderError: Bool {
|
|
||||||
if case .parameterEncoderFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `.multipartEncodingFailed`. When `true`, the `url` and `underlyingError`
|
|
||||||
/// properties will contain the associated values.
|
|
||||||
public var isMultipartEncodingError: Bool {
|
|
||||||
if case .multipartEncodingFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `.requestAdaptationFailed`. When `true`, the `underlyingError` property will
|
|
||||||
/// contain the associated value.
|
|
||||||
public var isRequestAdaptationError: Bool {
|
|
||||||
if case .requestAdaptationFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `.responseValidationFailed`. When `true`, the `acceptableContentTypes`,
|
|
||||||
/// `responseContentType`, `responseCode`, and `underlyingError` properties will contain the associated values.
|
|
||||||
public var isResponseValidationError: Bool {
|
|
||||||
if case .responseValidationFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `.responseSerializationFailed`. When `true`, the `failedStringEncoding` and
|
|
||||||
/// `underlyingError` properties will contain the associated values.
|
|
||||||
public var isResponseSerializationError: Bool {
|
|
||||||
if case .responseSerializationFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(Security)
|
|
||||||
/// Returns whether the instance is `.serverTrustEvaluationFailed`. When `true`, the `underlyingError` property will
|
|
||||||
/// contain the associated value.
|
|
||||||
public var isServerTrustEvaluationError: Bool {
|
|
||||||
if case .serverTrustEvaluationFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Returns whether the instance is `requestRetryFailed`. When `true`, the `underlyingError` property will
|
|
||||||
/// contain the associated value.
|
|
||||||
public var isRequestRetryError: Bool {
|
|
||||||
if case .requestRetryFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `createUploadableFailed`. When `true`, the `underlyingError` property will
|
|
||||||
/// contain the associated value.
|
|
||||||
public var isCreateUploadableError: Bool {
|
|
||||||
if case .createUploadableFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will
|
|
||||||
/// contain the associated value.
|
|
||||||
public var isCreateURLRequestError: Bool {
|
|
||||||
if case .createURLRequestFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `downloadedFileMoveFailed`. When `true`, the `destination` and `underlyingError` properties will
|
|
||||||
/// contain the associated values.
|
|
||||||
public var isDownloadedFileMoveError: Bool {
|
|
||||||
if case .downloadedFileMoveFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `createURLRequestFailed`. When `true`, the `underlyingError` property will
|
|
||||||
/// contain the associated value.
|
|
||||||
public var isSessionTaskError: Bool {
|
|
||||||
if case .sessionTaskFailed = self { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Convenience Properties
|
|
||||||
|
|
||||||
extension AFError {
|
|
||||||
/// The `URLConvertible` associated with the error.
|
|
||||||
public var urlConvertible: URLConvertible? {
|
|
||||||
guard case let .invalidURL(url) = self else { return nil }
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `URL` associated with the error.
|
|
||||||
public var url: URL? {
|
|
||||||
guard case let .multipartEncodingFailed(reason) = self else { return nil }
|
|
||||||
return reason.url
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The underlying `Error` responsible for generating the failure associated with `.sessionInvalidated`,
|
|
||||||
/// `.parameterEncodingFailed`, `.parameterEncoderFailed`, `.multipartEncodingFailed`, `.requestAdaptationFailed`,
|
|
||||||
/// `.responseSerializationFailed`, `.requestRetryFailed` errors.
|
|
||||||
public var underlyingError: Error? {
|
|
||||||
switch self {
|
|
||||||
case let .multipartEncodingFailed(reason):
|
|
||||||
return reason.underlyingError
|
|
||||||
case let .parameterEncodingFailed(reason):
|
|
||||||
return reason.underlyingError
|
|
||||||
case let .parameterEncoderFailed(reason):
|
|
||||||
return reason.underlyingError
|
|
||||||
case let .requestAdaptationFailed(error):
|
|
||||||
return error
|
|
||||||
case let .requestRetryFailed(retryError, _):
|
|
||||||
return retryError
|
|
||||||
case let .responseValidationFailed(reason):
|
|
||||||
return reason.underlyingError
|
|
||||||
case let .responseSerializationFailed(reason):
|
|
||||||
return reason.underlyingError
|
|
||||||
#if canImport(Security)
|
|
||||||
case let .serverTrustEvaluationFailed(reason):
|
|
||||||
return reason.underlyingError
|
|
||||||
#endif
|
|
||||||
case let .sessionInvalidated(error):
|
|
||||||
return error
|
|
||||||
case let .createUploadableFailed(error):
|
|
||||||
return error
|
|
||||||
case let .createURLRequestFailed(error):
|
|
||||||
return error
|
|
||||||
case let .downloadedFileMoveFailed(error, _, _):
|
|
||||||
return error
|
|
||||||
case let .sessionTaskFailed(error):
|
|
||||||
return error
|
|
||||||
case .explicitlyCancelled,
|
|
||||||
.invalidURL,
|
|
||||||
.sessionDeinitialized,
|
|
||||||
.urlRequestValidationFailed:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The acceptable `Content-Type`s of a `.responseValidationFailed` error.
|
|
||||||
public var acceptableContentTypes: [String]? {
|
|
||||||
guard case let .responseValidationFailed(reason) = self else { return nil }
|
|
||||||
return reason.acceptableContentTypes
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The response `Content-Type` of a `.responseValidationFailed` error.
|
|
||||||
public var responseContentType: String? {
|
|
||||||
guard case let .responseValidationFailed(reason) = self else { return nil }
|
|
||||||
return reason.responseContentType
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The response code of a `.responseValidationFailed` error.
|
|
||||||
public var responseCode: Int? {
|
|
||||||
guard case let .responseValidationFailed(reason) = self else { return nil }
|
|
||||||
return reason.responseCode
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `String.Encoding` associated with a failed `.stringResponse()` call.
|
|
||||||
public var failedStringEncoding: String.Encoding? {
|
|
||||||
guard case let .responseSerializationFailed(reason) = self else { return nil }
|
|
||||||
return reason.failedStringEncoding
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `source` URL of a `.downloadedFileMoveFailed` error.
|
|
||||||
public var sourceURL: URL? {
|
|
||||||
guard case let .downloadedFileMoveFailed(_, source, _) = self else { return nil }
|
|
||||||
return source
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `destination` URL of a `.downloadedFileMoveFailed` error.
|
|
||||||
public var destinationURL: URL? {
|
|
||||||
guard case let .downloadedFileMoveFailed(_, _, destination) = self else { return nil }
|
|
||||||
return destination
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(Security)
|
|
||||||
/// The download resume data of any underlying network error. Only produced by `DownloadRequest`s.
|
|
||||||
public var downloadResumeData: Data? {
|
|
||||||
(underlyingError as? URLError)?.userInfo[NSURLSessionDownloadTaskResumeData] as? Data
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AFError.ParameterEncodingFailureReason {
|
|
||||||
var underlyingError: Error? {
|
|
||||||
switch self {
|
|
||||||
case let .jsonEncodingFailed(error),
|
|
||||||
let .customEncodingFailed(error):
|
|
||||||
return error
|
|
||||||
case .missingURL:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AFError.ParameterEncoderFailureReason {
|
|
||||||
var underlyingError: Error? {
|
|
||||||
switch self {
|
|
||||||
case let .encoderFailed(error):
|
|
||||||
return error
|
|
||||||
case .missingRequiredComponent:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AFError.MultipartEncodingFailureReason {
|
|
||||||
var url: URL? {
|
|
||||||
switch self {
|
|
||||||
case let .bodyPartURLInvalid(url),
|
|
||||||
let .bodyPartFilenameInvalid(url),
|
|
||||||
let .bodyPartFileNotReachable(url),
|
|
||||||
let .bodyPartFileIsDirectory(url),
|
|
||||||
let .bodyPartFileSizeNotAvailable(url),
|
|
||||||
let .bodyPartInputStreamCreationFailed(url),
|
|
||||||
let .outputStreamCreationFailed(url),
|
|
||||||
let .outputStreamFileAlreadyExists(url),
|
|
||||||
let .outputStreamURLInvalid(url),
|
|
||||||
let .bodyPartFileNotReachableWithError(url, _),
|
|
||||||
let .bodyPartFileSizeQueryFailedWithError(url, _):
|
|
||||||
return url
|
|
||||||
case .outputStreamWriteFailed,
|
|
||||||
.inputStreamReadFailed:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var underlyingError: Error? {
|
|
||||||
switch self {
|
|
||||||
case let .bodyPartFileNotReachableWithError(_, error),
|
|
||||||
let .bodyPartFileSizeQueryFailedWithError(_, error),
|
|
||||||
let .outputStreamWriteFailed(error),
|
|
||||||
let .inputStreamReadFailed(error):
|
|
||||||
return error
|
|
||||||
case .bodyPartURLInvalid,
|
|
||||||
.bodyPartFilenameInvalid,
|
|
||||||
.bodyPartFileNotReachable,
|
|
||||||
.bodyPartFileIsDirectory,
|
|
||||||
.bodyPartFileSizeNotAvailable,
|
|
||||||
.bodyPartInputStreamCreationFailed,
|
|
||||||
.outputStreamCreationFailed,
|
|
||||||
.outputStreamFileAlreadyExists,
|
|
||||||
.outputStreamURLInvalid:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AFError.ResponseValidationFailureReason {
|
|
||||||
var acceptableContentTypes: [String]? {
|
|
||||||
switch self {
|
|
||||||
case let .missingContentType(types),
|
|
||||||
let .unacceptableContentType(types, _):
|
|
||||||
return types
|
|
||||||
case .dataFileNil,
|
|
||||||
.dataFileReadFailed,
|
|
||||||
.unacceptableStatusCode,
|
|
||||||
.customValidationFailed:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var responseContentType: String? {
|
|
||||||
switch self {
|
|
||||||
case let .unacceptableContentType(_, responseType):
|
|
||||||
return responseType
|
|
||||||
case .dataFileNil,
|
|
||||||
.dataFileReadFailed,
|
|
||||||
.missingContentType,
|
|
||||||
.unacceptableStatusCode,
|
|
||||||
.customValidationFailed:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var responseCode: Int? {
|
|
||||||
switch self {
|
|
||||||
case let .unacceptableStatusCode(code):
|
|
||||||
return code
|
|
||||||
case .dataFileNil,
|
|
||||||
.dataFileReadFailed,
|
|
||||||
.missingContentType,
|
|
||||||
.unacceptableContentType,
|
|
||||||
.customValidationFailed:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var underlyingError: Error? {
|
|
||||||
switch self {
|
|
||||||
case let .customValidationFailed(error):
|
|
||||||
return error
|
|
||||||
case .dataFileNil,
|
|
||||||
.dataFileReadFailed,
|
|
||||||
.missingContentType,
|
|
||||||
.unacceptableContentType,
|
|
||||||
.unacceptableStatusCode:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AFError.ResponseSerializationFailureReason {
|
|
||||||
var failedStringEncoding: String.Encoding? {
|
|
||||||
switch self {
|
|
||||||
case let .stringSerializationFailed(encoding):
|
|
||||||
return encoding
|
|
||||||
case .inputDataNilOrZeroLength,
|
|
||||||
.inputFileNil,
|
|
||||||
.inputFileReadFailed(_),
|
|
||||||
.jsonSerializationFailed(_),
|
|
||||||
.decodingFailed(_),
|
|
||||||
.customSerializationFailed(_),
|
|
||||||
.invalidEmptyResponse:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var underlyingError: Error? {
|
|
||||||
switch self {
|
|
||||||
case let .jsonSerializationFailed(error),
|
|
||||||
let .decodingFailed(error),
|
|
||||||
let .customSerializationFailed(error):
|
|
||||||
return error
|
|
||||||
case .inputDataNilOrZeroLength,
|
|
||||||
.inputFileNil,
|
|
||||||
.inputFileReadFailed,
|
|
||||||
.stringSerializationFailed,
|
|
||||||
.invalidEmptyResponse:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(Security)
|
|
||||||
extension AFError.ServerTrustFailureReason {
|
|
||||||
var output: AFError.ServerTrustFailureReason.Output? {
|
|
||||||
switch self {
|
|
||||||
case let .defaultEvaluationFailed(output),
|
|
||||||
let .hostValidationFailed(output),
|
|
||||||
let .revocationCheckFailed(output, _):
|
|
||||||
return output
|
|
||||||
case .noRequiredEvaluator,
|
|
||||||
.noCertificatesFound,
|
|
||||||
.noPublicKeysFound,
|
|
||||||
.policyApplicationFailed,
|
|
||||||
.settingAnchorCertificatesFailed,
|
|
||||||
.revocationPolicyCreationFailed,
|
|
||||||
.trustEvaluationFailed,
|
|
||||||
.certificatePinningFailed,
|
|
||||||
.publicKeyPinningFailed,
|
|
||||||
.customEvaluationFailed:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var underlyingError: Error? {
|
|
||||||
switch self {
|
|
||||||
case let .customEvaluationFailed(error):
|
|
||||||
return error
|
|
||||||
case let .trustEvaluationFailed(error):
|
|
||||||
return error
|
|
||||||
case .noRequiredEvaluator,
|
|
||||||
.noCertificatesFound,
|
|
||||||
.noPublicKeysFound,
|
|
||||||
.policyApplicationFailed,
|
|
||||||
.settingAnchorCertificatesFailed,
|
|
||||||
.revocationPolicyCreationFailed,
|
|
||||||
.defaultEvaluationFailed,
|
|
||||||
.hostValidationFailed,
|
|
||||||
.revocationCheckFailed,
|
|
||||||
.certificatePinningFailed,
|
|
||||||
.publicKeyPinningFailed:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: - Error Descriptions
|
|
||||||
|
|
||||||
extension AFError: LocalizedError {
|
|
||||||
public var errorDescription: String? {
|
|
||||||
switch self {
|
|
||||||
case .explicitlyCancelled:
|
|
||||||
return "Request explicitly cancelled."
|
|
||||||
case let .invalidURL(url):
|
|
||||||
return "URL is not valid: \(url)"
|
|
||||||
case let .parameterEncodingFailed(reason):
|
|
||||||
return reason.localizedDescription
|
|
||||||
case let .parameterEncoderFailed(reason):
|
|
||||||
return reason.localizedDescription
|
|
||||||
case let .multipartEncodingFailed(reason):
|
|
||||||
return reason.localizedDescription
|
|
||||||
case let .requestAdaptationFailed(error):
|
|
||||||
return "Request adaption failed with error: \(error.localizedDescription)"
|
|
||||||
case let .responseValidationFailed(reason):
|
|
||||||
return reason.localizedDescription
|
|
||||||
case let .responseSerializationFailed(reason):
|
|
||||||
return reason.localizedDescription
|
|
||||||
case let .requestRetryFailed(retryError, originalError):
|
|
||||||
return """
|
|
||||||
Request retry failed with retry error: \(retryError.localizedDescription), \
|
|
||||||
original error: \(originalError.localizedDescription)
|
|
||||||
"""
|
|
||||||
case .sessionDeinitialized:
|
|
||||||
return """
|
|
||||||
Session was invalidated without error, so it was likely deinitialized unexpectedly. \
|
|
||||||
Be sure to retain a reference to your Session for the duration of your requests.
|
|
||||||
"""
|
|
||||||
case let .sessionInvalidated(error):
|
|
||||||
return "Session was invalidated with error: \(error?.localizedDescription ?? "No description.")"
|
|
||||||
#if canImport(Security)
|
|
||||||
case let .serverTrustEvaluationFailed(reason):
|
|
||||||
return "Server trust evaluation failed due to reason: \(reason.localizedDescription)"
|
|
||||||
#endif
|
|
||||||
case let .urlRequestValidationFailed(reason):
|
|
||||||
return "URLRequest validation failed due to reason: \(reason.localizedDescription)"
|
|
||||||
case let .createUploadableFailed(error):
|
|
||||||
return "Uploadable creation failed with error: \(error.localizedDescription)"
|
|
||||||
case let .createURLRequestFailed(error):
|
|
||||||
return "URLRequest creation failed with error: \(error.localizedDescription)"
|
|
||||||
case let .downloadedFileMoveFailed(error, source, destination):
|
|
||||||
return "Moving downloaded file from: \(source) to: \(destination) failed with error: \(error.localizedDescription)"
|
|
||||||
case let .sessionTaskFailed(error):
|
|
||||||
return "URLSessionTask failed with error: \(error.localizedDescription)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AFError.ParameterEncodingFailureReason {
|
|
||||||
var localizedDescription: String {
|
|
||||||
switch self {
|
|
||||||
case .missingURL:
|
|
||||||
return "URL request to encode was missing a URL"
|
|
||||||
case let .jsonEncodingFailed(error):
|
|
||||||
return "JSON could not be encoded because of error:\n\(error.localizedDescription)"
|
|
||||||
case let .customEncodingFailed(error):
|
|
||||||
return "Custom parameter encoder failed with error: \(error.localizedDescription)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AFError.ParameterEncoderFailureReason {
|
|
||||||
var localizedDescription: String {
|
|
||||||
switch self {
|
|
||||||
case let .missingRequiredComponent(component):
|
|
||||||
return "Encoding failed due to a missing request component: \(component)"
|
|
||||||
case let .encoderFailed(error):
|
|
||||||
return "The underlying encoder failed with the error: \(error)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AFError.MultipartEncodingFailureReason {
|
|
||||||
var localizedDescription: String {
|
|
||||||
switch self {
|
|
||||||
case let .bodyPartURLInvalid(url):
|
|
||||||
return "The URL provided is not a file URL: \(url)"
|
|
||||||
case let .bodyPartFilenameInvalid(url):
|
|
||||||
return "The URL provided does not have a valid filename: \(url)"
|
|
||||||
case let .bodyPartFileNotReachable(url):
|
|
||||||
return "The URL provided is not reachable: \(url)"
|
|
||||||
case let .bodyPartFileNotReachableWithError(url, error):
|
|
||||||
return """
|
|
||||||
The system returned an error while checking the provided URL for reachability.
|
|
||||||
URL: \(url)
|
|
||||||
Error: \(error)
|
|
||||||
"""
|
|
||||||
case let .bodyPartFileIsDirectory(url):
|
|
||||||
return "The URL provided is a directory: \(url)"
|
|
||||||
case let .bodyPartFileSizeNotAvailable(url):
|
|
||||||
return "Could not fetch the file size from the provided URL: \(url)"
|
|
||||||
case let .bodyPartFileSizeQueryFailedWithError(url, error):
|
|
||||||
return """
|
|
||||||
The system returned an error while attempting to fetch the file size from the provided URL.
|
|
||||||
URL: \(url)
|
|
||||||
Error: \(error)
|
|
||||||
"""
|
|
||||||
case let .bodyPartInputStreamCreationFailed(url):
|
|
||||||
return "Failed to create an InputStream for the provided URL: \(url)"
|
|
||||||
case let .outputStreamCreationFailed(url):
|
|
||||||
return "Failed to create an OutputStream for URL: \(url)"
|
|
||||||
case let .outputStreamFileAlreadyExists(url):
|
|
||||||
return "A file already exists at the provided URL: \(url)"
|
|
||||||
case let .outputStreamURLInvalid(url):
|
|
||||||
return "The provided OutputStream URL is invalid: \(url)"
|
|
||||||
case let .outputStreamWriteFailed(error):
|
|
||||||
return "OutputStream write failed with error: \(error)"
|
|
||||||
case let .inputStreamReadFailed(error):
|
|
||||||
return "InputStream read failed with error: \(error)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AFError.ResponseSerializationFailureReason {
|
|
||||||
var localizedDescription: String {
|
|
||||||
switch self {
|
|
||||||
case .inputDataNilOrZeroLength:
|
|
||||||
return "Response could not be serialized, input data was nil or zero length."
|
|
||||||
case .inputFileNil:
|
|
||||||
return "Response could not be serialized, input file was nil."
|
|
||||||
case let .inputFileReadFailed(url):
|
|
||||||
return "Response could not be serialized, input file could not be read: \(url)."
|
|
||||||
case let .stringSerializationFailed(encoding):
|
|
||||||
return "String could not be serialized with encoding: \(encoding)."
|
|
||||||
case let .jsonSerializationFailed(error):
|
|
||||||
return "JSON could not be serialized because of error:\n\(error.localizedDescription)"
|
|
||||||
case let .invalidEmptyResponse(type):
|
|
||||||
return """
|
|
||||||
Empty response could not be serialized to type: \(type). \
|
|
||||||
Use Empty as the expected type for such responses.
|
|
||||||
"""
|
|
||||||
case let .decodingFailed(error):
|
|
||||||
return "Response could not be decoded because of error:\n\(error.localizedDescription)"
|
|
||||||
case let .customSerializationFailed(error):
|
|
||||||
return "Custom response serializer failed with error:\n\(error.localizedDescription)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AFError.ResponseValidationFailureReason {
|
|
||||||
var localizedDescription: String {
|
|
||||||
switch self {
|
|
||||||
case .dataFileNil:
|
|
||||||
return "Response could not be validated, data file was nil."
|
|
||||||
case let .dataFileReadFailed(url):
|
|
||||||
return "Response could not be validated, data file could not be read: \(url)."
|
|
||||||
case let .missingContentType(types):
|
|
||||||
return """
|
|
||||||
Response Content-Type was missing and acceptable content types \
|
|
||||||
(\(types.joined(separator: ","))) do not match "*/*".
|
|
||||||
"""
|
|
||||||
case let .unacceptableContentType(acceptableTypes, responseType):
|
|
||||||
return """
|
|
||||||
Response Content-Type "\(responseType)" does not match any acceptable types: \
|
|
||||||
\(acceptableTypes.joined(separator: ",")).
|
|
||||||
"""
|
|
||||||
case let .unacceptableStatusCode(code):
|
|
||||||
return "Response status code was unacceptable: \(code)."
|
|
||||||
case let .customValidationFailed(error):
|
|
||||||
return "Custom response validation failed with error: \(error.localizedDescription)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(Security)
|
|
||||||
extension AFError.ServerTrustFailureReason {
|
|
||||||
var localizedDescription: String {
|
|
||||||
switch self {
|
|
||||||
case let .noRequiredEvaluator(host):
|
|
||||||
return "A ServerTrustEvaluating value is required for host \(host) but none was found."
|
|
||||||
case .noCertificatesFound:
|
|
||||||
return "No certificates were found or provided for evaluation."
|
|
||||||
case .noPublicKeysFound:
|
|
||||||
return "No public keys were found or provided for evaluation."
|
|
||||||
case .policyApplicationFailed:
|
|
||||||
return "Attempting to set a SecPolicy failed."
|
|
||||||
case .settingAnchorCertificatesFailed:
|
|
||||||
return "Attempting to set the provided certificates as anchor certificates failed."
|
|
||||||
case .revocationPolicyCreationFailed:
|
|
||||||
return "Attempting to create a revocation policy failed."
|
|
||||||
case let .trustEvaluationFailed(error):
|
|
||||||
return "SecTrust evaluation failed with error: \(error?.localizedDescription ?? "None")"
|
|
||||||
case let .defaultEvaluationFailed(output):
|
|
||||||
return "Default evaluation failed for host \(output.host)."
|
|
||||||
case let .hostValidationFailed(output):
|
|
||||||
return "Host validation failed for host \(output.host)."
|
|
||||||
case let .revocationCheckFailed(output, _):
|
|
||||||
return "Revocation check failed for host \(output.host)."
|
|
||||||
case let .certificatePinningFailed(host, _, _, _):
|
|
||||||
return "Certificate pinning failed for host \(host)."
|
|
||||||
case let .publicKeyPinningFailed(host, _, _, _):
|
|
||||||
return "Public key pinning failed for host \(host)."
|
|
||||||
case let .customEvaluationFailed(error):
|
|
||||||
return "Custom trust evaluation failed with error: \(error.localizedDescription)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extension AFError.URLRequestValidationFailureReason {
|
|
||||||
var localizedDescription: String {
|
|
||||||
switch self {
|
|
||||||
case let .bodyDataInGETRequest(data):
|
|
||||||
return """
|
|
||||||
Invalid URLRequest: Requests with GET method cannot have body data:
|
|
||||||
\(String(decoding: data, as: UTF8.self))
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
40
SwiftProject/Pods/Alamofire/Source/Alamofire.swift
generated
40
SwiftProject/Pods/Alamofire/Source/Alamofire.swift
generated
@ -1,40 +0,0 @@
|
|||||||
//
|
|
||||||
// Alamofire.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2021 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Dispatch
|
|
||||||
import Foundation
|
|
||||||
#if canImport(FoundationNetworking)
|
|
||||||
@_exported import FoundationNetworking
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Enforce minimum Swift version for all platforms and build systems.
|
|
||||||
#if swift(<5.5)
|
|
||||||
#error("Alamofire doesn't support Swift versions below 5.5.")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Reference to `Session.default` for quick bootstrapping and examples.
|
|
||||||
public let AF = Session.default
|
|
||||||
|
|
||||||
/// Current Alamofire version. Necessary since SPM doesn't use dynamic libraries. Plus this will be more accurate.
|
|
||||||
let version = "5.8.0"
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
//
|
|
||||||
// AlamofireExtended.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
/// Type that acts as a generic extension point for all `AlamofireExtended` types.
|
|
||||||
public struct AlamofireExtension<ExtendedType> {
|
|
||||||
/// Stores the type or meta-type of any extended type.
|
|
||||||
public private(set) var type: ExtendedType
|
|
||||||
|
|
||||||
/// Create an instance from the provided value.
|
|
||||||
///
|
|
||||||
/// - Parameter type: Instance being extended.
|
|
||||||
public init(_ type: ExtendedType) {
|
|
||||||
self.type = type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Protocol describing the `af` extension points for Alamofire extended types.
|
|
||||||
public protocol AlamofireExtended {
|
|
||||||
/// Type being extended.
|
|
||||||
associatedtype ExtendedType
|
|
||||||
|
|
||||||
/// Static Alamofire extension point.
|
|
||||||
static var af: AlamofireExtension<ExtendedType>.Type { get set }
|
|
||||||
/// Instance Alamofire extension point.
|
|
||||||
var af: AlamofireExtension<ExtendedType> { get set }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AlamofireExtended {
|
|
||||||
/// Static Alamofire extension point.
|
|
||||||
public static var af: AlamofireExtension<Self>.Type {
|
|
||||||
get { AlamofireExtension<Self>.self }
|
|
||||||
set {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Instance Alamofire extension point.
|
|
||||||
public var af: AlamofireExtension<Self> {
|
|
||||||
get { AlamofireExtension(self) }
|
|
||||||
set {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,402 +0,0 @@
|
|||||||
//
|
|
||||||
// AuthenticationInterceptor.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Types adopting the `AuthenticationCredential` protocol can be used to authenticate `URLRequest`s.
|
|
||||||
///
|
|
||||||
/// One common example of an `AuthenticationCredential` is an OAuth2 credential containing an access token used to
|
|
||||||
/// authenticate all requests on behalf of a user. The access token generally has an expiration window of 60 minutes
|
|
||||||
/// which will then require a refresh of the credential using the refresh token to generate a new access token.
|
|
||||||
public protocol AuthenticationCredential {
|
|
||||||
/// Whether the credential requires a refresh. This property should always return `true` when the credential is
|
|
||||||
/// expired. It is also wise to consider returning `true` when the credential will expire in several seconds or
|
|
||||||
/// minutes depending on the expiration window of the credential.
|
|
||||||
///
|
|
||||||
/// For example, if the credential is valid for 60 minutes, then it would be wise to return `true` when the
|
|
||||||
/// credential is only valid for 5 minutes or less. That ensures the credential will not expire as it is passed
|
|
||||||
/// around backend services.
|
|
||||||
var requiresRefresh: Bool { get }
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// Types adopting the `Authenticator` protocol can be used to authenticate `URLRequest`s with an
|
|
||||||
/// `AuthenticationCredential` as well as refresh the `AuthenticationCredential` when required.
|
|
||||||
public protocol Authenticator: AnyObject {
|
|
||||||
/// The type of credential associated with the `Authenticator` instance.
|
|
||||||
associatedtype Credential: AuthenticationCredential
|
|
||||||
|
|
||||||
/// Applies the `Credential` to the `URLRequest`.
|
|
||||||
///
|
|
||||||
/// In the case of OAuth2, the access token of the `Credential` would be added to the `URLRequest` as a Bearer
|
|
||||||
/// token to the `Authorization` header.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - credential: The `Credential`.
|
|
||||||
/// - urlRequest: The `URLRequest`.
|
|
||||||
func apply(_ credential: Credential, to urlRequest: inout URLRequest)
|
|
||||||
|
|
||||||
/// Refreshes the `Credential` and executes the `completion` closure with the `Result` once complete.
|
|
||||||
///
|
|
||||||
/// Refresh can be called in one of two ways. It can be called before the `Request` is actually executed due to
|
|
||||||
/// a `requiresRefresh` returning `true` during the adapt portion of the `Request` creation process. It can also
|
|
||||||
/// be triggered by a failed `Request` where the authentication server denied access due to an expired or
|
|
||||||
/// invalidated access token.
|
|
||||||
///
|
|
||||||
/// In the case of OAuth2, this method would use the refresh token of the `Credential` to generate a new
|
|
||||||
/// `Credential` using the authentication service. Once complete, the `completion` closure should be called with
|
|
||||||
/// the new `Credential`, or the error that occurred.
|
|
||||||
///
|
|
||||||
/// In general, if the refresh call fails with certain status codes from the authentication server (commonly a 401),
|
|
||||||
/// the refresh token in the `Credential` can no longer be used to generate a valid `Credential`. In these cases,
|
|
||||||
/// you will need to reauthenticate the user with their username / password.
|
|
||||||
///
|
|
||||||
/// Please note, these are just general examples of common use cases. They are not meant to solve your specific
|
|
||||||
/// authentication server challenges. Please work with your authentication server team to ensure your
|
|
||||||
/// `Authenticator` logic matches their expectations.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - credential: The `Credential` to refresh.
|
|
||||||
/// - session: The `Session` requiring the refresh.
|
|
||||||
/// - completion: The closure to be executed once the refresh is complete.
|
|
||||||
func refresh(_ credential: Credential, for session: Session, completion: @escaping (Result<Credential, Error>) -> Void)
|
|
||||||
|
|
||||||
/// Determines whether the `URLRequest` failed due to an authentication error based on the `HTTPURLResponse`.
|
|
||||||
///
|
|
||||||
/// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `false`
|
|
||||||
/// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then you
|
|
||||||
/// will need to work with your authentication server team to understand how to identify when this occurs.
|
|
||||||
///
|
|
||||||
/// In the case of OAuth2, where an authentication server can invalidate credentials, you will need to inspect the
|
|
||||||
/// `HTTPURLResponse` or possibly the `Error` for when this occurs. This is commonly handled by the authentication
|
|
||||||
/// server returning a 401 status code and some additional header to indicate an OAuth2 failure occurred.
|
|
||||||
///
|
|
||||||
/// It is very important to understand how your authentication server works to be able to implement this correctly.
|
|
||||||
/// For example, if your authentication server returns a 401 when an OAuth2 error occurs, and your downstream
|
|
||||||
/// service also returns a 401 when you are not authorized to perform that operation, how do you know which layer
|
|
||||||
/// of the backend returned you a 401? You do not want to trigger a refresh unless you know your authentication
|
|
||||||
/// server is actually the layer rejecting the request. Again, work with your authentication server team to understand
|
|
||||||
/// how to identify an OAuth2 401 error vs. a downstream 401 error to avoid endless refresh loops.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - urlRequest: The `URLRequest`.
|
|
||||||
/// - response: The `HTTPURLResponse`.
|
|
||||||
/// - error: The `Error`.
|
|
||||||
///
|
|
||||||
/// - Returns: `true` if the `URLRequest` failed due to an authentication error, `false` otherwise.
|
|
||||||
func didRequest(_ urlRequest: URLRequest, with response: HTTPURLResponse, failDueToAuthenticationError error: Error) -> Bool
|
|
||||||
|
|
||||||
/// Determines whether the `URLRequest` is authenticated with the `Credential`.
|
|
||||||
///
|
|
||||||
/// If the authentication server **CANNOT** invalidate credentials after they are issued, then simply return `true`
|
|
||||||
/// for this method. If the authentication server **CAN** invalidate credentials due to security breaches, then
|
|
||||||
/// read on.
|
|
||||||
///
|
|
||||||
/// When an authentication server can invalidate credentials, it means that you may have a non-expired credential
|
|
||||||
/// that appears to be valid, but will be rejected by the authentication server when used. Generally when this
|
|
||||||
/// happens, a number of requests are all sent when the application is foregrounded, and all of them will be
|
|
||||||
/// rejected by the authentication server in the order they are received. The first failed request will trigger a
|
|
||||||
/// refresh internally, which will update the credential, and then retry all the queued requests with the new
|
|
||||||
/// credential. However, it is possible that some of the original requests will not return from the authentication
|
|
||||||
/// server until the refresh has completed. This is where this method comes in.
|
|
||||||
///
|
|
||||||
/// When the authentication server rejects a credential, we need to check to make sure we haven't refreshed the
|
|
||||||
/// credential while the request was in flight. If it has already refreshed, then we don't need to trigger an
|
|
||||||
/// additional refresh. If it hasn't refreshed, then we need to refresh.
|
|
||||||
///
|
|
||||||
/// Now that it is understood how the result of this method is used in the refresh lifecyle, let's walk through how
|
|
||||||
/// to implement it. You should return `true` in this method if the `URLRequest` is authenticated in a way that
|
|
||||||
/// matches the values in the `Credential`. In the case of OAuth2, this would mean that the Bearer token in the
|
|
||||||
/// `Authorization` header of the `URLRequest` matches the access token in the `Credential`. If it matches, then we
|
|
||||||
/// know the `Credential` was used to authenticate the `URLRequest` and should return `true`. If the Bearer token
|
|
||||||
/// did not match the access token, then you should return `false`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - urlRequest: The `URLRequest`.
|
|
||||||
/// - credential: The `Credential`.
|
|
||||||
///
|
|
||||||
/// - Returns: `true` if the `URLRequest` is authenticated with the `Credential`, `false` otherwise.
|
|
||||||
func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: Credential) -> Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// Represents various authentication failures that occur when using the `AuthenticationInterceptor`. All errors are
|
|
||||||
/// still vended from Alamofire as `AFError` types. The `AuthenticationError` instances will be embedded within
|
|
||||||
/// `AFError` `.requestAdaptationFailed` or `.requestRetryFailed` cases.
|
|
||||||
public enum AuthenticationError: Error {
|
|
||||||
/// The credential was missing so the request could not be authenticated.
|
|
||||||
case missingCredential
|
|
||||||
/// The credential was refreshed too many times within the `RefreshWindow`.
|
|
||||||
case excessiveRefresh
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// The `AuthenticationInterceptor` class manages the queuing and threading complexity of authenticating requests.
|
|
||||||
/// It relies on an `Authenticator` type to handle the actual `URLRequest` authentication and `Credential` refresh.
|
|
||||||
public class AuthenticationInterceptor<AuthenticatorType>: RequestInterceptor where AuthenticatorType: Authenticator {
|
|
||||||
// MARK: Typealiases
|
|
||||||
|
|
||||||
/// Type of credential used to authenticate requests.
|
|
||||||
public typealias Credential = AuthenticatorType.Credential
|
|
||||||
|
|
||||||
// MARK: Helper Types
|
|
||||||
|
|
||||||
/// Type that defines a time window used to identify excessive refresh calls. When enabled, prior to executing a
|
|
||||||
/// refresh, the `AuthenticationInterceptor` compares the timestamp history of previous refresh calls against the
|
|
||||||
/// `RefreshWindow`. If more refreshes have occurred within the refresh window than allowed, the refresh is
|
|
||||||
/// cancelled and an `AuthorizationError.excessiveRefresh` error is thrown.
|
|
||||||
public struct RefreshWindow {
|
|
||||||
/// `TimeInterval` defining the duration of the time window before the current time in which the number of
|
|
||||||
/// refresh attempts is compared against `maximumAttempts`. For example, if `interval` is 30 seconds, then the
|
|
||||||
/// `RefreshWindow` represents the past 30 seconds. If more attempts occurred in the past 30 seconds than
|
|
||||||
/// `maximumAttempts`, an `.excessiveRefresh` error will be thrown.
|
|
||||||
public let interval: TimeInterval
|
|
||||||
|
|
||||||
/// Total refresh attempts allowed within `interval` before throwing an `.excessiveRefresh` error.
|
|
||||||
public let maximumAttempts: Int
|
|
||||||
|
|
||||||
/// Creates a `RefreshWindow` instance from the specified `interval` and `maximumAttempts`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - interval: `TimeInterval` defining the duration of the time window before the current time.
|
|
||||||
/// - maximumAttempts: The maximum attempts allowed within the `TimeInterval`.
|
|
||||||
public init(interval: TimeInterval = 30.0, maximumAttempts: Int = 5) {
|
|
||||||
self.interval = interval
|
|
||||||
self.maximumAttempts = maximumAttempts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct AdaptOperation {
|
|
||||||
let urlRequest: URLRequest
|
|
||||||
let session: Session
|
|
||||||
let completion: (Result<URLRequest, Error>) -> Void
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum AdaptResult {
|
|
||||||
case adapt(Credential)
|
|
||||||
case doNotAdapt(AuthenticationError)
|
|
||||||
case adaptDeferred
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct MutableState {
|
|
||||||
var credential: Credential?
|
|
||||||
|
|
||||||
var isRefreshing = false
|
|
||||||
var refreshTimestamps: [TimeInterval] = []
|
|
||||||
var refreshWindow: RefreshWindow?
|
|
||||||
|
|
||||||
var adaptOperations: [AdaptOperation] = []
|
|
||||||
var requestsToRetry: [(RetryResult) -> Void] = []
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Properties
|
|
||||||
|
|
||||||
/// The `Credential` used to authenticate requests.
|
|
||||||
public var credential: Credential? {
|
|
||||||
get { mutableState.credential }
|
|
||||||
set { mutableState.credential = newValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
let authenticator: AuthenticatorType
|
|
||||||
let queue = DispatchQueue(label: "org.alamofire.authentication.inspector")
|
|
||||||
|
|
||||||
private let mutableState: Protected<MutableState>
|
|
||||||
|
|
||||||
// MARK: Initialization
|
|
||||||
|
|
||||||
/// Creates an `AuthenticationInterceptor` instance from the specified parameters.
|
|
||||||
///
|
|
||||||
/// A `nil` `RefreshWindow` will result in the `AuthenticationInterceptor` not checking for excessive refresh calls.
|
|
||||||
/// It is recommended to always use a `RefreshWindow` to avoid endless refresh cycles.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - authenticator: The `Authenticator` type.
|
|
||||||
/// - credential: The `Credential` if it exists. `nil` by default.
|
|
||||||
/// - refreshWindow: The `RefreshWindow` used to identify excessive refresh calls. `RefreshWindow()` by default.
|
|
||||||
public init(authenticator: AuthenticatorType,
|
|
||||||
credential: Credential? = nil,
|
|
||||||
refreshWindow: RefreshWindow? = RefreshWindow()) {
|
|
||||||
self.authenticator = authenticator
|
|
||||||
mutableState = Protected(MutableState(credential: credential, refreshWindow: refreshWindow))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Adapt
|
|
||||||
|
|
||||||
public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
||||||
let adaptResult: AdaptResult = mutableState.write { mutableState in
|
|
||||||
// Queue the adapt operation if a refresh is already in place.
|
|
||||||
guard !mutableState.isRefreshing else {
|
|
||||||
let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion)
|
|
||||||
mutableState.adaptOperations.append(operation)
|
|
||||||
return .adaptDeferred
|
|
||||||
}
|
|
||||||
|
|
||||||
// Throw missing credential error is the credential is missing.
|
|
||||||
guard let credential = mutableState.credential else {
|
|
||||||
let error = AuthenticationError.missingCredential
|
|
||||||
return .doNotAdapt(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Queue the adapt operation and trigger refresh operation if credential requires refresh.
|
|
||||||
guard !credential.requiresRefresh else {
|
|
||||||
let operation = AdaptOperation(urlRequest: urlRequest, session: session, completion: completion)
|
|
||||||
mutableState.adaptOperations.append(operation)
|
|
||||||
refresh(credential, for: session, insideLock: &mutableState)
|
|
||||||
return .adaptDeferred
|
|
||||||
}
|
|
||||||
|
|
||||||
return .adapt(credential)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch adaptResult {
|
|
||||||
case let .adapt(credential):
|
|
||||||
var authenticatedRequest = urlRequest
|
|
||||||
authenticator.apply(credential, to: &authenticatedRequest)
|
|
||||||
completion(.success(authenticatedRequest))
|
|
||||||
|
|
||||||
case let .doNotAdapt(adaptError):
|
|
||||||
completion(.failure(adaptError))
|
|
||||||
|
|
||||||
case .adaptDeferred:
|
|
||||||
// No-op: adapt operation captured during refresh.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Retry
|
|
||||||
|
|
||||||
public func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
|
|
||||||
// Do not attempt retry if there was not an original request and response from the server.
|
|
||||||
guard let urlRequest = request.request, let response = request.response else {
|
|
||||||
completion(.doNotRetry)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not attempt retry unless the `Authenticator` verifies failure was due to authentication error (i.e. 401 status code).
|
|
||||||
guard authenticator.didRequest(urlRequest, with: response, failDueToAuthenticationError: error) else {
|
|
||||||
completion(.doNotRetry)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not attempt retry if there is no credential.
|
|
||||||
guard let credential = credential else {
|
|
||||||
let error = AuthenticationError.missingCredential
|
|
||||||
completion(.doNotRetryWithError(error))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry the request if the `Authenticator` verifies it was authenticated with a previous credential.
|
|
||||||
guard authenticator.isRequest(urlRequest, authenticatedWith: credential) else {
|
|
||||||
completion(.retry)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mutableState.write { mutableState in
|
|
||||||
mutableState.requestsToRetry.append(completion)
|
|
||||||
|
|
||||||
guard !mutableState.isRefreshing else { return }
|
|
||||||
|
|
||||||
refresh(credential, for: session, insideLock: &mutableState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Refresh
|
|
||||||
|
|
||||||
private func refresh(_ credential: Credential, for session: Session, insideLock mutableState: inout MutableState) {
|
|
||||||
guard !isRefreshExcessive(insideLock: &mutableState) else {
|
|
||||||
let error = AuthenticationError.excessiveRefresh
|
|
||||||
handleRefreshFailure(error, insideLock: &mutableState)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mutableState.refreshTimestamps.append(ProcessInfo.processInfo.systemUptime)
|
|
||||||
mutableState.isRefreshing = true
|
|
||||||
|
|
||||||
// Dispatch to queue to hop out of the lock in case authenticator.refresh is implemented synchronously.
|
|
||||||
queue.async {
|
|
||||||
self.authenticator.refresh(credential, for: session) { result in
|
|
||||||
self.mutableState.write { mutableState in
|
|
||||||
switch result {
|
|
||||||
case let .success(credential):
|
|
||||||
self.handleRefreshSuccess(credential, insideLock: &mutableState)
|
|
||||||
case let .failure(error):
|
|
||||||
self.handleRefreshFailure(error, insideLock: &mutableState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func isRefreshExcessive(insideLock mutableState: inout MutableState) -> Bool {
|
|
||||||
guard let refreshWindow = mutableState.refreshWindow else { return false }
|
|
||||||
|
|
||||||
let refreshWindowMin = ProcessInfo.processInfo.systemUptime - refreshWindow.interval
|
|
||||||
|
|
||||||
let refreshAttemptsWithinWindow = mutableState.refreshTimestamps.reduce(into: 0) { attempts, refreshTimestamp in
|
|
||||||
guard refreshWindowMin <= refreshTimestamp else { return }
|
|
||||||
attempts += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let isRefreshExcessive = refreshAttemptsWithinWindow >= refreshWindow.maximumAttempts
|
|
||||||
|
|
||||||
return isRefreshExcessive
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleRefreshSuccess(_ credential: Credential, insideLock mutableState: inout MutableState) {
|
|
||||||
mutableState.credential = credential
|
|
||||||
|
|
||||||
let adaptOperations = mutableState.adaptOperations
|
|
||||||
let requestsToRetry = mutableState.requestsToRetry
|
|
||||||
|
|
||||||
mutableState.adaptOperations.removeAll()
|
|
||||||
mutableState.requestsToRetry.removeAll()
|
|
||||||
|
|
||||||
mutableState.isRefreshing = false
|
|
||||||
|
|
||||||
// Dispatch to queue to hop out of the mutable state lock
|
|
||||||
queue.async {
|
|
||||||
adaptOperations.forEach { self.adapt($0.urlRequest, for: $0.session, completion: $0.completion) }
|
|
||||||
requestsToRetry.forEach { $0(.retry) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleRefreshFailure(_ error: Error, insideLock mutableState: inout MutableState) {
|
|
||||||
let adaptOperations = mutableState.adaptOperations
|
|
||||||
let requestsToRetry = mutableState.requestsToRetry
|
|
||||||
|
|
||||||
mutableState.adaptOperations.removeAll()
|
|
||||||
mutableState.requestsToRetry.removeAll()
|
|
||||||
|
|
||||||
mutableState.isRefreshing = false
|
|
||||||
|
|
||||||
// Dispatch to queue to hop out of the mutable state lock
|
|
||||||
queue.async {
|
|
||||||
adaptOperations.forEach { $0.completion(.failure(error)) }
|
|
||||||
requestsToRetry.forEach { $0(.doNotRetryWithError(error)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,107 +0,0 @@
|
|||||||
//
|
|
||||||
// CachedResponseHandler.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// A type that handles whether the data task should store the HTTP response in the cache.
|
|
||||||
public protocol CachedResponseHandler {
|
|
||||||
/// Determines whether the HTTP response should be stored in the cache.
|
|
||||||
///
|
|
||||||
/// The `completion` closure should be passed one of three possible options:
|
|
||||||
///
|
|
||||||
/// 1. The cached response provided by the server (this is the most common use case).
|
|
||||||
/// 2. A modified version of the cached response (you may want to modify it in some way before caching).
|
|
||||||
/// 3. A `nil` value to prevent the cached response from being stored in the cache.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - task: The data task whose request resulted in the cached response.
|
|
||||||
/// - response: The cached response to potentially store in the cache.
|
|
||||||
/// - completion: The closure to execute containing cached response, a modified response, or `nil`.
|
|
||||||
func dataTask(_ task: URLSessionDataTask,
|
|
||||||
willCacheResponse response: CachedURLResponse,
|
|
||||||
completion: @escaping (CachedURLResponse?) -> Void)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// `ResponseCacher` is a convenience `CachedResponseHandler` making it easy to cache, not cache, or modify a cached
|
|
||||||
/// response.
|
|
||||||
public struct ResponseCacher {
|
|
||||||
/// Defines the behavior of the `ResponseCacher` type.
|
|
||||||
public enum Behavior {
|
|
||||||
/// Stores the cached response in the cache.
|
|
||||||
case cache
|
|
||||||
/// Prevents the cached response from being stored in the cache.
|
|
||||||
case doNotCache
|
|
||||||
/// Modifies the cached response before storing it in the cache.
|
|
||||||
case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a `ResponseCacher` with a `.cache` `Behavior`.
|
|
||||||
public static let cache = ResponseCacher(behavior: .cache)
|
|
||||||
/// Returns a `ResponseCacher` with a `.doNotCache` `Behavior`.
|
|
||||||
public static let doNotCache = ResponseCacher(behavior: .doNotCache)
|
|
||||||
|
|
||||||
/// The `Behavior` of the `ResponseCacher`.
|
|
||||||
public let behavior: Behavior
|
|
||||||
|
|
||||||
/// Creates a `ResponseCacher` instance from the `Behavior`.
|
|
||||||
///
|
|
||||||
/// - Parameter behavior: The `Behavior`.
|
|
||||||
public init(behavior: Behavior) {
|
|
||||||
self.behavior = behavior
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ResponseCacher: CachedResponseHandler {
|
|
||||||
public func dataTask(_ task: URLSessionDataTask,
|
|
||||||
willCacheResponse response: CachedURLResponse,
|
|
||||||
completion: @escaping (CachedURLResponse?) -> Void) {
|
|
||||||
switch behavior {
|
|
||||||
case .cache:
|
|
||||||
completion(response)
|
|
||||||
case .doNotCache:
|
|
||||||
completion(nil)
|
|
||||||
case let .modify(closure):
|
|
||||||
let response = closure(task, response)
|
|
||||||
completion(response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CachedResponseHandler where Self == ResponseCacher {
|
|
||||||
/// Provides a `ResponseCacher` which caches the response, if allowed. Equivalent to `ResponseCacher.cache`.
|
|
||||||
public static var cache: ResponseCacher { .cache }
|
|
||||||
|
|
||||||
/// Provides a `ResponseCacher` which does not cache the response. Equivalent to `ResponseCacher.doNotCache`.
|
|
||||||
public static var doNotCache: ResponseCacher { .doNotCache }
|
|
||||||
|
|
||||||
/// Creates a `ResponseCacher` which modifies the proposed `CachedURLResponse` using the provided closure.
|
|
||||||
///
|
|
||||||
/// - Parameter closure: Closure used to modify the `CachedURLResponse`.
|
|
||||||
/// - Returns: The `ResponseCacher`.
|
|
||||||
public static func modify(using closure: @escaping ((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)) -> ResponseCacher {
|
|
||||||
ResponseCacher(behavior: .modify(closure))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
652
SwiftProject/Pods/Alamofire/Source/Combine.swift
generated
652
SwiftProject/Pods/Alamofire/Source/Combine.swift
generated
@ -1,652 +0,0 @@
|
|||||||
//
|
|
||||||
// Combine.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
#if !((os(iOS) && (arch(i386) || arch(arm))) || os(Windows) || os(Linux) || os(Android))
|
|
||||||
|
|
||||||
import Combine
|
|
||||||
import Dispatch
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
// MARK: - DataRequest / UploadRequest
|
|
||||||
|
|
||||||
/// A Combine `Publisher` that publishes the `DataResponse<Value, AFError>` of the provided `DataRequest`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public struct DataResponsePublisher<Value>: Publisher {
|
|
||||||
public typealias Output = DataResponse<Value, AFError>
|
|
||||||
public typealias Failure = Never
|
|
||||||
|
|
||||||
private typealias Handler = (@escaping (_ response: DataResponse<Value, AFError>) -> Void) -> DataRequest
|
|
||||||
|
|
||||||
private let request: DataRequest
|
|
||||||
private let responseHandler: Handler
|
|
||||||
|
|
||||||
/// Creates an instance which will serialize responses using the provided `ResponseSerializer`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: `DataRequest` for which to publish the response.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default.
|
|
||||||
/// - serializer: `ResponseSerializer` used to produce the published `DataResponse`.
|
|
||||||
public init<Serializer: ResponseSerializer>(_ request: DataRequest, queue: DispatchQueue, serializer: Serializer)
|
|
||||||
where Value == Serializer.SerializedObject {
|
|
||||||
self.request = request
|
|
||||||
responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an instance which will serialize responses using the provided `DataResponseSerializerProtocol`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: `DataRequest` for which to publish the response.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default.
|
|
||||||
/// - serializer: `DataResponseSerializerProtocol` used to produce the published `DataResponse`.
|
|
||||||
public init<Serializer: DataResponseSerializerProtocol>(_ request: DataRequest,
|
|
||||||
queue: DispatchQueue,
|
|
||||||
serializer: Serializer)
|
|
||||||
where Value == Serializer.SerializedObject {
|
|
||||||
self.request = request
|
|
||||||
responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Publishes only the `Result` of the `DataResponse` value.
|
|
||||||
///
|
|
||||||
/// - Returns: The `AnyPublisher` publishing the `Result<Value, AFError>` value.
|
|
||||||
public func result() -> AnyPublisher<Result<Value, AFError>, Never> {
|
|
||||||
map(\.result).eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Publishes the `Result` of the `DataResponse` as a single `Value` or fail with the `AFError` instance.
|
|
||||||
///
|
|
||||||
/// - Returns: The `AnyPublisher<Value, AFError>` publishing the stream.
|
|
||||||
public func value() -> AnyPublisher<Value, AFError> {
|
|
||||||
setFailureType(to: AFError.self).flatMap(\.result.publisher).eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func receive<S>(subscriber: S) where S: Subscriber, DataResponsePublisher.Failure == S.Failure, DataResponsePublisher.Output == S.Input {
|
|
||||||
subscriber.receive(subscription: Inner(request: request,
|
|
||||||
responseHandler: responseHandler,
|
|
||||||
downstream: subscriber))
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class Inner<Downstream: Subscriber>: Subscription
|
|
||||||
where Downstream.Input == Output {
|
|
||||||
typealias Failure = Downstream.Failure
|
|
||||||
|
|
||||||
private let downstream: Protected<Downstream?>
|
|
||||||
private let request: DataRequest
|
|
||||||
private let responseHandler: Handler
|
|
||||||
|
|
||||||
init(request: DataRequest, responseHandler: @escaping Handler, downstream: Downstream) {
|
|
||||||
self.request = request
|
|
||||||
self.responseHandler = responseHandler
|
|
||||||
self.downstream = Protected(downstream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func request(_ demand: Subscribers.Demand) {
|
|
||||||
assert(demand > 0)
|
|
||||||
|
|
||||||
guard let downstream = downstream.read({ $0 }) else { return }
|
|
||||||
|
|
||||||
self.downstream.write(nil)
|
|
||||||
responseHandler { response in
|
|
||||||
_ = downstream.receive(response)
|
|
||||||
downstream.receive(completion: .finished)
|
|
||||||
}.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
func cancel() {
|
|
||||||
request.cancel()
|
|
||||||
downstream.write(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
extension DataResponsePublisher where Value == Data? {
|
|
||||||
/// Creates an instance which publishes a `DataResponse<Data?, AFError>` value without serialization.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public init(_ request: DataRequest, queue: DispatchQueue) {
|
|
||||||
self.request = request
|
|
||||||
responseHandler = { request.response(queue: queue, completionHandler: $0) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension DataRequest {
|
|
||||||
/// Creates a `DataResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - serializer: `ResponseSerializer` used to serialize response `Data`.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DataResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishResponse<Serializer: ResponseSerializer, T>(using serializer: Serializer, on queue: DispatchQueue = .main) -> DataResponsePublisher<T>
|
|
||||||
where Serializer.SerializedObject == T {
|
|
||||||
DataResponsePublisher(self, queue: queue, serializer: serializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the
|
|
||||||
/// response.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
|
|
||||||
/// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()`
|
|
||||||
/// by default.
|
|
||||||
/// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
|
|
||||||
/// default.
|
|
||||||
/// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of
|
|
||||||
/// status code. `[.head]` by default.
|
|
||||||
/// - Returns: The `DataResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishData(queue: DispatchQueue = .main,
|
|
||||||
preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
|
|
||||||
emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher<Data> {
|
|
||||||
publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
on: queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the
|
|
||||||
/// response.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
|
|
||||||
/// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()`
|
|
||||||
/// by default.
|
|
||||||
/// - encoding: `String.Encoding` to parse the response. `nil` by default, in which case the encoding
|
|
||||||
/// will be determined by the server response, falling back to the default HTTP character
|
|
||||||
/// set, `ISO-8859-1`.
|
|
||||||
/// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
|
|
||||||
/// default.
|
|
||||||
/// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of
|
|
||||||
/// status code. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DataResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishString(queue: DispatchQueue = .main,
|
|
||||||
preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
|
|
||||||
encoding: String.Encoding? = nil,
|
|
||||||
emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DataResponsePublisher<String> {
|
|
||||||
publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor,
|
|
||||||
encoding: encoding,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
on: queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
@_disfavoredOverload
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
@available(*, deprecated, message: "Renamed publishDecodable(type:queue:preprocessor:decoder:emptyResponseCodes:emptyRequestMethods).")
|
|
||||||
public func publishDecodable<T: Decodable>(type: T.Type = T.self,
|
|
||||||
queue: DispatchQueue = .main,
|
|
||||||
preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
|
|
||||||
decoder: DataDecoder = JSONDecoder(),
|
|
||||||
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
|
|
||||||
emptyResponseMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DataResponsePublisher<T> {
|
|
||||||
publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor,
|
|
||||||
decoder: decoder,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyResponseMethods),
|
|
||||||
on: queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize the
|
|
||||||
/// response.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - type: `Decodable` type to which to decode response `Data`. Inferred from the context by
|
|
||||||
/// default.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
|
|
||||||
/// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization.
|
|
||||||
/// `PassthroughPreprocessor()` by default.
|
|
||||||
/// - decoder: `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default.
|
|
||||||
/// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
|
|
||||||
/// default.
|
|
||||||
/// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of
|
|
||||||
/// status code. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DataResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishDecodable<T: Decodable>(type: T.Type = T.self,
|
|
||||||
queue: DispatchQueue = .main,
|
|
||||||
preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
|
|
||||||
decoder: DataDecoder = JSONDecoder(),
|
|
||||||
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DataResponsePublisher<T> {
|
|
||||||
publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor,
|
|
||||||
decoder: decoder,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
on: queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataResponsePublisher` for this instance which does not serialize the response before publishing.
|
|
||||||
///
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DataResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishUnserialized(queue: DispatchQueue = .main) -> DataResponsePublisher<Data?> {
|
|
||||||
DataResponsePublisher(self, queue: queue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Combine `Publisher` that publishes a sequence of `Stream<Value, AFError>` values received by the provided `DataStreamRequest`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public struct DataStreamPublisher<Value>: Publisher {
|
|
||||||
public typealias Output = DataStreamRequest.Stream<Value, AFError>
|
|
||||||
public typealias Failure = Never
|
|
||||||
|
|
||||||
private typealias Handler = (@escaping DataStreamRequest.Handler<Value, AFError>) -> DataStreamRequest
|
|
||||||
|
|
||||||
private let request: DataStreamRequest
|
|
||||||
private let streamHandler: Handler
|
|
||||||
|
|
||||||
/// Creates an instance which will serialize responses using the provided `DataStreamSerializer`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: `DataStreamRequest` for which to publish the response.
|
|
||||||
/// - queue: `DispatchQueue` on which the `Stream<Value, AFError>` values will be published. `.main` by
|
|
||||||
/// default.
|
|
||||||
/// - serializer: `DataStreamSerializer` used to produce the published `Stream<Value, AFError>` values.
|
|
||||||
public init<Serializer: DataStreamSerializer>(_ request: DataStreamRequest, queue: DispatchQueue, serializer: Serializer)
|
|
||||||
where Value == Serializer.SerializedObject {
|
|
||||||
self.request = request
|
|
||||||
streamHandler = { request.responseStream(using: serializer, on: queue, stream: $0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Publishes only the `Result` of the `DataStreamRequest.Stream`'s `Event`s.
|
|
||||||
///
|
|
||||||
/// - Returns: The `AnyPublisher` publishing the `Result<Value, AFError>` value.
|
|
||||||
public func result() -> AnyPublisher<Result<Value, AFError>, Never> {
|
|
||||||
compactMap { stream in
|
|
||||||
switch stream.event {
|
|
||||||
case let .stream(result):
|
|
||||||
return result
|
|
||||||
// If the stream has completed with an error, send the error value downstream as a `.failure`.
|
|
||||||
case let .complete(completion):
|
|
||||||
return completion.error.map(Result.failure)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Publishes the streamed values of the `DataStreamRequest.Stream` as a sequence of `Value` or fail with the
|
|
||||||
/// `AFError` instance.
|
|
||||||
///
|
|
||||||
/// - Returns: The `AnyPublisher<Value, AFError>` publishing the stream.
|
|
||||||
public func value() -> AnyPublisher<Value, AFError> {
|
|
||||||
result().setFailureType(to: AFError.self).flatMap(\.publisher).eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func receive<S>(subscriber: S) where S: Subscriber, DataStreamPublisher.Failure == S.Failure, DataStreamPublisher.Output == S.Input {
|
|
||||||
subscriber.receive(subscription: Inner(request: request,
|
|
||||||
streamHandler: streamHandler,
|
|
||||||
downstream: subscriber))
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class Inner<Downstream: Subscriber>: Subscription
|
|
||||||
where Downstream.Input == Output {
|
|
||||||
typealias Failure = Downstream.Failure
|
|
||||||
|
|
||||||
private let downstream: Protected<Downstream?>
|
|
||||||
private let request: DataStreamRequest
|
|
||||||
private let streamHandler: Handler
|
|
||||||
|
|
||||||
init(request: DataStreamRequest, streamHandler: @escaping Handler, downstream: Downstream) {
|
|
||||||
self.request = request
|
|
||||||
self.streamHandler = streamHandler
|
|
||||||
self.downstream = Protected(downstream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func request(_ demand: Subscribers.Demand) {
|
|
||||||
assert(demand > 0)
|
|
||||||
|
|
||||||
guard let downstream = downstream.read({ $0 }) else { return }
|
|
||||||
|
|
||||||
self.downstream.write(nil)
|
|
||||||
streamHandler { stream in
|
|
||||||
_ = downstream.receive(stream)
|
|
||||||
if case .complete = stream.event {
|
|
||||||
downstream.receive(completion: .finished)
|
|
||||||
}
|
|
||||||
}.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
func cancel() {
|
|
||||||
request.cancel()
|
|
||||||
downstream.write(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension DataStreamRequest {
|
|
||||||
/// Creates a `DataStreamPublisher` for this instance using the given `DataStreamSerializer` and `DispatchQueue`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - serializer: `DataStreamSerializer` used to serialize the streamed `Data`.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default.
|
|
||||||
/// - Returns: The `DataStreamPublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishStream<Serializer: DataStreamSerializer>(using serializer: Serializer,
|
|
||||||
on queue: DispatchQueue = .main) -> DataStreamPublisher<Serializer.SerializedObject> {
|
|
||||||
DataStreamPublisher(self, queue: queue, serializer: serializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataStreamPublisher` for this instance which uses a `PassthroughStreamSerializer` to stream `Data`
|
|
||||||
/// unserialized.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default.
|
|
||||||
/// - Returns: The `DataStreamPublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishData(queue: DispatchQueue = .main) -> DataStreamPublisher<Data> {
|
|
||||||
publishStream(using: PassthroughStreamSerializer(), on: queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataStreamPublisher` for this instance which uses a `StringStreamSerializer` to serialize stream
|
|
||||||
/// `Data` values into `String` values.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default.
|
|
||||||
/// - Returns: The `DataStreamPublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishString(queue: DispatchQueue = .main) -> DataStreamPublisher<String> {
|
|
||||||
publishStream(using: StringStreamSerializer(), on: queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataStreamPublisher` for this instance which uses a `DecodableStreamSerializer` with the provided
|
|
||||||
/// parameters to serialize stream `Data` values into the provided type.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - type: `Decodable` type to which to decode stream `Data`. Inferred from the context by default.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataRequest.Stream` values will be published. `.main` by default.
|
|
||||||
/// - decoder: `DataDecoder` instance used to decode stream `Data`. `JSONDecoder()` by default.
|
|
||||||
/// - preprocessor: `DataPreprocessor` which filters incoming stream `Data` before serialization.
|
|
||||||
/// `PassthroughPreprocessor()` by default.
|
|
||||||
/// - Returns: The `DataStreamPublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishDecodable<T: Decodable>(type: T.Type = T.self,
|
|
||||||
queue: DispatchQueue = .main,
|
|
||||||
decoder: DataDecoder = JSONDecoder(),
|
|
||||||
preprocessor: DataPreprocessor = PassthroughPreprocessor()) -> DataStreamPublisher<T> {
|
|
||||||
publishStream(using: DecodableStreamSerializer(decoder: decoder,
|
|
||||||
dataPreprocessor: preprocessor),
|
|
||||||
on: queue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Combine `Publisher` that publishes the `DownloadResponse<Value, AFError>` of the provided `DownloadRequest`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public struct DownloadResponsePublisher<Value>: Publisher {
|
|
||||||
public typealias Output = DownloadResponse<Value, AFError>
|
|
||||||
public typealias Failure = Never
|
|
||||||
|
|
||||||
private typealias Handler = (@escaping (_ response: DownloadResponse<Value, AFError>) -> Void) -> DownloadRequest
|
|
||||||
|
|
||||||
private let request: DownloadRequest
|
|
||||||
private let responseHandler: Handler
|
|
||||||
|
|
||||||
/// Creates an instance which will serialize responses using the provided `ResponseSerializer`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: `DownloadRequest` for which to publish the response.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DownloadResponse` value will be published. `.main` by default.
|
|
||||||
/// - serializer: `ResponseSerializer` used to produce the published `DownloadResponse`.
|
|
||||||
public init<Serializer: ResponseSerializer>(_ request: DownloadRequest, queue: DispatchQueue, serializer: Serializer)
|
|
||||||
where Value == Serializer.SerializedObject {
|
|
||||||
self.request = request
|
|
||||||
responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an instance which will serialize responses using the provided `DownloadResponseSerializerProtocol` value.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: `DownloadRequest` for which to publish the response.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataResponse` value will be published. `.main` by default.
|
|
||||||
/// - serializer: `DownloadResponseSerializerProtocol` used to produce the published `DownloadResponse`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public init<Serializer: DownloadResponseSerializerProtocol>(_ request: DownloadRequest,
|
|
||||||
queue: DispatchQueue,
|
|
||||||
serializer: Serializer)
|
|
||||||
where Value == Serializer.SerializedObject {
|
|
||||||
self.request = request
|
|
||||||
responseHandler = { request.response(queue: queue, responseSerializer: serializer, completionHandler: $0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Publishes only the `Result` of the `DownloadResponse` value.
|
|
||||||
///
|
|
||||||
/// - Returns: The `AnyPublisher` publishing the `Result<Value, AFError>` value.
|
|
||||||
public func result() -> AnyPublisher<Result<Value, AFError>, Never> {
|
|
||||||
map(\.result).eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Publishes the `Result` of the `DownloadResponse` as a single `Value` or fail with the `AFError` instance.
|
|
||||||
///
|
|
||||||
/// - Returns: The `AnyPublisher<Value, AFError>` publishing the stream.
|
|
||||||
public func value() -> AnyPublisher<Value, AFError> {
|
|
||||||
setFailureType(to: AFError.self).flatMap(\.result.publisher).eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func receive<S>(subscriber: S) where S: Subscriber, DownloadResponsePublisher.Failure == S.Failure, DownloadResponsePublisher.Output == S.Input {
|
|
||||||
subscriber.receive(subscription: Inner(request: request,
|
|
||||||
responseHandler: responseHandler,
|
|
||||||
downstream: subscriber))
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class Inner<Downstream: Subscriber>: Subscription
|
|
||||||
where Downstream.Input == Output {
|
|
||||||
typealias Failure = Downstream.Failure
|
|
||||||
|
|
||||||
private let downstream: Protected<Downstream?>
|
|
||||||
private let request: DownloadRequest
|
|
||||||
private let responseHandler: Handler
|
|
||||||
|
|
||||||
init(request: DownloadRequest, responseHandler: @escaping Handler, downstream: Downstream) {
|
|
||||||
self.request = request
|
|
||||||
self.responseHandler = responseHandler
|
|
||||||
self.downstream = Protected(downstream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func request(_ demand: Subscribers.Demand) {
|
|
||||||
assert(demand > 0)
|
|
||||||
|
|
||||||
guard let downstream = downstream.read({ $0 }) else { return }
|
|
||||||
|
|
||||||
self.downstream.write(nil)
|
|
||||||
responseHandler { response in
|
|
||||||
_ = downstream.receive(response)
|
|
||||||
downstream.receive(completion: .finished)
|
|
||||||
}.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
func cancel() {
|
|
||||||
request.cancel()
|
|
||||||
downstream.write(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension DownloadRequest {
|
|
||||||
/// Creates a `DownloadResponsePublisher` for this instance using the given `ResponseSerializer` and `DispatchQueue`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - serializer: `ResponseSerializer` used to serialize the response `Data` from disk.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishResponse<Serializer: ResponseSerializer, T>(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher<T>
|
|
||||||
where Serializer.SerializedObject == T {
|
|
||||||
DownloadResponsePublisher(self, queue: queue, serializer: serializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DownloadResponsePublisher` for this instance using the given `DownloadResponseSerializerProtocol` and
|
|
||||||
/// `DispatchQueue`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - serializer: `DownloadResponseSerializer` used to serialize the response `Data` from disk.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DownloadResponse` will be published.`.main` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishResponse<Serializer: DownloadResponseSerializerProtocol, T>(using serializer: Serializer, on queue: DispatchQueue = .main) -> DownloadResponsePublisher<T>
|
|
||||||
where Serializer.SerializedObject == T {
|
|
||||||
DownloadResponsePublisher(self, queue: queue, serializer: serializer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DownloadResponsePublisher` for this instance and uses a `URLResponseSerializer` to serialize the
|
|
||||||
/// response.
|
|
||||||
///
|
|
||||||
/// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishURL(queue: DispatchQueue = .main) -> DownloadResponsePublisher<URL> {
|
|
||||||
publishResponse(using: URLResponseSerializer(), on: queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DownloadResponsePublisher` for this instance and uses a `DataResponseSerializer` to serialize the
|
|
||||||
/// response.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default.
|
|
||||||
/// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()`
|
|
||||||
/// by default.
|
|
||||||
/// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
|
|
||||||
/// default.
|
|
||||||
/// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of
|
|
||||||
/// status code. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishData(queue: DispatchQueue = .main,
|
|
||||||
preprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
|
|
||||||
emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher<Data> {
|
|
||||||
publishResponse(using: DataResponseSerializer(dataPreprocessor: preprocessor,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
on: queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DownloadResponsePublisher` for this instance and uses a `StringResponseSerializer` to serialize the
|
|
||||||
/// response.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
|
|
||||||
/// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization. `PassthroughPreprocessor()`
|
|
||||||
/// by default.
|
|
||||||
/// - encoding: `String.Encoding` to parse the response. `nil` by default, in which case the encoding
|
|
||||||
/// will be determined by the server response, falling back to the default HTTP character
|
|
||||||
/// set, `ISO-8859-1`.
|
|
||||||
/// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
|
|
||||||
/// default.
|
|
||||||
/// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless of
|
|
||||||
/// status code. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishString(queue: DispatchQueue = .main,
|
|
||||||
preprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
|
|
||||||
encoding: String.Encoding? = nil,
|
|
||||||
emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadResponsePublisher<String> {
|
|
||||||
publishResponse(using: StringResponseSerializer(dataPreprocessor: preprocessor,
|
|
||||||
encoding: encoding,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
on: queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
@_disfavoredOverload
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
@available(*, deprecated, message: "Renamed publishDecodable(type:queue:preprocessor:decoder:emptyResponseCodes:emptyRequestMethods).")
|
|
||||||
public func publishDecodable<T: Decodable>(type: T.Type = T.self,
|
|
||||||
queue: DispatchQueue = .main,
|
|
||||||
preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
|
|
||||||
decoder: DataDecoder = JSONDecoder(),
|
|
||||||
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
|
|
||||||
emptyResponseMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DownloadResponsePublisher<T> {
|
|
||||||
publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor,
|
|
||||||
decoder: decoder,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyResponseMethods),
|
|
||||||
on: queue)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DownloadResponsePublisher` for this instance and uses a `DecodableResponseSerializer` to serialize
|
|
||||||
/// the response.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - type: `Decodable` type to which to decode response `Data`. Inferred from the context by default.
|
|
||||||
/// - queue: `DispatchQueue` on which the `DataResponse` will be published. `.main` by default.
|
|
||||||
/// - preprocessor: `DataPreprocessor` which filters the `Data` before serialization.
|
|
||||||
/// `PassthroughPreprocessor()` by default.
|
|
||||||
/// - decoder: `DataDecoder` instance used to decode response `Data`. `JSONDecoder()` by default.
|
|
||||||
/// - emptyResponseCodes: `Set<Int>` of HTTP status codes for which empty responses are allowed. `[204, 205]` by
|
|
||||||
/// default.
|
|
||||||
/// - emptyRequestMethods: `Set<HTTPMethod>` of `HTTPMethod`s for which empty responses are allowed, regardless
|
|
||||||
/// of status code. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishDecodable<T: Decodable>(type: T.Type = T.self,
|
|
||||||
queue: DispatchQueue = .main,
|
|
||||||
preprocessor: DataPreprocessor = DecodableResponseSerializer<T>.defaultDataPreprocessor,
|
|
||||||
decoder: DataDecoder = JSONDecoder(),
|
|
||||||
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<T>.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<T>.defaultEmptyRequestMethods) -> DownloadResponsePublisher<T> {
|
|
||||||
publishResponse(using: DecodableResponseSerializer(dataPreprocessor: preprocessor,
|
|
||||||
decoder: decoder,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
on: queue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
extension DownloadResponsePublisher where Value == URL? {
|
|
||||||
/// Creates an instance which publishes a `DownloadResponse<URL?, AFError>` value without serialization.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public init(_ request: DownloadRequest, queue: DispatchQueue) {
|
|
||||||
self.request = request
|
|
||||||
responseHandler = { request.response(queue: queue, completionHandler: $0) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension DownloadRequest {
|
|
||||||
/// Creates a `DownloadResponsePublisher` for this instance which does not serialize the response before publishing.
|
|
||||||
///
|
|
||||||
/// - Parameter queue: `DispatchQueue` on which the `DownloadResponse` will be published. `.main` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadResponsePublisher`.
|
|
||||||
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
|
|
||||||
public func publishUnserialized(on queue: DispatchQueue = .main) -> DownloadResponsePublisher<URL?> {
|
|
||||||
DownloadResponsePublisher(self, queue: queue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
832
SwiftProject/Pods/Alamofire/Source/Concurrency.swift
generated
832
SwiftProject/Pods/Alamofire/Source/Concurrency.swift
generated
@ -1,832 +0,0 @@
|
|||||||
//
|
|
||||||
// Concurrency.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2021 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
#if compiler(>=5.6.0) && canImport(_Concurrency)
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
// MARK: - Request Event Streams
|
|
||||||
|
|
||||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
||||||
extension Request {
|
|
||||||
/// Creates a `StreamOf<Progress>` for the instance's upload progress.
|
|
||||||
///
|
|
||||||
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `StreamOf<Progress>`.
|
|
||||||
public func uploadProgress(bufferingPolicy: StreamOf<Progress>.BufferingPolicy = .unbounded) -> StreamOf<Progress> {
|
|
||||||
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
||||||
uploadProgress(queue: underlyingQueue) { progress in
|
|
||||||
continuation.yield(progress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `StreamOf<Progress>` for the instance's download progress.
|
|
||||||
///
|
|
||||||
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `StreamOf<Progress>`.
|
|
||||||
public func downloadProgress(bufferingPolicy: StreamOf<Progress>.BufferingPolicy = .unbounded) -> StreamOf<Progress> {
|
|
||||||
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
||||||
downloadProgress(queue: underlyingQueue) { progress in
|
|
||||||
continuation.yield(progress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `StreamOf<URLRequest>` for the `URLRequest`s produced for the instance.
|
|
||||||
///
|
|
||||||
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `StreamOf<URLRequest>`.
|
|
||||||
public func urlRequests(bufferingPolicy: StreamOf<URLRequest>.BufferingPolicy = .unbounded) -> StreamOf<URLRequest> {
|
|
||||||
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
||||||
onURLRequestCreation(on: underlyingQueue) { request in
|
|
||||||
continuation.yield(request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `StreamOf<URLSessionTask>` for the `URLSessionTask`s produced for the instance.
|
|
||||||
///
|
|
||||||
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `StreamOf<URLSessionTask>`.
|
|
||||||
public func urlSessionTasks(bufferingPolicy: StreamOf<URLSessionTask>.BufferingPolicy = .unbounded) -> StreamOf<URLSessionTask> {
|
|
||||||
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
||||||
onURLSessionTaskCreation(on: underlyingQueue) { task in
|
|
||||||
continuation.yield(task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `StreamOf<String>` for the cURL descriptions produced for the instance.
|
|
||||||
///
|
|
||||||
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `StreamOf<String>`.
|
|
||||||
public func cURLDescriptions(bufferingPolicy: StreamOf<String>.BufferingPolicy = .unbounded) -> StreamOf<String> {
|
|
||||||
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
||||||
cURLDescription(on: underlyingQueue) { description in
|
|
||||||
continuation.yield(description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func stream<T>(of type: T.Type = T.self,
|
|
||||||
bufferingPolicy: StreamOf<T>.BufferingPolicy = .unbounded,
|
|
||||||
yielder: @escaping (StreamOf<T>.Continuation) -> Void) -> StreamOf<T> {
|
|
||||||
StreamOf<T>(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
||||||
yielder(continuation)
|
|
||||||
// Must come after serializers run in order to catch retry progress.
|
|
||||||
onFinish {
|
|
||||||
continuation.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - DataTask
|
|
||||||
|
|
||||||
/// Value used to `await` a `DataResponse` and associated values.
|
|
||||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
||||||
public struct DataTask<Value> {
|
|
||||||
/// `DataResponse` produced by the `DataRequest` and its response handler.
|
|
||||||
public var response: DataResponse<Value, AFError> {
|
|
||||||
get async {
|
|
||||||
if shouldAutomaticallyCancel {
|
|
||||||
return await withTaskCancellationHandler {
|
|
||||||
await task.value
|
|
||||||
} onCancel: {
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return await task.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Result` of any response serialization performed for the `response`.
|
|
||||||
public var result: Result<Value, AFError> {
|
|
||||||
get async { await response.result }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Value` returned by the `response`.
|
|
||||||
public var value: Value {
|
|
||||||
get async throws {
|
|
||||||
try await result.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private let request: DataRequest
|
|
||||||
private let task: Task<DataResponse<Value, AFError>, Never>
|
|
||||||
private let shouldAutomaticallyCancel: Bool
|
|
||||||
|
|
||||||
fileprivate init(request: DataRequest, task: Task<DataResponse<Value, AFError>, Never>, shouldAutomaticallyCancel: Bool) {
|
|
||||||
self.request = request
|
|
||||||
self.task = task
|
|
||||||
self.shouldAutomaticallyCancel = shouldAutomaticallyCancel
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cancel the underlying `DataRequest` and `Task`.
|
|
||||||
public func cancel() {
|
|
||||||
task.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resume the underlying `DataRequest`.
|
|
||||||
public func resume() {
|
|
||||||
request.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Suspend the underlying `DataRequest`.
|
|
||||||
public func suspend() {
|
|
||||||
request.suspend()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
||||||
extension DataRequest {
|
|
||||||
/// Creates a `StreamOf<HTTPURLResponse>` for the instance's responses.
|
|
||||||
///
|
|
||||||
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `StreamOf<HTTPURLResponse>`.
|
|
||||||
public func httpResponses(bufferingPolicy: StreamOf<HTTPURLResponse>.BufferingPolicy = .unbounded) -> StreamOf<HTTPURLResponse> {
|
|
||||||
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
||||||
onHTTPResponse(on: underlyingQueue) { response in
|
|
||||||
continuation.yield(response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if swift(>=5.7)
|
|
||||||
/// Sets an async closure returning a `Request.ResponseDisposition`, called whenever the `DataRequest` produces an
|
|
||||||
/// `HTTPURLResponse`.
|
|
||||||
///
|
|
||||||
/// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
|
|
||||||
/// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
|
|
||||||
/// where responses after the first will contain the part headers.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - handler: Async closure executed when a new `HTTPURLResponse` is received and returning a
|
|
||||||
/// `ResponseDisposition` value. This value determines whether to continue the request or cancel it as
|
|
||||||
/// if `cancel()` had been called on the instance. Note, this closure is called on an arbitrary thread,
|
|
||||||
/// so any synchronous calls in it will execute in that context.
|
|
||||||
///
|
|
||||||
/// - Returns: The instance.
|
|
||||||
@_disfavoredOverload
|
|
||||||
@discardableResult
|
|
||||||
public func onHTTPResponse(
|
|
||||||
perform handler: @escaping @Sendable (_ response: HTTPURLResponse) async -> ResponseDisposition
|
|
||||||
) -> Self {
|
|
||||||
onHTTPResponse(on: underlyingQueue) { response, completionHandler in
|
|
||||||
Task {
|
|
||||||
let disposition = await handler(response)
|
|
||||||
completionHandler(disposition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets an async closure called whenever the `DataRequest` produces an `HTTPURLResponse`.
|
|
||||||
///
|
|
||||||
/// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
|
|
||||||
/// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
|
|
||||||
/// where responses after the first will contain the part headers.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - handler: Async closure executed when a new `HTTPURLResponse` is received. Note, this closure is called on an
|
|
||||||
/// arbitrary thread, so any synchronous calls in it will execute in that context.
|
|
||||||
///
|
|
||||||
/// - Returns: The instance.
|
|
||||||
@discardableResult
|
|
||||||
public func onHTTPResponse(perform handler: @escaping @Sendable (_ response: HTTPURLResponse) async -> Void) -> Self {
|
|
||||||
onHTTPResponse { response in
|
|
||||||
await handler(response)
|
|
||||||
return .allow
|
|
||||||
}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Creates a `DataTask` to `await` a `Data` value.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before completion.
|
|
||||||
/// - emptyResponseCodes: HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
|
|
||||||
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DataTask`.
|
|
||||||
public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
||||||
dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
|
|
||||||
emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DataTask<Data> {
|
|
||||||
serializingResponse(using: DataResponseSerializer(dataPreprocessor: dataPreprocessor,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataTask` to `await` serialization of a `Decodable` value.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - type: `Decodable` type to decode from response data.
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
|
|
||||||
/// `PassthroughPreprocessor()` by default.
|
|
||||||
/// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
|
|
||||||
/// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
|
|
||||||
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DataTask`.
|
|
||||||
public func serializingDecodable<Value: Decodable>(_ type: Value.Type = Value.self,
|
|
||||||
automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
||||||
dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<Value>.defaultDataPreprocessor,
|
|
||||||
decoder: DataDecoder = JSONDecoder(),
|
|
||||||
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<Value>.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<Value>.defaultEmptyRequestMethods) -> DataTask<Value> {
|
|
||||||
serializingResponse(using: DecodableResponseSerializer<Value>(dataPreprocessor: dataPreprocessor,
|
|
||||||
decoder: decoder,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataTask` to `await` serialization of a `String` value.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
|
|
||||||
/// `PassthroughPreprocessor()` by default.
|
|
||||||
/// - encoding: `String.Encoding` to use during serialization. Defaults to `nil`, in which case
|
|
||||||
/// the encoding will be determined from the server response, falling back to the
|
|
||||||
/// default HTTP character set, `ISO-8859-1`.
|
|
||||||
/// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
|
|
||||||
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DataTask`.
|
|
||||||
public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
||||||
dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
|
|
||||||
encoding: String.Encoding? = nil,
|
|
||||||
emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DataTask<String> {
|
|
||||||
serializingResponse(using: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
|
|
||||||
encoding: encoding,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataTask` to `await` serialization using the provided `ResponseSerializer` instance.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - serializer: `ResponseSerializer` responsible for serializing the request, response, and data.
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DataTask`.
|
|
||||||
public func serializingResponse<Serializer: ResponseSerializer>(using serializer: Serializer,
|
|
||||||
automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
|
|
||||||
-> DataTask<Serializer.SerializedObject> {
|
|
||||||
dataTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
|
|
||||||
response(queue: underlyingQueue,
|
|
||||||
responseSerializer: serializer,
|
|
||||||
completionHandler: $0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DataTask` to `await` serialization using the provided `DataResponseSerializerProtocol` instance.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - serializer: `DataResponseSerializerProtocol` responsible for serializing the request,
|
|
||||||
/// response, and data.
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DataTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DataTask`.
|
|
||||||
public func serializingResponse<Serializer: DataResponseSerializerProtocol>(using serializer: Serializer,
|
|
||||||
automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
|
|
||||||
-> DataTask<Serializer.SerializedObject> {
|
|
||||||
dataTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
|
|
||||||
response(queue: underlyingQueue,
|
|
||||||
responseSerializer: serializer,
|
|
||||||
completionHandler: $0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func dataTask<Value>(automaticallyCancelling shouldAutomaticallyCancel: Bool,
|
|
||||||
forResponse onResponse: @escaping (@escaping (DataResponse<Value, AFError>) -> Void) -> Void)
|
|
||||||
-> DataTask<Value> {
|
|
||||||
let task = Task {
|
|
||||||
await withTaskCancellationHandler {
|
|
||||||
await withCheckedContinuation { continuation in
|
|
||||||
onResponse {
|
|
||||||
continuation.resume(returning: $0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} onCancel: {
|
|
||||||
self.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DataTask<Value>(request: self, task: task, shouldAutomaticallyCancel: shouldAutomaticallyCancel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - DownloadTask
|
|
||||||
|
|
||||||
/// Value used to `await` a `DownloadResponse` and associated values.
|
|
||||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
||||||
public struct DownloadTask<Value> {
|
|
||||||
/// `DownloadResponse` produced by the `DownloadRequest` and its response handler.
|
|
||||||
public var response: DownloadResponse<Value, AFError> {
|
|
||||||
get async {
|
|
||||||
if shouldAutomaticallyCancel {
|
|
||||||
return await withTaskCancellationHandler {
|
|
||||||
await task.value
|
|
||||||
} onCancel: {
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return await task.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Result` of any response serialization performed for the `response`.
|
|
||||||
public var result: Result<Value, AFError> {
|
|
||||||
get async { await response.result }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Value` returned by the `response`.
|
|
||||||
public var value: Value {
|
|
||||||
get async throws {
|
|
||||||
try await result.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private let task: Task<AFDownloadResponse<Value>, Never>
|
|
||||||
private let request: DownloadRequest
|
|
||||||
private let shouldAutomaticallyCancel: Bool
|
|
||||||
|
|
||||||
fileprivate init(request: DownloadRequest, task: Task<AFDownloadResponse<Value>, Never>, shouldAutomaticallyCancel: Bool) {
|
|
||||||
self.request = request
|
|
||||||
self.task = task
|
|
||||||
self.shouldAutomaticallyCancel = shouldAutomaticallyCancel
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cancel the underlying `DownloadRequest` and `Task`.
|
|
||||||
public func cancel() {
|
|
||||||
task.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resume the underlying `DownloadRequest`.
|
|
||||||
public func resume() {
|
|
||||||
request.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Suspend the underlying `DownloadRequest`.
|
|
||||||
public func suspend() {
|
|
||||||
request.suspend()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
||||||
extension DownloadRequest {
|
|
||||||
/// Creates a `DownloadTask` to `await` a `Data` value.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before completion.
|
|
||||||
/// - emptyResponseCodes: HTTP response codes for which empty responses are allowed. `[204, 205]` by default.
|
|
||||||
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadTask`.
|
|
||||||
public func serializingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
||||||
dataPreprocessor: DataPreprocessor = DataResponseSerializer.defaultDataPreprocessor,
|
|
||||||
emptyResponseCodes: Set<Int> = DataResponseSerializer.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = DataResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask<Data> {
|
|
||||||
serializingDownload(using: DataResponseSerializer(dataPreprocessor: dataPreprocessor,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DownloadTask` to `await` serialization of a `Decodable` value.
|
|
||||||
///
|
|
||||||
/// - Note: This serializer reads the entire response into memory before parsing.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - type: `Decodable` type to decode from response data.
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the serializer.
|
|
||||||
/// `PassthroughPreprocessor()` by default.
|
|
||||||
/// - decoder: `DataDecoder` to use to decode the response. `JSONDecoder()` by default.
|
|
||||||
/// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
|
|
||||||
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadTask`.
|
|
||||||
public func serializingDecodable<Value: Decodable>(_ type: Value.Type = Value.self,
|
|
||||||
automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
||||||
dataPreprocessor: DataPreprocessor = DecodableResponseSerializer<Value>.defaultDataPreprocessor,
|
|
||||||
decoder: DataDecoder = JSONDecoder(),
|
|
||||||
emptyResponseCodes: Set<Int> = DecodableResponseSerializer<Value>.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = DecodableResponseSerializer<Value>.defaultEmptyRequestMethods) -> DownloadTask<Value> {
|
|
||||||
serializingDownload(using: DecodableResponseSerializer<Value>(dataPreprocessor: dataPreprocessor,
|
|
||||||
decoder: decoder,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DownloadTask` to `await` serialization of the downloaded file's `URL` on disk.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadTask`.
|
|
||||||
public func serializingDownloadedFileURL(automaticallyCancelling shouldAutomaticallyCancel: Bool = true) -> DownloadTask<URL> {
|
|
||||||
serializingDownload(using: URLResponseSerializer(),
|
|
||||||
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DownloadTask` to `await` serialization of a `String` value.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
/// - dataPreprocessor: `DataPreprocessor` which processes the received `Data` before calling the
|
|
||||||
/// serializer. `PassthroughPreprocessor()` by default.
|
|
||||||
/// - encoding: `String.Encoding` to use during serialization. Defaults to `nil`, in which case
|
|
||||||
/// the encoding will be determined from the server response, falling back to the
|
|
||||||
/// default HTTP character set, `ISO-8859-1`.
|
|
||||||
/// - emptyResponseCodes: HTTP status codes for which empty responses are always valid. `[204, 205]` by default.
|
|
||||||
/// - emptyRequestMethods: `HTTPMethod`s for which empty responses are always valid. `[.head]` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadTask`.
|
|
||||||
public func serializingString(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
||||||
dataPreprocessor: DataPreprocessor = StringResponseSerializer.defaultDataPreprocessor,
|
|
||||||
encoding: String.Encoding? = nil,
|
|
||||||
emptyResponseCodes: Set<Int> = StringResponseSerializer.defaultEmptyResponseCodes,
|
|
||||||
emptyRequestMethods: Set<HTTPMethod> = StringResponseSerializer.defaultEmptyRequestMethods) -> DownloadTask<String> {
|
|
||||||
serializingDownload(using: StringResponseSerializer(dataPreprocessor: dataPreprocessor,
|
|
||||||
encoding: encoding,
|
|
||||||
emptyResponseCodes: emptyResponseCodes,
|
|
||||||
emptyRequestMethods: emptyRequestMethods),
|
|
||||||
automaticallyCancelling: shouldAutomaticallyCancel)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DownloadTask` to `await` serialization using the provided `ResponseSerializer` instance.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - serializer: `ResponseSerializer` responsible for serializing the request, response, and data.
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadTask`.
|
|
||||||
public func serializingDownload<Serializer: ResponseSerializer>(using serializer: Serializer,
|
|
||||||
automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
|
|
||||||
-> DownloadTask<Serializer.SerializedObject> {
|
|
||||||
downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
|
|
||||||
response(queue: underlyingQueue,
|
|
||||||
responseSerializer: serializer,
|
|
||||||
completionHandler: $0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DownloadTask` to `await` serialization using the provided `DownloadResponseSerializerProtocol`
|
|
||||||
/// instance.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - serializer: `DownloadResponseSerializerProtocol` responsible for serializing the request,
|
|
||||||
/// response, and data.
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` determining whether or not the request should be cancelled when the
|
|
||||||
/// enclosing async context is cancelled. Only applies to `DownloadTask`'s async
|
|
||||||
/// properties. `true` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DownloadTask`.
|
|
||||||
public func serializingDownload<Serializer: DownloadResponseSerializerProtocol>(using serializer: Serializer,
|
|
||||||
automaticallyCancelling shouldAutomaticallyCancel: Bool = true)
|
|
||||||
-> DownloadTask<Serializer.SerializedObject> {
|
|
||||||
downloadTask(automaticallyCancelling: shouldAutomaticallyCancel) { [self] in
|
|
||||||
response(queue: underlyingQueue,
|
|
||||||
responseSerializer: serializer,
|
|
||||||
completionHandler: $0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func downloadTask<Value>(automaticallyCancelling shouldAutomaticallyCancel: Bool,
|
|
||||||
forResponse onResponse: @escaping (@escaping (DownloadResponse<Value, AFError>) -> Void) -> Void)
|
|
||||||
-> DownloadTask<Value> {
|
|
||||||
let task = Task {
|
|
||||||
await withTaskCancellationHandler {
|
|
||||||
await withCheckedContinuation { continuation in
|
|
||||||
onResponse {
|
|
||||||
continuation.resume(returning: $0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} onCancel: {
|
|
||||||
self.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DownloadTask<Value>(request: self, task: task, shouldAutomaticallyCancel: shouldAutomaticallyCancel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - DataStreamTask
|
|
||||||
|
|
||||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
||||||
public struct DataStreamTask {
|
|
||||||
// Type of created streams.
|
|
||||||
public typealias Stream<Success, Failure: Error> = StreamOf<DataStreamRequest.Stream<Success, Failure>>
|
|
||||||
|
|
||||||
private let request: DataStreamRequest
|
|
||||||
|
|
||||||
fileprivate init(request: DataStreamRequest) {
|
|
||||||
self.request = request
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `Stream` of `Data` values from the underlying `DataStreamRequest`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
|
|
||||||
/// which observation of the stream stops. `true` by default.
|
|
||||||
/// - bufferingPolicy: ` BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `Stream`.
|
|
||||||
public func streamingData(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, bufferingPolicy: Stream<Data, Never>.BufferingPolicy = .unbounded) -> Stream<Data, Never> {
|
|
||||||
createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
|
|
||||||
request.responseStream(on: .streamCompletionQueue(forRequestID: request.id), stream: onStream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `Stream` of `UTF-8` `String`s from the underlying `DataStreamRequest`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
|
|
||||||
/// which observation of the stream stops. `true` by default.
|
|
||||||
/// - bufferingPolicy: ` BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
/// - Returns:
|
|
||||||
public func streamingStrings(automaticallyCancelling shouldAutomaticallyCancel: Bool = true, bufferingPolicy: Stream<String, Never>.BufferingPolicy = .unbounded) -> Stream<String, Never> {
|
|
||||||
createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
|
|
||||||
request.responseStreamString(on: .streamCompletionQueue(forRequestID: request.id), stream: onStream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `Stream` of `Decodable` values from the underlying `DataStreamRequest`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - type: `Decodable` type to be serialized from stream payloads.
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
|
|
||||||
/// which observation of the stream stops. `true` by default.
|
|
||||||
/// - bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `Stream`.
|
|
||||||
public func streamingDecodables<T>(_ type: T.Type = T.self,
|
|
||||||
automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
||||||
bufferingPolicy: Stream<T, AFError>.BufferingPolicy = .unbounded)
|
|
||||||
-> Stream<T, AFError> where T: Decodable {
|
|
||||||
streamingResponses(serializedUsing: DecodableStreamSerializer<T>(),
|
|
||||||
automaticallyCancelling: shouldAutomaticallyCancel,
|
|
||||||
bufferingPolicy: bufferingPolicy)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `Stream` of values using the provided `DataStreamSerializer` from the underlying `DataStreamRequest`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - serializer: `DataStreamSerializer` to use to serialize incoming `Data`.
|
|
||||||
/// - shouldAutomaticallyCancel: `Bool` indicating whether the underlying `DataStreamRequest` should be canceled
|
|
||||||
/// which observation of the stream stops. `true` by default.
|
|
||||||
/// - bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `Stream`.
|
|
||||||
public func streamingResponses<Serializer: DataStreamSerializer>(serializedUsing serializer: Serializer,
|
|
||||||
automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
||||||
bufferingPolicy: Stream<Serializer.SerializedObject, AFError>.BufferingPolicy = .unbounded)
|
|
||||||
-> Stream<Serializer.SerializedObject, AFError> {
|
|
||||||
createStream(automaticallyCancelling: shouldAutomaticallyCancel, bufferingPolicy: bufferingPolicy) { onStream in
|
|
||||||
request.responseStream(using: serializer,
|
|
||||||
on: .streamCompletionQueue(forRequestID: request.id),
|
|
||||||
stream: onStream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func createStream<Success, Failure: Error>(automaticallyCancelling shouldAutomaticallyCancel: Bool = true,
|
|
||||||
bufferingPolicy: Stream<Success, Failure>.BufferingPolicy = .unbounded,
|
|
||||||
forResponse onResponse: @escaping (@escaping (DataStreamRequest.Stream<Success, Failure>) -> Void) -> Void)
|
|
||||||
-> Stream<Success, Failure> {
|
|
||||||
StreamOf(bufferingPolicy: bufferingPolicy) {
|
|
||||||
guard shouldAutomaticallyCancel,
|
|
||||||
request.isInitialized || request.isResumed || request.isSuspended else { return }
|
|
||||||
|
|
||||||
cancel()
|
|
||||||
} builder: { continuation in
|
|
||||||
onResponse { stream in
|
|
||||||
continuation.yield(stream)
|
|
||||||
if case .complete = stream.event {
|
|
||||||
continuation.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cancel the underlying `DataStreamRequest`.
|
|
||||||
public func cancel() {
|
|
||||||
request.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resume the underlying `DataStreamRequest`.
|
|
||||||
public func resume() {
|
|
||||||
request.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Suspend the underlying `DataStreamRequest`.
|
|
||||||
public func suspend() {
|
|
||||||
request.suspend()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
||||||
extension DataStreamRequest {
|
|
||||||
/// Creates a `StreamOf<HTTPURLResponse>` for the instance's responses.
|
|
||||||
///
|
|
||||||
/// - Parameter bufferingPolicy: `BufferingPolicy` that determines the stream's buffering behavior.`.unbounded` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `StreamOf<HTTPURLResponse>`.
|
|
||||||
public func httpResponses(bufferingPolicy: StreamOf<HTTPURLResponse>.BufferingPolicy = .unbounded) -> StreamOf<HTTPURLResponse> {
|
|
||||||
stream(bufferingPolicy: bufferingPolicy) { [unowned self] continuation in
|
|
||||||
onHTTPResponse(on: underlyingQueue) { response in
|
|
||||||
continuation.yield(response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if swift(>=5.7)
|
|
||||||
/// Sets an async closure returning a `Request.ResponseDisposition`, called whenever the `DataStreamRequest`
|
|
||||||
/// produces an `HTTPURLResponse`.
|
|
||||||
///
|
|
||||||
/// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
|
|
||||||
/// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
|
|
||||||
/// where responses after the first will contain the part headers.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - handler: Async closure executed when a new `HTTPURLResponse` is received and returning a
|
|
||||||
/// `ResponseDisposition` value. This value determines whether to continue the request or cancel it as
|
|
||||||
/// if `cancel()` had been called on the instance. Note, this closure is called on an arbitrary thread,
|
|
||||||
/// so any synchronous calls in it will execute in that context.
|
|
||||||
///
|
|
||||||
/// - Returns: The instance.
|
|
||||||
@_disfavoredOverload
|
|
||||||
@discardableResult
|
|
||||||
public func onHTTPResponse(perform handler: @escaping @Sendable (HTTPURLResponse) async -> ResponseDisposition) -> Self {
|
|
||||||
onHTTPResponse(on: underlyingQueue) { response, completionHandler in
|
|
||||||
Task {
|
|
||||||
let disposition = await handler(response)
|
|
||||||
completionHandler(disposition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets an async closure called whenever the `DataStreamRequest` produces an `HTTPURLResponse`.
|
|
||||||
///
|
|
||||||
/// - Note: Most requests will only produce a single response for each outgoing attempt (initial + retries).
|
|
||||||
/// However, some types of response may trigger multiple `HTTPURLResponse`s, such as multipart streams,
|
|
||||||
/// where responses after the first will contain the part headers.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - handler: Async closure executed when a new `HTTPURLResponse` is received. Note, this closure is called on an
|
|
||||||
/// arbitrary thread, so any synchronous calls in it will execute in that context.
|
|
||||||
///
|
|
||||||
/// - Returns: The instance.
|
|
||||||
@discardableResult
|
|
||||||
public func onHTTPResponse(perform handler: @escaping @Sendable (HTTPURLResponse) async -> Void) -> Self {
|
|
||||||
onHTTPResponse { response in
|
|
||||||
await handler(response)
|
|
||||||
return .allow
|
|
||||||
}
|
|
||||||
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Creates a `DataStreamTask` used to `await` streams of serialized values.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DataStreamTask`.
|
|
||||||
public func streamTask() -> DataStreamTask {
|
|
||||||
DataStreamTask(request: self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension DispatchQueue {
|
|
||||||
fileprivate static let singleEventQueue = DispatchQueue(label: "org.alamofire.concurrencySingleEventQueue",
|
|
||||||
attributes: .concurrent)
|
|
||||||
|
|
||||||
fileprivate static func streamCompletionQueue(forRequestID id: UUID) -> DispatchQueue {
|
|
||||||
DispatchQueue(label: "org.alamofire.concurrencyStreamCompletionQueue-\(id)", target: .singleEventQueue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An asynchronous sequence generated from an underlying `AsyncStream`. Only produced by Alamofire.
|
|
||||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
||||||
public struct StreamOf<Element>: AsyncSequence {
|
|
||||||
public typealias AsyncIterator = Iterator
|
|
||||||
public typealias BufferingPolicy = AsyncStream<Element>.Continuation.BufferingPolicy
|
|
||||||
fileprivate typealias Continuation = AsyncStream<Element>.Continuation
|
|
||||||
|
|
||||||
private let bufferingPolicy: BufferingPolicy
|
|
||||||
private let onTermination: (() -> Void)?
|
|
||||||
private let builder: (Continuation) -> Void
|
|
||||||
|
|
||||||
fileprivate init(bufferingPolicy: BufferingPolicy = .unbounded,
|
|
||||||
onTermination: (() -> Void)? = nil,
|
|
||||||
builder: @escaping (Continuation) -> Void) {
|
|
||||||
self.bufferingPolicy = bufferingPolicy
|
|
||||||
self.onTermination = onTermination
|
|
||||||
self.builder = builder
|
|
||||||
}
|
|
||||||
|
|
||||||
public func makeAsyncIterator() -> Iterator {
|
|
||||||
var continuation: AsyncStream<Element>.Continuation?
|
|
||||||
let stream = AsyncStream<Element>(bufferingPolicy: bufferingPolicy) { innerContinuation in
|
|
||||||
continuation = innerContinuation
|
|
||||||
builder(innerContinuation)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Iterator(iterator: stream.makeAsyncIterator()) {
|
|
||||||
continuation?.finish()
|
|
||||||
onTermination?()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Iterator: AsyncIteratorProtocol {
|
|
||||||
private final class Token {
|
|
||||||
private let onDeinit: () -> Void
|
|
||||||
|
|
||||||
init(onDeinit: @escaping () -> Void) {
|
|
||||||
self.onDeinit = onDeinit
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
onDeinit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var iterator: AsyncStream<Element>.AsyncIterator
|
|
||||||
private let token: Token
|
|
||||||
|
|
||||||
init(iterator: AsyncStream<Element>.AsyncIterator, onCancellation: @escaping () -> Void) {
|
|
||||||
self.iterator = iterator
|
|
||||||
token = Token(onDeinit: onCancellation)
|
|
||||||
}
|
|
||||||
|
|
||||||
public mutating func next() async -> Element? {
|
|
||||||
await iterator.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
//
|
|
||||||
// DispatchQueue+Alamofire.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Dispatch
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension DispatchQueue {
|
|
||||||
/// Execute the provided closure after a `TimeInterval`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - delay: `TimeInterval` to delay execution.
|
|
||||||
/// - closure: Closure to execute.
|
|
||||||
func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
|
|
||||||
asyncAfter(deadline: .now() + delay, execute: closure)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
907
SwiftProject/Pods/Alamofire/Source/EventMonitor.swift
generated
907
SwiftProject/Pods/Alamofire/Source/EventMonitor.swift
generated
@ -1,907 +0,0 @@
|
|||||||
//
|
|
||||||
// EventMonitor.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Protocol outlining the lifetime events inside Alamofire. It includes both events received from the various
|
|
||||||
/// `URLSession` delegate protocols as well as various events from the lifetime of `Request` and its subclasses.
|
|
||||||
public protocol EventMonitor {
|
|
||||||
/// The `DispatchQueue` onto which Alamofire's root `CompositeEventMonitor` will dispatch events. `.main` by default.
|
|
||||||
var queue: DispatchQueue { get }
|
|
||||||
|
|
||||||
// MARK: - URLSession Events
|
|
||||||
|
|
||||||
// MARK: URLSessionDelegate Events
|
|
||||||
|
|
||||||
/// Event called during `URLSessionDelegate`'s `urlSession(_:didBecomeInvalidWithError:)` method.
|
|
||||||
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?)
|
|
||||||
|
|
||||||
// MARK: URLSessionTaskDelegate Events
|
|
||||||
|
|
||||||
/// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didReceive:completionHandler:)` method.
|
|
||||||
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge)
|
|
||||||
|
|
||||||
/// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` method.
|
|
||||||
func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
didSendBodyData bytesSent: Int64,
|
|
||||||
totalBytesSent: Int64,
|
|
||||||
totalBytesExpectedToSend: Int64)
|
|
||||||
|
|
||||||
/// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:needNewBodyStream:)` method.
|
|
||||||
func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask)
|
|
||||||
|
|
||||||
/// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` method.
|
|
||||||
func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
willPerformHTTPRedirection response: HTTPURLResponse,
|
|
||||||
newRequest request: URLRequest)
|
|
||||||
|
|
||||||
/// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didFinishCollecting:)` method.
|
|
||||||
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics)
|
|
||||||
|
|
||||||
/// Event called during `URLSessionTaskDelegate`'s `urlSession(_:task:didCompleteWithError:)` method.
|
|
||||||
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
|
|
||||||
|
|
||||||
/// Event called during `URLSessionTaskDelegate`'s `urlSession(_:taskIsWaitingForConnectivity:)` method.
|
|
||||||
func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask)
|
|
||||||
|
|
||||||
// MARK: URLSessionDataDelegate Events
|
|
||||||
|
|
||||||
/// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:completionHandler:)` method.
|
|
||||||
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse)
|
|
||||||
|
|
||||||
/// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:didReceive:)` method.
|
|
||||||
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
|
|
||||||
|
|
||||||
/// Event called during `URLSessionDataDelegate`'s `urlSession(_:dataTask:willCacheResponse:completionHandler:)` method.
|
|
||||||
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse)
|
|
||||||
|
|
||||||
// MARK: URLSessionDownloadDelegate Events
|
|
||||||
|
|
||||||
/// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` method.
|
|
||||||
func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didResumeAtOffset fileOffset: Int64,
|
|
||||||
expectedTotalBytes: Int64)
|
|
||||||
|
|
||||||
/// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` method.
|
|
||||||
func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didWriteData bytesWritten: Int64,
|
|
||||||
totalBytesWritten: Int64,
|
|
||||||
totalBytesExpectedToWrite: Int64)
|
|
||||||
|
|
||||||
/// Event called during `URLSessionDownloadDelegate`'s `urlSession(_:downloadTask:didFinishDownloadingTo:)` method.
|
|
||||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
|
|
||||||
|
|
||||||
// MARK: - Request Events
|
|
||||||
|
|
||||||
/// Event called when a `URLRequest` is first created for a `Request`. If a `RequestAdapter` is active, the
|
|
||||||
/// `URLRequest` will be adapted before being issued.
|
|
||||||
func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest)
|
|
||||||
|
|
||||||
/// Event called when the attempt to create a `URLRequest` from a `Request`'s original `URLRequestConvertible` value fails.
|
|
||||||
func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError)
|
|
||||||
|
|
||||||
/// Event called when a `RequestAdapter` adapts the `Request`'s initial `URLRequest`.
|
|
||||||
func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest)
|
|
||||||
|
|
||||||
/// Event called when a `RequestAdapter` fails to adapt the `Request`'s initial `URLRequest`.
|
|
||||||
func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError)
|
|
||||||
|
|
||||||
/// Event called when a final `URLRequest` is created for a `Request`.
|
|
||||||
func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest)
|
|
||||||
|
|
||||||
/// Event called when a `URLSessionTask` subclass instance is created for a `Request`.
|
|
||||||
func request(_ request: Request, didCreateTask task: URLSessionTask)
|
|
||||||
|
|
||||||
/// Event called when a `Request` receives a `URLSessionTaskMetrics` value.
|
|
||||||
func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics)
|
|
||||||
|
|
||||||
/// Event called when a `Request` fails due to an error created by Alamofire. e.g. When certificate pinning fails.
|
|
||||||
func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError)
|
|
||||||
|
|
||||||
/// Event called when a `Request`'s task completes, possibly with an error. A `Request` may receive this event
|
|
||||||
/// multiple times if it is retried.
|
|
||||||
func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?)
|
|
||||||
|
|
||||||
/// Event called when a `Request` is about to be retried.
|
|
||||||
func requestIsRetrying(_ request: Request)
|
|
||||||
|
|
||||||
/// Event called when a `Request` finishes and response serializers are being called.
|
|
||||||
func requestDidFinish(_ request: Request)
|
|
||||||
|
|
||||||
/// Event called when a `Request` receives a `resume` call.
|
|
||||||
func requestDidResume(_ request: Request)
|
|
||||||
|
|
||||||
/// Event called when a `Request`'s associated `URLSessionTask` is resumed.
|
|
||||||
func request(_ request: Request, didResumeTask task: URLSessionTask)
|
|
||||||
|
|
||||||
/// Event called when a `Request` receives a `suspend` call.
|
|
||||||
func requestDidSuspend(_ request: Request)
|
|
||||||
|
|
||||||
/// Event called when a `Request`'s associated `URLSessionTask` is suspended.
|
|
||||||
func request(_ request: Request, didSuspendTask task: URLSessionTask)
|
|
||||||
|
|
||||||
/// Event called when a `Request` receives a `cancel` call.
|
|
||||||
func requestDidCancel(_ request: Request)
|
|
||||||
|
|
||||||
/// Event called when a `Request`'s associated `URLSessionTask` is cancelled.
|
|
||||||
func request(_ request: Request, didCancelTask task: URLSessionTask)
|
|
||||||
|
|
||||||
// MARK: DataRequest Events
|
|
||||||
|
|
||||||
/// Event called when a `DataRequest` calls a `Validation`.
|
|
||||||
func request(_ request: DataRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
data: Data?,
|
|
||||||
withResult result: Request.ValidationResult)
|
|
||||||
|
|
||||||
/// Event called when a `DataRequest` creates a `DataResponse<Data?>` value without calling a `ResponseSerializer`.
|
|
||||||
func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>)
|
|
||||||
|
|
||||||
/// Event called when a `DataRequest` calls a `ResponseSerializer` and creates a generic `DataResponse<Value, AFError>`.
|
|
||||||
func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>)
|
|
||||||
|
|
||||||
// MARK: DataStreamRequest Events
|
|
||||||
|
|
||||||
/// Event called when a `DataStreamRequest` calls a `Validation` closure.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: `DataStreamRequest` which is calling the `Validation`.
|
|
||||||
/// - urlRequest: `URLRequest` of the request being validated.
|
|
||||||
/// - response: `HTTPURLResponse` of the request being validated.
|
|
||||||
/// - result: Produced `ValidationResult`.
|
|
||||||
func request(_ request: DataStreamRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
withResult result: Request.ValidationResult)
|
|
||||||
|
|
||||||
/// Event called when a `DataStreamSerializer` produces a value from streamed `Data`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: `DataStreamRequest` for which the value was serialized.
|
|
||||||
/// - result: `Result` of the serialization attempt.
|
|
||||||
func request<Value>(_ request: DataStreamRequest, didParseStream result: Result<Value, AFError>)
|
|
||||||
|
|
||||||
// MARK: UploadRequest Events
|
|
||||||
|
|
||||||
/// Event called when an `UploadRequest` creates its `Uploadable` value, indicating the type of upload it represents.
|
|
||||||
func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable)
|
|
||||||
|
|
||||||
/// Event called when an `UploadRequest` failed to create its `Uploadable` value due to an error.
|
|
||||||
func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError)
|
|
||||||
|
|
||||||
/// Event called when an `UploadRequest` provides the `InputStream` from its `Uploadable` value. This only occurs if
|
|
||||||
/// the `InputStream` does not wrap a `Data` value or file `URL`.
|
|
||||||
func request(_ request: UploadRequest, didProvideInputStream stream: InputStream)
|
|
||||||
|
|
||||||
// MARK: DownloadRequest Events
|
|
||||||
|
|
||||||
/// Event called when a `DownloadRequest`'s `URLSessionDownloadTask` finishes and the temporary file has been moved.
|
|
||||||
func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>)
|
|
||||||
|
|
||||||
/// Event called when a `DownloadRequest`'s `Destination` closure is called and creates the destination URL the
|
|
||||||
/// downloaded file will be moved to.
|
|
||||||
func request(_ request: DownloadRequest, didCreateDestinationURL url: URL)
|
|
||||||
|
|
||||||
/// Event called when a `DownloadRequest` calls a `Validation`.
|
|
||||||
func request(_ request: DownloadRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
fileURL: URL?,
|
|
||||||
withResult result: Request.ValidationResult)
|
|
||||||
|
|
||||||
/// Event called when a `DownloadRequest` creates a `DownloadResponse<URL?, AFError>` without calling a `ResponseSerializer`.
|
|
||||||
func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>)
|
|
||||||
|
|
||||||
/// Event called when a `DownloadRequest` calls a `DownloadResponseSerializer` and creates a generic `DownloadResponse<Value, AFError>`
|
|
||||||
func request<Value>(_ request: DownloadRequest, didParseResponse response: DownloadResponse<Value, AFError>)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension EventMonitor {
|
|
||||||
/// The default queue on which `CompositeEventMonitor`s will call the `EventMonitor` methods. `.main` by default.
|
|
||||||
public var queue: DispatchQueue { .main }
|
|
||||||
|
|
||||||
// MARK: Default Implementations
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {}
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
didReceive challenge: URLAuthenticationChallenge) {}
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
didSendBodyData bytesSent: Int64,
|
|
||||||
totalBytesSent: Int64,
|
|
||||||
totalBytesExpectedToSend: Int64) {}
|
|
||||||
public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) {}
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
willPerformHTTPRedirection response: HTTPURLResponse,
|
|
||||||
newRequest request: URLRequest) {}
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
didFinishCollecting metrics: URLSessionTaskMetrics) {}
|
|
||||||
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {}
|
|
||||||
public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {}
|
|
||||||
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) {}
|
|
||||||
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {}
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
dataTask: URLSessionDataTask,
|
|
||||||
willCacheResponse proposedResponse: CachedURLResponse) {}
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didResumeAtOffset fileOffset: Int64,
|
|
||||||
expectedTotalBytes: Int64) {}
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didWriteData bytesWritten: Int64,
|
|
||||||
totalBytesWritten: Int64,
|
|
||||||
totalBytesExpectedToWrite: Int64) {}
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didFinishDownloadingTo location: URL) {}
|
|
||||||
public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {}
|
|
||||||
public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {}
|
|
||||||
public func request(_ request: Request,
|
|
||||||
didAdaptInitialRequest initialRequest: URLRequest,
|
|
||||||
to adaptedRequest: URLRequest) {}
|
|
||||||
public func request(_ request: Request,
|
|
||||||
didFailToAdaptURLRequest initialRequest: URLRequest,
|
|
||||||
withError error: AFError) {}
|
|
||||||
public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {}
|
|
||||||
public func request(_ request: Request, didCreateTask task: URLSessionTask) {}
|
|
||||||
public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) {}
|
|
||||||
public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {}
|
|
||||||
public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {}
|
|
||||||
public func requestIsRetrying(_ request: Request) {}
|
|
||||||
public func requestDidFinish(_ request: Request) {}
|
|
||||||
public func requestDidResume(_ request: Request) {}
|
|
||||||
public func request(_ request: Request, didResumeTask task: URLSessionTask) {}
|
|
||||||
public func requestDidSuspend(_ request: Request) {}
|
|
||||||
public func request(_ request: Request, didSuspendTask task: URLSessionTask) {}
|
|
||||||
public func requestDidCancel(_ request: Request) {}
|
|
||||||
public func request(_ request: Request, didCancelTask task: URLSessionTask) {}
|
|
||||||
public func request(_ request: DataRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
data: Data?,
|
|
||||||
withResult result: Request.ValidationResult) {}
|
|
||||||
public func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>) {}
|
|
||||||
public func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {}
|
|
||||||
public func request(_ request: DataStreamRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
withResult result: Request.ValidationResult) {}
|
|
||||||
public func request<Value>(_ request: DataStreamRequest, didParseStream result: Result<Value, AFError>) {}
|
|
||||||
public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) {}
|
|
||||||
public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) {}
|
|
||||||
public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) {}
|
|
||||||
public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>) {}
|
|
||||||
public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) {}
|
|
||||||
public func request(_ request: DownloadRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
fileURL: URL?,
|
|
||||||
withResult result: Request.ValidationResult) {}
|
|
||||||
public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>) {}
|
|
||||||
public func request<Value>(_ request: DownloadRequest, didParseResponse response: DownloadResponse<Value, AFError>) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An `EventMonitor` which can contain multiple `EventMonitor`s and calls their methods on their queues.
|
|
||||||
public final class CompositeEventMonitor: EventMonitor {
|
|
||||||
public let queue = DispatchQueue(label: "org.alamofire.compositeEventMonitor", qos: .utility)
|
|
||||||
|
|
||||||
let monitors: [EventMonitor]
|
|
||||||
|
|
||||||
init(monitors: [EventMonitor]) {
|
|
||||||
self.monitors = monitors
|
|
||||||
}
|
|
||||||
|
|
||||||
func performEvent(_ event: @escaping (EventMonitor) -> Void) {
|
|
||||||
queue.async {
|
|
||||||
for monitor in self.monitors {
|
|
||||||
monitor.queue.async { event(monitor) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
|
|
||||||
performEvent { $0.urlSession(session, didBecomeInvalidWithError: error) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
didReceive challenge: URLAuthenticationChallenge) {
|
|
||||||
performEvent { $0.urlSession(session, task: task, didReceive: challenge) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
didSendBodyData bytesSent: Int64,
|
|
||||||
totalBytesSent: Int64,
|
|
||||||
totalBytesExpectedToSend: Int64) {
|
|
||||||
performEvent {
|
|
||||||
$0.urlSession(session,
|
|
||||||
task: task,
|
|
||||||
didSendBodyData: bytesSent,
|
|
||||||
totalBytesSent: totalBytesSent,
|
|
||||||
totalBytesExpectedToSend: totalBytesExpectedToSend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) {
|
|
||||||
performEvent {
|
|
||||||
$0.urlSession(session, taskNeedsNewBodyStream: task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
willPerformHTTPRedirection response: HTTPURLResponse,
|
|
||||||
newRequest request: URLRequest) {
|
|
||||||
performEvent {
|
|
||||||
$0.urlSession(session,
|
|
||||||
task: task,
|
|
||||||
willPerformHTTPRedirection: response,
|
|
||||||
newRequest: request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
|
|
||||||
performEvent { $0.urlSession(session, task: task, didFinishCollecting: metrics) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
|
||||||
performEvent { $0.urlSession(session, task: task, didCompleteWithError: error) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
|
|
||||||
public func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {
|
|
||||||
performEvent { $0.urlSession(session, taskIsWaitingForConnectivity: task) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) {
|
|
||||||
performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: response) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
|
||||||
performEvent { $0.urlSession(session, dataTask: dataTask, didReceive: data) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
dataTask: URLSessionDataTask,
|
|
||||||
willCacheResponse proposedResponse: CachedURLResponse) {
|
|
||||||
performEvent { $0.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didResumeAtOffset fileOffset: Int64,
|
|
||||||
expectedTotalBytes: Int64) {
|
|
||||||
performEvent {
|
|
||||||
$0.urlSession(session,
|
|
||||||
downloadTask: downloadTask,
|
|
||||||
didResumeAtOffset: fileOffset,
|
|
||||||
expectedTotalBytes: expectedTotalBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didWriteData bytesWritten: Int64,
|
|
||||||
totalBytesWritten: Int64,
|
|
||||||
totalBytesExpectedToWrite: Int64) {
|
|
||||||
performEvent {
|
|
||||||
$0.urlSession(session,
|
|
||||||
downloadTask: downloadTask,
|
|
||||||
didWriteData: bytesWritten,
|
|
||||||
totalBytesWritten: totalBytesWritten,
|
|
||||||
totalBytesExpectedToWrite: totalBytesExpectedToWrite)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didFinishDownloadingTo location: URL) {
|
|
||||||
performEvent { $0.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {
|
|
||||||
performEvent { $0.request(request, didCreateInitialURLRequest: urlRequest) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {
|
|
||||||
performEvent { $0.request(request, didFailToCreateURLRequestWithError: error) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) {
|
|
||||||
performEvent { $0.request(request, didAdaptInitialRequest: initialRequest, to: adaptedRequest) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) {
|
|
||||||
performEvent { $0.request(request, didFailToAdaptURLRequest: initialRequest, withError: error) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
|
|
||||||
performEvent { $0.request(request, didCreateURLRequest: urlRequest) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didCreateTask task: URLSessionTask) {
|
|
||||||
performEvent { $0.request(request, didCreateTask: task) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) {
|
|
||||||
performEvent { $0.request(request, didGatherMetrics: metrics) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {
|
|
||||||
performEvent { $0.request(request, didFailTask: task, earlyWithError: error) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {
|
|
||||||
performEvent { $0.request(request, didCompleteTask: task, with: error) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func requestIsRetrying(_ request: Request) {
|
|
||||||
performEvent { $0.requestIsRetrying(request) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func requestDidFinish(_ request: Request) {
|
|
||||||
performEvent { $0.requestDidFinish(request) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func requestDidResume(_ request: Request) {
|
|
||||||
performEvent { $0.requestDidResume(request) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didResumeTask task: URLSessionTask) {
|
|
||||||
performEvent { $0.request(request, didResumeTask: task) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func requestDidSuspend(_ request: Request) {
|
|
||||||
performEvent { $0.requestDidSuspend(request) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didSuspendTask task: URLSessionTask) {
|
|
||||||
performEvent { $0.request(request, didSuspendTask: task) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func requestDidCancel(_ request: Request) {
|
|
||||||
performEvent { $0.requestDidCancel(request) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didCancelTask task: URLSessionTask) {
|
|
||||||
performEvent { $0.request(request, didCancelTask: task) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: DataRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
data: Data?,
|
|
||||||
withResult result: Request.ValidationResult) {
|
|
||||||
performEvent { $0.request(request,
|
|
||||||
didValidateRequest: urlRequest,
|
|
||||||
response: response,
|
|
||||||
data: data,
|
|
||||||
withResult: result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>) {
|
|
||||||
performEvent { $0.request(request, didParseResponse: response) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {
|
|
||||||
performEvent { $0.request(request, didParseResponse: response) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: DataStreamRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
withResult result: Request.ValidationResult) {
|
|
||||||
performEvent { $0.request(request,
|
|
||||||
didValidateRequest: urlRequest,
|
|
||||||
response: response,
|
|
||||||
withResult: result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request<Value>(_ request: DataStreamRequest, didParseStream result: Result<Value, AFError>) {
|
|
||||||
performEvent { $0.request(request, didParseStream: result) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) {
|
|
||||||
performEvent { $0.request(request, didCreateUploadable: uploadable) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) {
|
|
||||||
performEvent { $0.request(request, didFailToCreateUploadableWithError: error) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) {
|
|
||||||
performEvent { $0.request(request, didProvideInputStream: stream) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>) {
|
|
||||||
performEvent { $0.request(request, didFinishDownloadingUsing: task, with: result) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) {
|
|
||||||
performEvent { $0.request(request, didCreateDestinationURL: url) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: DownloadRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
fileURL: URL?,
|
|
||||||
withResult result: Request.ValidationResult) {
|
|
||||||
performEvent { $0.request(request,
|
|
||||||
didValidateRequest: urlRequest,
|
|
||||||
response: response,
|
|
||||||
fileURL: fileURL,
|
|
||||||
withResult: result) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>) {
|
|
||||||
performEvent { $0.request(request, didParseResponse: response) }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request<Value>(_ request: DownloadRequest, didParseResponse response: DownloadResponse<Value, AFError>) {
|
|
||||||
performEvent { $0.request(request, didParseResponse: response) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `EventMonitor` that allows optional closures to be set to receive events.
|
|
||||||
open class ClosureEventMonitor: EventMonitor {
|
|
||||||
/// Closure called on the `urlSession(_:didBecomeInvalidWithError:)` event.
|
|
||||||
open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:task:didReceive:completionHandler:)`.
|
|
||||||
open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> Void)?
|
|
||||||
|
|
||||||
/// Closure that receives `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)` event.
|
|
||||||
open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:task:needNewBodyStream:)` event.
|
|
||||||
open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` event.
|
|
||||||
open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:task:didFinishCollecting:)` event.
|
|
||||||
open var taskDidFinishCollectingMetrics: ((URLSession, URLSessionTask, URLSessionTaskMetrics) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:task:didCompleteWithError:)` event.
|
|
||||||
open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:taskIsWaitingForConnectivity:)` event.
|
|
||||||
open var taskIsWaitingForConnectivity: ((URLSession, URLSessionTask) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:dataTask:didReceive:completionHandler:)` event.
|
|
||||||
open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> Void)?
|
|
||||||
|
|
||||||
/// Closure that receives the `urlSession(_:dataTask:didReceive:)` event.
|
|
||||||
open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:dataTask:willCacheResponse:completionHandler:)` event.
|
|
||||||
open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:downloadTask:didFinishDownloadingTo:)` event.
|
|
||||||
open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`
|
|
||||||
/// event.
|
|
||||||
open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)` event.
|
|
||||||
open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
|
|
||||||
|
|
||||||
// MARK: - Request Events
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didCreateInitialURLRequest:)` event.
|
|
||||||
open var requestDidCreateInitialURLRequest: ((Request, URLRequest) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didFailToCreateURLRequestWithError:)` event.
|
|
||||||
open var requestDidFailToCreateURLRequestWithError: ((Request, AFError) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didAdaptInitialRequest:to:)` event.
|
|
||||||
open var requestDidAdaptInitialRequestToAdaptedRequest: ((Request, URLRequest, URLRequest) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didFailToAdaptURLRequest:withError:)` event.
|
|
||||||
open var requestDidFailToAdaptURLRequestWithError: ((Request, URLRequest, AFError) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didCreateURLRequest:)` event.
|
|
||||||
open var requestDidCreateURLRequest: ((Request, URLRequest) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didCreateTask:)` event.
|
|
||||||
open var requestDidCreateTask: ((Request, URLSessionTask) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didGatherMetrics:)` event.
|
|
||||||
open var requestDidGatherMetrics: ((Request, URLSessionTaskMetrics) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didFailTask:earlyWithError:)` event.
|
|
||||||
open var requestDidFailTaskEarlyWithError: ((Request, URLSessionTask, AFError) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didCompleteTask:with:)` event.
|
|
||||||
open var requestDidCompleteTaskWithError: ((Request, URLSessionTask, AFError?) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `requestIsRetrying(_:)` event.
|
|
||||||
open var requestIsRetrying: ((Request) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `requestDidFinish(_:)` event.
|
|
||||||
open var requestDidFinish: ((Request) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `requestDidResume(_:)` event.
|
|
||||||
open var requestDidResume: ((Request) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didResumeTask:)` event.
|
|
||||||
open var requestDidResumeTask: ((Request, URLSessionTask) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `requestDidSuspend(_:)` event.
|
|
||||||
open var requestDidSuspend: ((Request) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didSuspendTask:)` event.
|
|
||||||
open var requestDidSuspendTask: ((Request, URLSessionTask) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `requestDidCancel(_:)` event.
|
|
||||||
open var requestDidCancel: ((Request) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didCancelTask:)` event.
|
|
||||||
open var requestDidCancelTask: ((Request, URLSessionTask) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didValidateRequest:response:data:withResult:)` event.
|
|
||||||
open var requestDidValidateRequestResponseDataWithResult: ((DataRequest, URLRequest?, HTTPURLResponse, Data?, Request.ValidationResult) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didParseResponse:)` event.
|
|
||||||
open var requestDidParseResponse: ((DataRequest, DataResponse<Data?, AFError>) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didValidateRequest:response:withResult:)` event.
|
|
||||||
open var requestDidValidateRequestResponseWithResult: ((DataStreamRequest, URLRequest?, HTTPURLResponse, Request.ValidationResult) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didCreateUploadable:)` event.
|
|
||||||
open var requestDidCreateUploadable: ((UploadRequest, UploadRequest.Uploadable) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didFailToCreateUploadableWithError:)` event.
|
|
||||||
open var requestDidFailToCreateUploadableWithError: ((UploadRequest, AFError) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didProvideInputStream:)` event.
|
|
||||||
open var requestDidProvideInputStream: ((UploadRequest, InputStream) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didFinishDownloadingUsing:with:)` event.
|
|
||||||
open var requestDidFinishDownloadingUsingTaskWithResult: ((DownloadRequest, URLSessionTask, Result<URL, AFError>) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didCreateDestinationURL:)` event.
|
|
||||||
open var requestDidCreateDestinationURL: ((DownloadRequest, URL) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didValidateRequest:response:temporaryURL:destinationURL:withResult:)` event.
|
|
||||||
open var requestDidValidateRequestResponseFileURLWithResult: ((DownloadRequest, URLRequest?, HTTPURLResponse, URL?, Request.ValidationResult) -> Void)?
|
|
||||||
|
|
||||||
/// Closure called on the `request(_:didParseResponse:)` event.
|
|
||||||
open var requestDidParseDownloadResponse: ((DownloadRequest, DownloadResponse<URL?, AFError>) -> Void)?
|
|
||||||
|
|
||||||
public let queue: DispatchQueue
|
|
||||||
|
|
||||||
/// Creates an instance using the provided queue.
|
|
||||||
///
|
|
||||||
/// - Parameter queue: `DispatchQueue` on which events will fired. `.main` by default.
|
|
||||||
public init(queue: DispatchQueue = .main) {
|
|
||||||
self.queue = queue
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
|
|
||||||
sessionDidBecomeInvalidWithError?(session, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge) {
|
|
||||||
taskDidReceiveChallenge?(session, task, challenge)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
didSendBodyData bytesSent: Int64,
|
|
||||||
totalBytesSent: Int64,
|
|
||||||
totalBytesExpectedToSend: Int64) {
|
|
||||||
taskDidSendBodyData?(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, taskNeedsNewBodyStream task: URLSessionTask) {
|
|
||||||
taskNeedNewBodyStream?(session, task)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
willPerformHTTPRedirection response: HTTPURLResponse,
|
|
||||||
newRequest request: URLRequest) {
|
|
||||||
taskWillPerformHTTPRedirection?(session, task, response, request)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
|
|
||||||
taskDidFinishCollectingMetrics?(session, task, metrics)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
|
||||||
taskDidComplete?(session, task, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
|
|
||||||
open func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {
|
|
||||||
taskIsWaitingForConnectivity?(session, task)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse) {
|
|
||||||
dataTaskDidReceiveResponse?(session, dataTask, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
|
||||||
dataTaskDidReceiveData?(session, dataTask, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse) {
|
|
||||||
dataTaskWillCacheResponse?(session, dataTask, proposedResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didResumeAtOffset fileOffset: Int64,
|
|
||||||
expectedTotalBytes: Int64) {
|
|
||||||
downloadTaskDidResumeAtOffset?(session, downloadTask, fileOffset, expectedTotalBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didWriteData bytesWritten: Int64,
|
|
||||||
totalBytesWritten: Int64,
|
|
||||||
totalBytesExpectedToWrite: Int64) {
|
|
||||||
downloadTaskDidWriteData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
|
|
||||||
downloadTaskDidFinishDownloadingToURL?(session, downloadTask, location)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Request Events
|
|
||||||
|
|
||||||
open func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {
|
|
||||||
requestDidCreateInitialURLRequest?(request, urlRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {
|
|
||||||
requestDidFailToCreateURLRequestWithError?(request, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: Request, didAdaptInitialRequest initialRequest: URLRequest, to adaptedRequest: URLRequest) {
|
|
||||||
requestDidAdaptInitialRequestToAdaptedRequest?(request, initialRequest, adaptedRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: AFError) {
|
|
||||||
requestDidFailToAdaptURLRequestWithError?(request, initialRequest, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
|
|
||||||
requestDidCreateURLRequest?(request, urlRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: Request, didCreateTask task: URLSessionTask) {
|
|
||||||
requestDidCreateTask?(request, task)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) {
|
|
||||||
requestDidGatherMetrics?(request, metrics)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {
|
|
||||||
requestDidFailTaskEarlyWithError?(request, task, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {
|
|
||||||
requestDidCompleteTaskWithError?(request, task, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func requestIsRetrying(_ request: Request) {
|
|
||||||
requestIsRetrying?(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func requestDidFinish(_ request: Request) {
|
|
||||||
requestDidFinish?(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func requestDidResume(_ request: Request) {
|
|
||||||
requestDidResume?(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didResumeTask task: URLSessionTask) {
|
|
||||||
requestDidResumeTask?(request, task)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func requestDidSuspend(_ request: Request) {
|
|
||||||
requestDidSuspend?(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didSuspendTask task: URLSessionTask) {
|
|
||||||
requestDidSuspendTask?(request, task)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func requestDidCancel(_ request: Request) {
|
|
||||||
requestDidCancel?(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didCancelTask task: URLSessionTask) {
|
|
||||||
requestDidCancelTask?(request, task)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: DataRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
data: Data?,
|
|
||||||
withResult result: Request.ValidationResult) {
|
|
||||||
requestDidValidateRequestResponseDataWithResult?(request, urlRequest, response, data, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: DataRequest, didParseResponse response: DataResponse<Data?, AFError>) {
|
|
||||||
requestDidParseResponse?(request, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: DataStreamRequest, didValidateRequest urlRequest: URLRequest?, response: HTTPURLResponse, withResult result: Request.ValidationResult) {
|
|
||||||
requestDidValidateRequestResponseWithResult?(request, urlRequest, response, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: UploadRequest, didCreateUploadable uploadable: UploadRequest.Uploadable) {
|
|
||||||
requestDidCreateUploadable?(request, uploadable)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: UploadRequest, didFailToCreateUploadableWithError error: AFError) {
|
|
||||||
requestDidFailToCreateUploadableWithError?(request, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: UploadRequest, didProvideInputStream stream: InputStream) {
|
|
||||||
requestDidProvideInputStream?(request, stream)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: DownloadRequest, didFinishDownloadingUsing task: URLSessionTask, with result: Result<URL, AFError>) {
|
|
||||||
requestDidFinishDownloadingUsingTaskWithResult?(request, task, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: DownloadRequest, didCreateDestinationURL url: URL) {
|
|
||||||
requestDidCreateDestinationURL?(request, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: DownloadRequest,
|
|
||||||
didValidateRequest urlRequest: URLRequest?,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
fileURL: URL?,
|
|
||||||
withResult result: Request.ValidationResult) {
|
|
||||||
requestDidValidateRequestResponseFileURLWithResult?(request,
|
|
||||||
urlRequest,
|
|
||||||
response,
|
|
||||||
fileURL,
|
|
||||||
result)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func request(_ request: DownloadRequest, didParseResponse response: DownloadResponse<URL?, AFError>) {
|
|
||||||
requestDidParseDownloadResponse?(request, response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
452
SwiftProject/Pods/Alamofire/Source/HTTPHeaders.swift
generated
452
SwiftProject/Pods/Alamofire/Source/HTTPHeaders.swift
generated
@ -1,452 +0,0 @@
|
|||||||
//
|
|
||||||
// HTTPHeaders.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// An order-preserving and case-insensitive representation of HTTP headers.
|
|
||||||
public struct HTTPHeaders {
|
|
||||||
private var headers: [HTTPHeader] = []
|
|
||||||
|
|
||||||
/// Creates an empty instance.
|
|
||||||
public init() {}
|
|
||||||
|
|
||||||
/// Creates an instance from an array of `HTTPHeader`s. Duplicate case-insensitive names are collapsed into the last
|
|
||||||
/// name and value encountered.
|
|
||||||
public init(_ headers: [HTTPHeader]) {
|
|
||||||
headers.forEach { update($0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an instance from a `[String: String]`. Duplicate case-insensitive names are collapsed into the last name
|
|
||||||
/// and value encountered.
|
|
||||||
public init(_ dictionary: [String: String]) {
|
|
||||||
dictionary.forEach { update(HTTPHeader(name: $0.key, value: $0.value)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - name: The `HTTPHeader` name.
|
|
||||||
/// - value: The `HTTPHeader value.
|
|
||||||
public mutating func add(name: String, value: String) {
|
|
||||||
update(HTTPHeader(name: name, value: value))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Case-insensitively updates or appends the provided `HTTPHeader` into the instance.
|
|
||||||
///
|
|
||||||
/// - Parameter header: The `HTTPHeader` to update or append.
|
|
||||||
public mutating func add(_ header: HTTPHeader) {
|
|
||||||
update(header)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - name: The `HTTPHeader` name.
|
|
||||||
/// - value: The `HTTPHeader value.
|
|
||||||
public mutating func update(name: String, value: String) {
|
|
||||||
update(HTTPHeader(name: name, value: value))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Case-insensitively updates or appends the provided `HTTPHeader` into the instance.
|
|
||||||
///
|
|
||||||
/// - Parameter header: The `HTTPHeader` to update or append.
|
|
||||||
public mutating func update(_ header: HTTPHeader) {
|
|
||||||
guard let index = headers.index(of: header.name) else {
|
|
||||||
headers.append(header)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
headers.replaceSubrange(index...index, with: [header])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Case-insensitively removes an `HTTPHeader`, if it exists, from the instance.
|
|
||||||
///
|
|
||||||
/// - Parameter name: The name of the `HTTPHeader` to remove.
|
|
||||||
public mutating func remove(name: String) {
|
|
||||||
guard let index = headers.index(of: name) else { return }
|
|
||||||
|
|
||||||
headers.remove(at: index)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sort the current instance by header name, case insensitively.
|
|
||||||
public mutating func sort() {
|
|
||||||
headers.sort { $0.name.lowercased() < $1.name.lowercased() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an instance sorted by header name.
|
|
||||||
///
|
|
||||||
/// - Returns: A copy of the current instance sorted by name.
|
|
||||||
public func sorted() -> HTTPHeaders {
|
|
||||||
var headers = self
|
|
||||||
headers.sort()
|
|
||||||
|
|
||||||
return headers
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Case-insensitively find a header's value by name.
|
|
||||||
///
|
|
||||||
/// - Parameter name: The name of the header to search for, case-insensitively.
|
|
||||||
///
|
|
||||||
/// - Returns: The value of header, if it exists.
|
|
||||||
public func value(for name: String) -> String? {
|
|
||||||
guard let index = headers.index(of: name) else { return nil }
|
|
||||||
|
|
||||||
return headers[index].value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Case-insensitively access the header with the given name.
|
|
||||||
///
|
|
||||||
/// - Parameter name: The name of the header.
|
|
||||||
public subscript(_ name: String) -> String? {
|
|
||||||
get { value(for: name) }
|
|
||||||
set {
|
|
||||||
if let value = newValue {
|
|
||||||
update(name: name, value: value)
|
|
||||||
} else {
|
|
||||||
remove(name: name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The dictionary representation of all headers.
|
|
||||||
///
|
|
||||||
/// This representation does not preserve the current order of the instance.
|
|
||||||
public var dictionary: [String: String] {
|
|
||||||
let namesAndValues = headers.map { ($0.name, $0.value) }
|
|
||||||
|
|
||||||
return Dictionary(namesAndValues, uniquingKeysWith: { _, last in last })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTTPHeaders: ExpressibleByDictionaryLiteral {
|
|
||||||
public init(dictionaryLiteral elements: (String, String)...) {
|
|
||||||
elements.forEach { update(name: $0.0, value: $0.1) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTTPHeaders: ExpressibleByArrayLiteral {
|
|
||||||
public init(arrayLiteral elements: HTTPHeader...) {
|
|
||||||
self.init(elements)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTTPHeaders: Sequence {
|
|
||||||
public func makeIterator() -> IndexingIterator<[HTTPHeader]> {
|
|
||||||
headers.makeIterator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTTPHeaders: Collection {
|
|
||||||
public var startIndex: Int {
|
|
||||||
headers.startIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
public var endIndex: Int {
|
|
||||||
headers.endIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
public subscript(position: Int) -> HTTPHeader {
|
|
||||||
headers[position]
|
|
||||||
}
|
|
||||||
|
|
||||||
public func index(after i: Int) -> Int {
|
|
||||||
headers.index(after: i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTTPHeaders: CustomStringConvertible {
|
|
||||||
public var description: String {
|
|
||||||
headers.map(\.description)
|
|
||||||
.joined(separator: "\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - HTTPHeader
|
|
||||||
|
|
||||||
/// A representation of a single HTTP header's name / value pair.
|
|
||||||
public struct HTTPHeader: Hashable {
|
|
||||||
/// Name of the header.
|
|
||||||
public let name: String
|
|
||||||
|
|
||||||
/// Value of the header.
|
|
||||||
public let value: String
|
|
||||||
|
|
||||||
/// Creates an instance from the given `name` and `value`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - name: The name of the header.
|
|
||||||
/// - value: The value of the header.
|
|
||||||
public init(name: String, value: String) {
|
|
||||||
self.name = name
|
|
||||||
self.value = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTTPHeader: CustomStringConvertible {
|
|
||||||
public var description: String {
|
|
||||||
"\(name): \(value)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTTPHeader {
|
|
||||||
/// Returns an `Accept` header.
|
|
||||||
///
|
|
||||||
/// - Parameter value: The `Accept` value.
|
|
||||||
/// - Returns: The header.
|
|
||||||
public static func accept(_ value: String) -> HTTPHeader {
|
|
||||||
HTTPHeader(name: "Accept", value: value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an `Accept-Charset` header.
|
|
||||||
///
|
|
||||||
/// - Parameter value: The `Accept-Charset` value.
|
|
||||||
/// - Returns: The header.
|
|
||||||
public static func acceptCharset(_ value: String) -> HTTPHeader {
|
|
||||||
HTTPHeader(name: "Accept-Charset", value: value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an `Accept-Language` header.
|
|
||||||
///
|
|
||||||
/// Alamofire offers a default Accept-Language header that accumulates and encodes the system's preferred languages.
|
|
||||||
/// Use `HTTPHeader.defaultAcceptLanguage`.
|
|
||||||
///
|
|
||||||
/// - Parameter value: The `Accept-Language` value.
|
|
||||||
///
|
|
||||||
/// - Returns: The header.
|
|
||||||
public static func acceptLanguage(_ value: String) -> HTTPHeader {
|
|
||||||
HTTPHeader(name: "Accept-Language", value: value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an `Accept-Encoding` header.
|
|
||||||
///
|
|
||||||
/// Alamofire offers a default accept encoding value that provides the most common values. Use
|
|
||||||
/// `HTTPHeader.defaultAcceptEncoding`.
|
|
||||||
///
|
|
||||||
/// - Parameter value: The `Accept-Encoding` value.
|
|
||||||
///
|
|
||||||
/// - Returns: The header
|
|
||||||
public static func acceptEncoding(_ value: String) -> HTTPHeader {
|
|
||||||
HTTPHeader(name: "Accept-Encoding", value: value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a `Basic` `Authorization` header using the `username` and `password` provided.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - username: The username of the header.
|
|
||||||
/// - password: The password of the header.
|
|
||||||
///
|
|
||||||
/// - Returns: The header.
|
|
||||||
public static func authorization(username: String, password: String) -> HTTPHeader {
|
|
||||||
let credential = Data("\(username):\(password)".utf8).base64EncodedString()
|
|
||||||
|
|
||||||
return authorization("Basic \(credential)")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a `Bearer` `Authorization` header using the `bearerToken` provided
|
|
||||||
///
|
|
||||||
/// - Parameter bearerToken: The bearer token.
|
|
||||||
///
|
|
||||||
/// - Returns: The header.
|
|
||||||
public static func authorization(bearerToken: String) -> HTTPHeader {
|
|
||||||
authorization("Bearer \(bearerToken)")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an `Authorization` header.
|
|
||||||
///
|
|
||||||
/// Alamofire provides built-in methods to produce `Authorization` headers. For a Basic `Authorization` header use
|
|
||||||
/// `HTTPHeader.authorization(username:password:)`. For a Bearer `Authorization` header, use
|
|
||||||
/// `HTTPHeader.authorization(bearerToken:)`.
|
|
||||||
///
|
|
||||||
/// - Parameter value: The `Authorization` value.
|
|
||||||
///
|
|
||||||
/// - Returns: The header.
|
|
||||||
public static func authorization(_ value: String) -> HTTPHeader {
|
|
||||||
HTTPHeader(name: "Authorization", value: value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a `Content-Disposition` header.
|
|
||||||
///
|
|
||||||
/// - Parameter value: The `Content-Disposition` value.
|
|
||||||
///
|
|
||||||
/// - Returns: The header.
|
|
||||||
public static func contentDisposition(_ value: String) -> HTTPHeader {
|
|
||||||
HTTPHeader(name: "Content-Disposition", value: value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a `Content-Encoding` header.
|
|
||||||
///
|
|
||||||
/// - Parameter value: The `Content-Encoding`.
|
|
||||||
///
|
|
||||||
/// - Returns: The header.
|
|
||||||
public static func contentEncoding(_ value: String) -> HTTPHeader {
|
|
||||||
HTTPHeader(name: "Content-Encoding", value: value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a `Content-Type` header.
|
|
||||||
///
|
|
||||||
/// All Alamofire `ParameterEncoding`s and `ParameterEncoder`s set the `Content-Type` of the request, so it may not
|
|
||||||
/// be necessary to manually set this value.
|
|
||||||
///
|
|
||||||
/// - Parameter value: The `Content-Type` value.
|
|
||||||
///
|
|
||||||
/// - Returns: The header.
|
|
||||||
public static func contentType(_ value: String) -> HTTPHeader {
|
|
||||||
HTTPHeader(name: "Content-Type", value: value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a `User-Agent` header.
|
|
||||||
///
|
|
||||||
/// - Parameter value: The `User-Agent` value.
|
|
||||||
///
|
|
||||||
/// - Returns: The header.
|
|
||||||
public static func userAgent(_ value: String) -> HTTPHeader {
|
|
||||||
HTTPHeader(name: "User-Agent", value: value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Array where Element == HTTPHeader {
|
|
||||||
/// Case-insensitively finds the index of an `HTTPHeader` with the provided name, if it exists.
|
|
||||||
func index(of name: String) -> Int? {
|
|
||||||
let lowercasedName = name.lowercased()
|
|
||||||
return firstIndex { $0.name.lowercased() == lowercasedName }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Defaults
|
|
||||||
|
|
||||||
extension HTTPHeaders {
|
|
||||||
/// The default set of `HTTPHeaders` used by Alamofire. Includes `Accept-Encoding`, `Accept-Language`, and
|
|
||||||
/// `User-Agent`.
|
|
||||||
public static let `default`: HTTPHeaders = [.defaultAcceptEncoding,
|
|
||||||
.defaultAcceptLanguage,
|
|
||||||
.defaultUserAgent]
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTTPHeader {
|
|
||||||
/// Returns Alamofire's default `Accept-Encoding` header, appropriate for the encodings supported by particular OS
|
|
||||||
/// versions.
|
|
||||||
///
|
|
||||||
/// See the [Accept-Encoding HTTP header documentation](https://tools.ietf.org/html/rfc7230#section-4.2.3) .
|
|
||||||
public static let defaultAcceptEncoding: HTTPHeader = {
|
|
||||||
let encodings: [String]
|
|
||||||
if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) {
|
|
||||||
encodings = ["br", "gzip", "deflate"]
|
|
||||||
} else {
|
|
||||||
encodings = ["gzip", "deflate"]
|
|
||||||
}
|
|
||||||
|
|
||||||
return .acceptEncoding(encodings.qualityEncoded())
|
|
||||||
}()
|
|
||||||
|
|
||||||
/// Returns Alamofire's default `Accept-Language` header, generated by querying `Locale` for the user's
|
|
||||||
/// `preferredLanguages`.
|
|
||||||
///
|
|
||||||
/// See the [Accept-Language HTTP header documentation](https://tools.ietf.org/html/rfc7231#section-5.3.5).
|
|
||||||
public static let defaultAcceptLanguage: HTTPHeader = .acceptLanguage(Locale.preferredLanguages.prefix(6).qualityEncoded())
|
|
||||||
|
|
||||||
/// Returns Alamofire's default `User-Agent` header.
|
|
||||||
///
|
|
||||||
/// See the [User-Agent header documentation](https://tools.ietf.org/html/rfc7231#section-5.5.3).
|
|
||||||
///
|
|
||||||
/// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0`
|
|
||||||
public static let defaultUserAgent: HTTPHeader = {
|
|
||||||
let info = Bundle.main.infoDictionary
|
|
||||||
let executable = (info?["CFBundleExecutable"] as? String) ??
|
|
||||||
(ProcessInfo.processInfo.arguments.first?.split(separator: "/").last.map(String.init)) ??
|
|
||||||
"Unknown"
|
|
||||||
let bundle = info?["CFBundleIdentifier"] as? String ?? "Unknown"
|
|
||||||
let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown"
|
|
||||||
let appBuild = info?["CFBundleVersion"] as? String ?? "Unknown"
|
|
||||||
|
|
||||||
let osNameVersion: String = {
|
|
||||||
let version = ProcessInfo.processInfo.operatingSystemVersion
|
|
||||||
let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
|
|
||||||
let osName: String = {
|
|
||||||
#if os(iOS)
|
|
||||||
#if targetEnvironment(macCatalyst)
|
|
||||||
return "macOS(Catalyst)"
|
|
||||||
#else
|
|
||||||
return "iOS"
|
|
||||||
#endif
|
|
||||||
#elseif os(watchOS)
|
|
||||||
return "watchOS"
|
|
||||||
#elseif os(tvOS)
|
|
||||||
return "tvOS"
|
|
||||||
#elseif os(macOS)
|
|
||||||
return "macOS"
|
|
||||||
#elseif os(Linux)
|
|
||||||
return "Linux"
|
|
||||||
#elseif os(Windows)
|
|
||||||
return "Windows"
|
|
||||||
#elseif os(Android)
|
|
||||||
return "Android"
|
|
||||||
#else
|
|
||||||
return "Unknown"
|
|
||||||
#endif
|
|
||||||
}()
|
|
||||||
|
|
||||||
return "\(osName) \(versionString)"
|
|
||||||
}()
|
|
||||||
|
|
||||||
let alamofireVersion = "Alamofire/\(version)"
|
|
||||||
|
|
||||||
let userAgent = "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"
|
|
||||||
|
|
||||||
return .userAgent(userAgent)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Collection where Element == String {
|
|
||||||
func qualityEncoded() -> String {
|
|
||||||
enumerated().map { index, encoding in
|
|
||||||
let quality = 1.0 - (Double(index) * 0.1)
|
|
||||||
return "\(encoding);q=\(quality)"
|
|
||||||
}.joined(separator: ", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - System Type Extensions
|
|
||||||
|
|
||||||
extension URLRequest {
|
|
||||||
/// Returns `allHTTPHeaderFields` as `HTTPHeaders`.
|
|
||||||
public var headers: HTTPHeaders {
|
|
||||||
get { allHTTPHeaderFields.map(HTTPHeaders.init) ?? HTTPHeaders() }
|
|
||||||
set { allHTTPHeaderFields = newValue.dictionary }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HTTPURLResponse {
|
|
||||||
/// Returns `allHeaderFields` as `HTTPHeaders`.
|
|
||||||
public var headers: HTTPHeaders {
|
|
||||||
(allHeaderFields as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension URLSessionConfiguration {
|
|
||||||
/// Returns `httpAdditionalHeaders` as `HTTPHeaders`.
|
|
||||||
public var headers: HTTPHeaders {
|
|
||||||
get { (httpAdditionalHeaders as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() }
|
|
||||||
set { httpAdditionalHeaders = newValue.dictionary }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
56
SwiftProject/Pods/Alamofire/Source/HTTPMethod.swift
generated
56
SwiftProject/Pods/Alamofire/Source/HTTPMethod.swift
generated
@ -1,56 +0,0 @@
|
|||||||
//
|
|
||||||
// HTTPMethod.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
/// Type representing HTTP methods. Raw `String` value is stored and compared case-sensitively, so
|
|
||||||
/// `HTTPMethod.get != HTTPMethod(rawValue: "get")`.
|
|
||||||
///
|
|
||||||
/// See https://tools.ietf.org/html/rfc7231#section-4.3
|
|
||||||
public struct HTTPMethod: RawRepresentable, Equatable, Hashable {
|
|
||||||
/// `CONNECT` method.
|
|
||||||
public static let connect = HTTPMethod(rawValue: "CONNECT")
|
|
||||||
/// `DELETE` method.
|
|
||||||
public static let delete = HTTPMethod(rawValue: "DELETE")
|
|
||||||
/// `GET` method.
|
|
||||||
public static let get = HTTPMethod(rawValue: "GET")
|
|
||||||
/// `HEAD` method.
|
|
||||||
public static let head = HTTPMethod(rawValue: "HEAD")
|
|
||||||
/// `OPTIONS` method.
|
|
||||||
public static let options = HTTPMethod(rawValue: "OPTIONS")
|
|
||||||
/// `PATCH` method.
|
|
||||||
public static let patch = HTTPMethod(rawValue: "PATCH")
|
|
||||||
/// `POST` method.
|
|
||||||
public static let post = HTTPMethod(rawValue: "POST")
|
|
||||||
/// `PUT` method.
|
|
||||||
public static let put = HTTPMethod(rawValue: "PUT")
|
|
||||||
/// `QUERY` method.
|
|
||||||
public static let query = HTTPMethod(rawValue: "QUERY")
|
|
||||||
/// `TRACE` method.
|
|
||||||
public static let trace = HTTPMethod(rawValue: "TRACE")
|
|
||||||
|
|
||||||
public let rawValue: String
|
|
||||||
|
|
||||||
public init(rawValue: String) {
|
|
||||||
self.rawValue = rawValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,601 +0,0 @@
|
|||||||
//
|
|
||||||
// MultipartFormData.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
#if canImport(MobileCoreServices)
|
|
||||||
import MobileCoreServices
|
|
||||||
#elseif canImport(CoreServices)
|
|
||||||
import CoreServices
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode
|
|
||||||
/// multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead
|
|
||||||
/// to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the
|
|
||||||
/// data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for
|
|
||||||
/// larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset.
|
|
||||||
///
|
|
||||||
/// For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well
|
|
||||||
/// and the w3 form documentation.
|
|
||||||
///
|
|
||||||
/// - https://www.ietf.org/rfc/rfc2388.txt
|
|
||||||
/// - https://www.ietf.org/rfc/rfc2045.txt
|
|
||||||
/// - https://www.w3.org/TR/html401/interact/forms.html#h-17.13
|
|
||||||
open class MultipartFormData {
|
|
||||||
// MARK: - Helper Types
|
|
||||||
|
|
||||||
enum EncodingCharacters {
|
|
||||||
static let crlf = "\r\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BoundaryGenerator {
|
|
||||||
enum BoundaryType {
|
|
||||||
case initial, encapsulated, final
|
|
||||||
}
|
|
||||||
|
|
||||||
static func randomBoundary() -> String {
|
|
||||||
let first = UInt32.random(in: UInt32.min...UInt32.max)
|
|
||||||
let second = UInt32.random(in: UInt32.min...UInt32.max)
|
|
||||||
|
|
||||||
return String(format: "alamofire.boundary.%08x%08x", first, second)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data {
|
|
||||||
let boundaryText: String
|
|
||||||
|
|
||||||
switch boundaryType {
|
|
||||||
case .initial:
|
|
||||||
boundaryText = "--\(boundary)\(EncodingCharacters.crlf)"
|
|
||||||
case .encapsulated:
|
|
||||||
boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)"
|
|
||||||
case .final:
|
|
||||||
boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)"
|
|
||||||
}
|
|
||||||
|
|
||||||
return Data(boundaryText.utf8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BodyPart {
|
|
||||||
let headers: HTTPHeaders
|
|
||||||
let bodyStream: InputStream
|
|
||||||
let bodyContentLength: UInt64
|
|
||||||
var hasInitialBoundary = false
|
|
||||||
var hasFinalBoundary = false
|
|
||||||
|
|
||||||
init(headers: HTTPHeaders, bodyStream: InputStream, bodyContentLength: UInt64) {
|
|
||||||
self.headers = headers
|
|
||||||
self.bodyStream = bodyStream
|
|
||||||
self.bodyContentLength = bodyContentLength
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
/// Default memory threshold used when encoding `MultipartFormData`, in bytes.
|
|
||||||
public static let encodingMemoryThreshold: UInt64 = 10_000_000
|
|
||||||
|
|
||||||
/// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`.
|
|
||||||
open lazy var contentType: String = "multipart/form-data; boundary=\(self.boundary)"
|
|
||||||
|
|
||||||
/// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries.
|
|
||||||
public var contentLength: UInt64 { bodyParts.reduce(0) { $0 + $1.bodyContentLength } }
|
|
||||||
|
|
||||||
/// The boundary used to separate the body parts in the encoded form data.
|
|
||||||
public let boundary: String
|
|
||||||
|
|
||||||
let fileManager: FileManager
|
|
||||||
|
|
||||||
private var bodyParts: [BodyPart]
|
|
||||||
private var bodyPartError: AFError?
|
|
||||||
private let streamBufferSize: Int
|
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
|
||||||
|
|
||||||
/// Creates an instance.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - fileManager: `FileManager` to use for file operations, if needed.
|
|
||||||
/// - boundary: Boundary `String` used to separate body parts.
|
|
||||||
public init(fileManager: FileManager = .default, boundary: String? = nil) {
|
|
||||||
self.fileManager = fileManager
|
|
||||||
self.boundary = boundary ?? BoundaryGenerator.randomBoundary()
|
|
||||||
bodyParts = []
|
|
||||||
|
|
||||||
//
|
|
||||||
// The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more
|
|
||||||
// information, please refer to the following article:
|
|
||||||
// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html
|
|
||||||
//
|
|
||||||
streamBufferSize = 1024
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Body Parts
|
|
||||||
|
|
||||||
/// Creates a body part from the data and appends it to the instance.
|
|
||||||
///
|
|
||||||
/// The body part data will be encoded using the following format:
|
|
||||||
///
|
|
||||||
/// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
|
|
||||||
/// - `Content-Type: #{mimeType}` (HTTP Header)
|
|
||||||
/// - Encoded file data
|
|
||||||
/// - Multipart form boundary
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - data: `Data` to encoding into the instance.
|
|
||||||
/// - name: Name to associate with the `Data` in the `Content-Disposition` HTTP header.
|
|
||||||
/// - fileName: Filename to associate with the `Data` in the `Content-Disposition` HTTP header.
|
|
||||||
/// - mimeType: MIME type to associate with the data in the `Content-Type` HTTP header.
|
|
||||||
public func append(_ data: Data, withName name: String, fileName: String? = nil, mimeType: String? = nil) {
|
|
||||||
let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
|
|
||||||
let stream = InputStream(data: data)
|
|
||||||
let length = UInt64(data.count)
|
|
||||||
|
|
||||||
append(stream, withLength: length, headers: headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a body part from the file and appends it to the instance.
|
|
||||||
///
|
|
||||||
/// The body part data will be encoded using the following format:
|
|
||||||
///
|
|
||||||
/// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header)
|
|
||||||
/// - `Content-Type: #{generated mimeType}` (HTTP Header)
|
|
||||||
/// - Encoded file data
|
|
||||||
/// - Multipart form boundary
|
|
||||||
///
|
|
||||||
/// The filename in the `Content-Disposition` HTTP header is generated from the last path component of the
|
|
||||||
/// `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the
|
|
||||||
/// system associated MIME type.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - fileURL: `URL` of the file whose content will be encoded into the instance.
|
|
||||||
/// - name: Name to associate with the file content in the `Content-Disposition` HTTP header.
|
|
||||||
public func append(_ fileURL: URL, withName name: String) {
|
|
||||||
let fileName = fileURL.lastPathComponent
|
|
||||||
let pathExtension = fileURL.pathExtension
|
|
||||||
|
|
||||||
if !fileName.isEmpty && !pathExtension.isEmpty {
|
|
||||||
let mime = mimeType(forPathExtension: pathExtension)
|
|
||||||
append(fileURL, withName: name, fileName: fileName, mimeType: mime)
|
|
||||||
} else {
|
|
||||||
setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a body part from the file and appends it to the instance.
|
|
||||||
///
|
|
||||||
/// The body part data will be encoded using the following format:
|
|
||||||
///
|
|
||||||
/// - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header)
|
|
||||||
/// - Content-Type: #{mimeType} (HTTP Header)
|
|
||||||
/// - Encoded file data
|
|
||||||
/// - Multipart form boundary
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - fileURL: `URL` of the file whose content will be encoded into the instance.
|
|
||||||
/// - name: Name to associate with the file content in the `Content-Disposition` HTTP header.
|
|
||||||
/// - fileName: Filename to associate with the file content in the `Content-Disposition` HTTP header.
|
|
||||||
/// - mimeType: MIME type to associate with the file content in the `Content-Type` HTTP header.
|
|
||||||
public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) {
|
|
||||||
let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
|
|
||||||
|
|
||||||
//============================================================
|
|
||||||
// Check 1 - is file URL?
|
|
||||||
//============================================================
|
|
||||||
|
|
||||||
guard fileURL.isFileURL else {
|
|
||||||
setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================
|
|
||||||
// Check 2 - is file URL reachable?
|
|
||||||
//============================================================
|
|
||||||
|
|
||||||
#if !(os(Linux) || os(Windows) || os(Android))
|
|
||||||
do {
|
|
||||||
let isReachable = try fileURL.checkPromisedItemIsReachable()
|
|
||||||
guard isReachable else {
|
|
||||||
setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//============================================================
|
|
||||||
// Check 3 - is file URL a directory?
|
|
||||||
//============================================================
|
|
||||||
|
|
||||||
var isDirectory: ObjCBool = false
|
|
||||||
let path = fileURL.path
|
|
||||||
|
|
||||||
guard fileManager.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else {
|
|
||||||
setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================
|
|
||||||
// Check 4 - can the file size be extracted?
|
|
||||||
//============================================================
|
|
||||||
|
|
||||||
let bodyContentLength: UInt64
|
|
||||||
|
|
||||||
do {
|
|
||||||
guard let fileSize = try fileManager.attributesOfItem(atPath: path)[.size] as? NSNumber else {
|
|
||||||
setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bodyContentLength = fileSize.uint64Value
|
|
||||||
} catch {
|
|
||||||
setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================
|
|
||||||
// Check 5 - can a stream be created from file URL?
|
|
||||||
//============================================================
|
|
||||||
|
|
||||||
guard let stream = InputStream(url: fileURL) else {
|
|
||||||
setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
append(stream, withLength: bodyContentLength, headers: headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a body part from the stream and appends it to the instance.
|
|
||||||
///
|
|
||||||
/// The body part data will be encoded using the following format:
|
|
||||||
///
|
|
||||||
/// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
|
|
||||||
/// - `Content-Type: #{mimeType}` (HTTP Header)
|
|
||||||
/// - Encoded stream data
|
|
||||||
/// - Multipart form boundary
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - stream: `InputStream` to encode into the instance.
|
|
||||||
/// - length: Length, in bytes, of the stream.
|
|
||||||
/// - name: Name to associate with the stream content in the `Content-Disposition` HTTP header.
|
|
||||||
/// - fileName: Filename to associate with the stream content in the `Content-Disposition` HTTP header.
|
|
||||||
/// - mimeType: MIME type to associate with the stream content in the `Content-Type` HTTP header.
|
|
||||||
public func append(_ stream: InputStream,
|
|
||||||
withLength length: UInt64,
|
|
||||||
name: String,
|
|
||||||
fileName: String,
|
|
||||||
mimeType: String) {
|
|
||||||
let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
|
|
||||||
append(stream, withLength: length, headers: headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a body part with the stream, length, and headers and appends it to the instance.
|
|
||||||
///
|
|
||||||
/// The body part data will be encoded using the following format:
|
|
||||||
///
|
|
||||||
/// - HTTP headers
|
|
||||||
/// - Encoded stream data
|
|
||||||
/// - Multipart form boundary
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - stream: `InputStream` to encode into the instance.
|
|
||||||
/// - length: Length, in bytes, of the stream.
|
|
||||||
/// - headers: `HTTPHeaders` for the body part.
|
|
||||||
public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) {
|
|
||||||
let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
|
|
||||||
bodyParts.append(bodyPart)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Data Encoding
|
|
||||||
|
|
||||||
/// Encodes all appended body parts into a single `Data` value.
|
|
||||||
///
|
|
||||||
/// - Note: This method will load all the appended body parts into memory all at the same time. This method should
|
|
||||||
/// only be used when the encoded data will have a small memory footprint. For large data cases, please use
|
|
||||||
/// the `writeEncodedData(to:))` method.
|
|
||||||
///
|
|
||||||
/// - Returns: The encoded `Data`, if encoding is successful.
|
|
||||||
/// - Throws: An `AFError` if encoding encounters an error.
|
|
||||||
public func encode() throws -> Data {
|
|
||||||
if let bodyPartError = bodyPartError {
|
|
||||||
throw bodyPartError
|
|
||||||
}
|
|
||||||
|
|
||||||
var encoded = Data()
|
|
||||||
|
|
||||||
bodyParts.first?.hasInitialBoundary = true
|
|
||||||
bodyParts.last?.hasFinalBoundary = true
|
|
||||||
|
|
||||||
for bodyPart in bodyParts {
|
|
||||||
let encodedData = try encode(bodyPart)
|
|
||||||
encoded.append(encodedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
return encoded
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes all appended body parts to the given file `URL`.
|
|
||||||
///
|
|
||||||
/// This process is facilitated by reading and writing with input and output streams, respectively. Thus,
|
|
||||||
/// this approach is very memory efficient and should be used for large body part data.
|
|
||||||
///
|
|
||||||
/// - Parameter fileURL: File `URL` to which to write the form data.
|
|
||||||
/// - Throws: An `AFError` if encoding encounters an error.
|
|
||||||
public func writeEncodedData(to fileURL: URL) throws {
|
|
||||||
if let bodyPartError = bodyPartError {
|
|
||||||
throw bodyPartError
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileManager.fileExists(atPath: fileURL.path) {
|
|
||||||
throw AFError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL))
|
|
||||||
} else if !fileURL.isFileURL {
|
|
||||||
throw AFError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let outputStream = OutputStream(url: fileURL, append: false) else {
|
|
||||||
throw AFError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL))
|
|
||||||
}
|
|
||||||
|
|
||||||
outputStream.open()
|
|
||||||
defer { outputStream.close() }
|
|
||||||
|
|
||||||
bodyParts.first?.hasInitialBoundary = true
|
|
||||||
bodyParts.last?.hasFinalBoundary = true
|
|
||||||
|
|
||||||
for bodyPart in bodyParts {
|
|
||||||
try write(bodyPart, to: outputStream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private - Body Part Encoding
|
|
||||||
|
|
||||||
private func encode(_ bodyPart: BodyPart) throws -> Data {
|
|
||||||
var encoded = Data()
|
|
||||||
|
|
||||||
let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
|
|
||||||
encoded.append(initialData)
|
|
||||||
|
|
||||||
let headerData = encodeHeaders(for: bodyPart)
|
|
||||||
encoded.append(headerData)
|
|
||||||
|
|
||||||
let bodyStreamData = try encodeBodyStream(for: bodyPart)
|
|
||||||
encoded.append(bodyStreamData)
|
|
||||||
|
|
||||||
if bodyPart.hasFinalBoundary {
|
|
||||||
encoded.append(finalBoundaryData())
|
|
||||||
}
|
|
||||||
|
|
||||||
return encoded
|
|
||||||
}
|
|
||||||
|
|
||||||
private func encodeHeaders(for bodyPart: BodyPart) -> Data {
|
|
||||||
let headerText = bodyPart.headers.map { "\($0.name): \($0.value)\(EncodingCharacters.crlf)" }
|
|
||||||
.joined()
|
|
||||||
+ EncodingCharacters.crlf
|
|
||||||
|
|
||||||
return Data(headerText.utf8)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data {
|
|
||||||
let inputStream = bodyPart.bodyStream
|
|
||||||
inputStream.open()
|
|
||||||
defer { inputStream.close() }
|
|
||||||
|
|
||||||
var encoded = Data()
|
|
||||||
|
|
||||||
while inputStream.hasBytesAvailable {
|
|
||||||
var buffer = [UInt8](repeating: 0, count: streamBufferSize)
|
|
||||||
let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
|
|
||||||
|
|
||||||
if let error = inputStream.streamError {
|
|
||||||
throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error))
|
|
||||||
}
|
|
||||||
|
|
||||||
if bytesRead > 0 {
|
|
||||||
encoded.append(buffer, count: bytesRead)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard UInt64(encoded.count) == bodyPart.bodyContentLength else {
|
|
||||||
let error = AFError.UnexpectedInputStreamLength(bytesExpected: bodyPart.bodyContentLength,
|
|
||||||
bytesRead: UInt64(encoded.count))
|
|
||||||
throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error))
|
|
||||||
}
|
|
||||||
|
|
||||||
return encoded
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private - Writing Body Part to Output Stream
|
|
||||||
|
|
||||||
private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
|
||||||
try writeInitialBoundaryData(for: bodyPart, to: outputStream)
|
|
||||||
try writeHeaderData(for: bodyPart, to: outputStream)
|
|
||||||
try writeBodyStream(for: bodyPart, to: outputStream)
|
|
||||||
try writeFinalBoundaryData(for: bodyPart, to: outputStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
|
||||||
let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
|
|
||||||
return try write(initialData, to: outputStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
|
||||||
let headerData = encodeHeaders(for: bodyPart)
|
|
||||||
return try write(headerData, to: outputStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
|
||||||
let inputStream = bodyPart.bodyStream
|
|
||||||
|
|
||||||
inputStream.open()
|
|
||||||
defer { inputStream.close() }
|
|
||||||
|
|
||||||
var bytesLeftToRead = bodyPart.bodyContentLength
|
|
||||||
while inputStream.hasBytesAvailable && bytesLeftToRead > 0 {
|
|
||||||
let bufferSize = min(streamBufferSize, Int(bytesLeftToRead))
|
|
||||||
var buffer = [UInt8](repeating: 0, count: bufferSize)
|
|
||||||
let bytesRead = inputStream.read(&buffer, maxLength: bufferSize)
|
|
||||||
|
|
||||||
if let streamError = inputStream.streamError {
|
|
||||||
throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError))
|
|
||||||
}
|
|
||||||
|
|
||||||
if bytesRead > 0 {
|
|
||||||
if buffer.count != bytesRead {
|
|
||||||
buffer = Array(buffer[0..<bytesRead])
|
|
||||||
}
|
|
||||||
|
|
||||||
try write(&buffer, to: outputStream)
|
|
||||||
bytesLeftToRead -= UInt64(bytesRead)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func writeFinalBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
|
||||||
if bodyPart.hasFinalBoundary {
|
|
||||||
return try write(finalBoundaryData(), to: outputStream)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private - Writing Buffered Data to Output Stream
|
|
||||||
|
|
||||||
private func write(_ data: Data, to outputStream: OutputStream) throws {
|
|
||||||
var buffer = [UInt8](repeating: 0, count: data.count)
|
|
||||||
data.copyBytes(to: &buffer, count: data.count)
|
|
||||||
|
|
||||||
return try write(&buffer, to: outputStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func write(_ buffer: inout [UInt8], to outputStream: OutputStream) throws {
|
|
||||||
var bytesToWrite = buffer.count
|
|
||||||
|
|
||||||
while bytesToWrite > 0, outputStream.hasSpaceAvailable {
|
|
||||||
let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite)
|
|
||||||
|
|
||||||
if let error = outputStream.streamError {
|
|
||||||
throw AFError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error))
|
|
||||||
}
|
|
||||||
|
|
||||||
bytesToWrite -= bytesWritten
|
|
||||||
|
|
||||||
if bytesToWrite > 0 {
|
|
||||||
buffer = Array(buffer[bytesWritten..<buffer.count])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private - Content Headers
|
|
||||||
|
|
||||||
private func contentHeaders(withName name: String, fileName: String? = nil, mimeType: String? = nil) -> HTTPHeaders {
|
|
||||||
var disposition = "form-data; name=\"\(name)\""
|
|
||||||
if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" }
|
|
||||||
|
|
||||||
var headers: HTTPHeaders = [.contentDisposition(disposition)]
|
|
||||||
if let mimeType = mimeType { headers.add(.contentType(mimeType)) }
|
|
||||||
|
|
||||||
return headers
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private - Boundary Encoding
|
|
||||||
|
|
||||||
private func initialBoundaryData() -> Data {
|
|
||||||
BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func encapsulatedBoundaryData() -> Data {
|
|
||||||
BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func finalBoundaryData() -> Data {
|
|
||||||
BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private - Errors
|
|
||||||
|
|
||||||
private func setBodyPartError(withReason reason: AFError.MultipartEncodingFailureReason) {
|
|
||||||
guard bodyPartError == nil else { return }
|
|
||||||
bodyPartError = AFError.multipartEncodingFailed(reason: reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(UniformTypeIdentifiers)
|
|
||||||
import UniformTypeIdentifiers
|
|
||||||
|
|
||||||
extension MultipartFormData {
|
|
||||||
// MARK: - Private - Mime Type
|
|
||||||
|
|
||||||
private func mimeType(forPathExtension pathExtension: String) -> String {
|
|
||||||
#if swift(>=5.9)
|
|
||||||
if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, visionOS 1, *) {
|
|
||||||
return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream"
|
|
||||||
} else {
|
|
||||||
if
|
|
||||||
let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
|
|
||||||
let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() {
|
|
||||||
return contentType as String
|
|
||||||
}
|
|
||||||
|
|
||||||
return "application/octet-stream"
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) {
|
|
||||||
return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream"
|
|
||||||
} else {
|
|
||||||
if
|
|
||||||
let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
|
|
||||||
let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() {
|
|
||||||
return contentType as String
|
|
||||||
}
|
|
||||||
|
|
||||||
return "application/octet-stream"
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
extension MultipartFormData {
|
|
||||||
// MARK: - Private - Mime Type
|
|
||||||
|
|
||||||
private func mimeType(forPathExtension pathExtension: String) -> String {
|
|
||||||
#if canImport(CoreServices) || canImport(MobileCoreServices)
|
|
||||||
if
|
|
||||||
let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
|
|
||||||
let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() {
|
|
||||||
return contentType as String
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return "application/octet-stream"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
//
|
|
||||||
// MultipartUpload.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Internal type which encapsulates a `MultipartFormData` upload.
|
|
||||||
final class MultipartUpload {
|
|
||||||
lazy var result = Result { try build() }
|
|
||||||
|
|
||||||
private let multipartFormData: Protected<MultipartFormData>
|
|
||||||
|
|
||||||
let encodingMemoryThreshold: UInt64
|
|
||||||
let request: URLRequestConvertible
|
|
||||||
let fileManager: FileManager
|
|
||||||
|
|
||||||
init(encodingMemoryThreshold: UInt64,
|
|
||||||
request: URLRequestConvertible,
|
|
||||||
multipartFormData: MultipartFormData) {
|
|
||||||
self.encodingMemoryThreshold = encodingMemoryThreshold
|
|
||||||
self.request = request
|
|
||||||
fileManager = multipartFormData.fileManager
|
|
||||||
self.multipartFormData = Protected(multipartFormData)
|
|
||||||
}
|
|
||||||
|
|
||||||
func build() throws -> UploadRequest.Uploadable {
|
|
||||||
let uploadable: UploadRequest.Uploadable
|
|
||||||
if multipartFormData.contentLength < encodingMemoryThreshold {
|
|
||||||
let data = try multipartFormData.read { try $0.encode() }
|
|
||||||
|
|
||||||
uploadable = .data(data)
|
|
||||||
} else {
|
|
||||||
let tempDirectoryURL = fileManager.temporaryDirectory
|
|
||||||
let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data")
|
|
||||||
let fileName = UUID().uuidString
|
|
||||||
let fileURL = directoryURL.appendingPathComponent(fileName)
|
|
||||||
|
|
||||||
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
|
|
||||||
|
|
||||||
do {
|
|
||||||
try multipartFormData.read { try $0.writeEncodedData(to: fileURL) }
|
|
||||||
} catch {
|
|
||||||
// Cleanup after attempted write if it fails.
|
|
||||||
try? fileManager.removeItem(at: fileURL)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadable = .file(fileURL, shouldRemove: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return uploadable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension MultipartUpload: UploadConvertible {
|
|
||||||
func asURLRequest() throws -> URLRequest {
|
|
||||||
var urlRequest = try request.asURLRequest()
|
|
||||||
|
|
||||||
multipartFormData.read { multipartFormData in
|
|
||||||
urlRequest.headers.add(.contentType(multipartFormData.contentType))
|
|
||||||
}
|
|
||||||
|
|
||||||
return urlRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
func createUploadable() throws -> UploadRequest.Uploadable {
|
|
||||||
try result.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,292 +0,0 @@
|
|||||||
//
|
|
||||||
// NetworkReachabilityManager.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
#if canImport(SystemConfiguration)
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import SystemConfiguration
|
|
||||||
|
|
||||||
/// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both cellular and
|
|
||||||
/// WiFi network interfaces.
|
|
||||||
///
|
|
||||||
/// Reachability can be used to determine background information about why a network operation failed, or to retry
|
|
||||||
/// network requests when a connection is established. It should not be used to prevent a user from initiating a network
|
|
||||||
/// request, as it's possible that an initial request may be required to establish reachability.
|
|
||||||
open class NetworkReachabilityManager {
|
|
||||||
/// Defines the various states of network reachability.
|
|
||||||
public enum NetworkReachabilityStatus {
|
|
||||||
/// It is unknown whether the network is reachable.
|
|
||||||
case unknown
|
|
||||||
/// The network is not reachable.
|
|
||||||
case notReachable
|
|
||||||
/// The network is reachable on the associated `ConnectionType`.
|
|
||||||
case reachable(ConnectionType)
|
|
||||||
|
|
||||||
init(_ flags: SCNetworkReachabilityFlags) {
|
|
||||||
guard flags.isActuallyReachable else { self = .notReachable; return }
|
|
||||||
|
|
||||||
var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi)
|
|
||||||
|
|
||||||
if flags.isCellular { networkStatus = .reachable(.cellular) }
|
|
||||||
|
|
||||||
self = networkStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Defines the various connection types detected by reachability flags.
|
|
||||||
public enum ConnectionType {
|
|
||||||
/// The connection type is either over Ethernet or WiFi.
|
|
||||||
case ethernetOrWiFi
|
|
||||||
/// The connection type is a cellular connection.
|
|
||||||
case cellular
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A closure executed when the network reachability status changes. The closure takes a single argument: the
|
|
||||||
/// network reachability status.
|
|
||||||
public typealias Listener = (NetworkReachabilityStatus) -> Void
|
|
||||||
|
|
||||||
/// Default `NetworkReachabilityManager` for the zero address and a `listenerQueue` of `.main`.
|
|
||||||
public static let `default` = NetworkReachabilityManager()
|
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
/// Whether the network is currently reachable.
|
|
||||||
open var isReachable: Bool { isReachableOnCellular || isReachableOnEthernetOrWiFi }
|
|
||||||
|
|
||||||
/// Whether the network is currently reachable over the cellular interface.
|
|
||||||
///
|
|
||||||
/// - Note: Using this property to decide whether to make a high or low bandwidth request is not recommended.
|
|
||||||
/// Instead, set the `allowsCellularAccess` on any `URLRequest`s being issued.
|
|
||||||
///
|
|
||||||
open var isReachableOnCellular: Bool { status == .reachable(.cellular) }
|
|
||||||
|
|
||||||
/// Whether the network is currently reachable over Ethernet or WiFi interface.
|
|
||||||
open var isReachableOnEthernetOrWiFi: Bool { status == .reachable(.ethernetOrWiFi) }
|
|
||||||
|
|
||||||
/// `DispatchQueue` on which reachability will update.
|
|
||||||
public let reachabilityQueue = DispatchQueue(label: "org.alamofire.reachabilityQueue")
|
|
||||||
|
|
||||||
/// Flags of the current reachability type, if any.
|
|
||||||
open var flags: SCNetworkReachabilityFlags? {
|
|
||||||
var flags = SCNetworkReachabilityFlags()
|
|
||||||
|
|
||||||
return SCNetworkReachabilityGetFlags(reachability, &flags) ? flags : nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The current network reachability status.
|
|
||||||
open var status: NetworkReachabilityStatus {
|
|
||||||
flags.map(NetworkReachabilityStatus.init) ?? .unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutable state storage.
|
|
||||||
struct MutableState {
|
|
||||||
/// A closure executed when the network reachability status changes.
|
|
||||||
var listener: Listener?
|
|
||||||
/// `DispatchQueue` on which listeners will be called.
|
|
||||||
var listenerQueue: DispatchQueue?
|
|
||||||
/// Previously calculated status.
|
|
||||||
var previousStatus: NetworkReachabilityStatus?
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `SCNetworkReachability` instance providing notifications.
|
|
||||||
private let reachability: SCNetworkReachability
|
|
||||||
|
|
||||||
/// Protected storage for mutable state.
|
|
||||||
private let mutableState = Protected(MutableState())
|
|
||||||
|
|
||||||
// MARK: - Initialization
|
|
||||||
|
|
||||||
/// Creates an instance with the specified host.
|
|
||||||
///
|
|
||||||
/// - Note: The `host` value must *not* contain a scheme, just the hostname.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - host: Host used to evaluate network reachability. Must *not* include the scheme (e.g. `https`).
|
|
||||||
public convenience init?(host: String) {
|
|
||||||
guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil }
|
|
||||||
|
|
||||||
self.init(reachability: reachability)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an instance that monitors the address 0.0.0.0.
|
|
||||||
///
|
|
||||||
/// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing
|
|
||||||
/// status of the device, both IPv4 and IPv6.
|
|
||||||
public convenience init?() {
|
|
||||||
var zero = sockaddr()
|
|
||||||
zero.sa_len = UInt8(MemoryLayout<sockaddr>.size)
|
|
||||||
zero.sa_family = sa_family_t(AF_INET)
|
|
||||||
|
|
||||||
guard let reachability = SCNetworkReachabilityCreateWithAddress(nil, &zero) else { return nil }
|
|
||||||
|
|
||||||
self.init(reachability: reachability)
|
|
||||||
}
|
|
||||||
|
|
||||||
private init(reachability: SCNetworkReachability) {
|
|
||||||
self.reachability = reachability
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
stopListening()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Listening
|
|
||||||
|
|
||||||
/// Starts listening for changes in network reachability status.
|
|
||||||
///
|
|
||||||
/// - Note: Stops and removes any existing listener.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - queue: `DispatchQueue` on which to call the `listener` closure. `.main` by default.
|
|
||||||
/// - listener: `Listener` closure called when reachability changes.
|
|
||||||
///
|
|
||||||
/// - Returns: `true` if listening was started successfully, `false` otherwise.
|
|
||||||
@discardableResult
|
|
||||||
open func startListening(onQueue queue: DispatchQueue = .main,
|
|
||||||
onUpdatePerforming listener: @escaping Listener) -> Bool {
|
|
||||||
stopListening()
|
|
||||||
|
|
||||||
mutableState.write { state in
|
|
||||||
state.listenerQueue = queue
|
|
||||||
state.listener = listener
|
|
||||||
}
|
|
||||||
|
|
||||||
let weakManager = WeakManager(manager: self)
|
|
||||||
|
|
||||||
var context = SCNetworkReachabilityContext(
|
|
||||||
version: 0,
|
|
||||||
info: Unmanaged.passUnretained(weakManager).toOpaque(),
|
|
||||||
retain: { info in
|
|
||||||
let unmanaged = Unmanaged<WeakManager>.fromOpaque(info)
|
|
||||||
_ = unmanaged.retain()
|
|
||||||
|
|
||||||
return UnsafeRawPointer(unmanaged.toOpaque())
|
|
||||||
},
|
|
||||||
release: { info in
|
|
||||||
let unmanaged = Unmanaged<WeakManager>.fromOpaque(info)
|
|
||||||
unmanaged.release()
|
|
||||||
},
|
|
||||||
copyDescription: { info in
|
|
||||||
let unmanaged = Unmanaged<WeakManager>.fromOpaque(info)
|
|
||||||
let weakManager = unmanaged.takeUnretainedValue()
|
|
||||||
let description = weakManager.manager?.flags?.readableDescription ?? "nil"
|
|
||||||
|
|
||||||
return Unmanaged.passRetained(description as CFString)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
let callback: SCNetworkReachabilityCallBack = { _, flags, info in
|
|
||||||
guard let info = info else { return }
|
|
||||||
|
|
||||||
let weakManager = Unmanaged<WeakManager>.fromOpaque(info).takeUnretainedValue()
|
|
||||||
weakManager.manager?.notifyListener(flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
let queueAdded = SCNetworkReachabilitySetDispatchQueue(reachability, reachabilityQueue)
|
|
||||||
let callbackAdded = SCNetworkReachabilitySetCallback(reachability, callback, &context)
|
|
||||||
|
|
||||||
// Manually call listener to give initial state, since the framework may not.
|
|
||||||
if let currentFlags = flags {
|
|
||||||
reachabilityQueue.async {
|
|
||||||
self.notifyListener(currentFlags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return callbackAdded && queueAdded
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stops listening for changes in network reachability status.
|
|
||||||
open func stopListening() {
|
|
||||||
SCNetworkReachabilitySetCallback(reachability, nil, nil)
|
|
||||||
SCNetworkReachabilitySetDispatchQueue(reachability, nil)
|
|
||||||
mutableState.write { state in
|
|
||||||
state.listener = nil
|
|
||||||
state.listenerQueue = nil
|
|
||||||
state.previousStatus = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Internal - Listener Notification
|
|
||||||
|
|
||||||
/// Calls the `listener` closure of the `listenerQueue` if the computed status hasn't changed.
|
|
||||||
///
|
|
||||||
/// - Note: Should only be called from the `reachabilityQueue`.
|
|
||||||
///
|
|
||||||
/// - Parameter flags: `SCNetworkReachabilityFlags` to use to calculate the status.
|
|
||||||
func notifyListener(_ flags: SCNetworkReachabilityFlags) {
|
|
||||||
let newStatus = NetworkReachabilityStatus(flags)
|
|
||||||
|
|
||||||
mutableState.write { state in
|
|
||||||
guard state.previousStatus != newStatus else { return }
|
|
||||||
|
|
||||||
state.previousStatus = newStatus
|
|
||||||
|
|
||||||
let listener = state.listener
|
|
||||||
state.listenerQueue?.async { listener?(newStatus) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class WeakManager {
|
|
||||||
weak var manager: NetworkReachabilityManager?
|
|
||||||
|
|
||||||
init(manager: NetworkReachabilityManager?) {
|
|
||||||
self.manager = manager
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {}
|
|
||||||
|
|
||||||
extension SCNetworkReachabilityFlags {
|
|
||||||
var isReachable: Bool { contains(.reachable) }
|
|
||||||
var isConnectionRequired: Bool { contains(.connectionRequired) }
|
|
||||||
var canConnectAutomatically: Bool { contains(.connectionOnDemand) || contains(.connectionOnTraffic) }
|
|
||||||
var canConnectWithoutUserInteraction: Bool { canConnectAutomatically && !contains(.interventionRequired) }
|
|
||||||
var isActuallyReachable: Bool { isReachable && (!isConnectionRequired || canConnectWithoutUserInteraction) }
|
|
||||||
var isCellular: Bool {
|
|
||||||
#if os(iOS) || os(tvOS) || (swift(>=5.9) && os(visionOS))
|
|
||||||
return contains(.isWWAN)
|
|
||||||
#else
|
|
||||||
return false
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Human readable `String` for all states, to help with debugging.
|
|
||||||
var readableDescription: String {
|
|
||||||
let W = isCellular ? "W" : "-"
|
|
||||||
let R = isReachable ? "R" : "-"
|
|
||||||
let c = isConnectionRequired ? "c" : "-"
|
|
||||||
let t = contains(.transientConnection) ? "t" : "-"
|
|
||||||
let i = contains(.interventionRequired) ? "i" : "-"
|
|
||||||
let C = contains(.connectionOnTraffic) ? "C" : "-"
|
|
||||||
let D = contains(.connectionOnDemand) ? "D" : "-"
|
|
||||||
let l = contains(.isLocalAddress) ? "l" : "-"
|
|
||||||
let d = contains(.isDirect) ? "d" : "-"
|
|
||||||
let a = contains(.connectionAutomatic) ? "a" : "-"
|
|
||||||
|
|
||||||
return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)\(a)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
115
SwiftProject/Pods/Alamofire/Source/Notifications.swift
generated
115
SwiftProject/Pods/Alamofire/Source/Notifications.swift
generated
@ -1,115 +0,0 @@
|
|||||||
//
|
|
||||||
// Notifications.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension Request {
|
|
||||||
/// Posted when a `Request` is resumed. The `Notification` contains the resumed `Request`.
|
|
||||||
public static let didResumeNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResume")
|
|
||||||
/// Posted when a `Request` is suspended. The `Notification` contains the suspended `Request`.
|
|
||||||
public static let didSuspendNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspend")
|
|
||||||
/// Posted when a `Request` is cancelled. The `Notification` contains the cancelled `Request`.
|
|
||||||
public static let didCancelNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancel")
|
|
||||||
/// Posted when a `Request` is finished. The `Notification` contains the completed `Request`.
|
|
||||||
public static let didFinishNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didFinish")
|
|
||||||
|
|
||||||
/// Posted when a `URLSessionTask` is resumed. The `Notification` contains the `Request` associated with the `URLSessionTask`.
|
|
||||||
public static let didResumeTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didResumeTask")
|
|
||||||
/// Posted when a `URLSessionTask` is suspended. The `Notification` contains the `Request` associated with the `URLSessionTask`.
|
|
||||||
public static let didSuspendTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didSuspendTask")
|
|
||||||
/// Posted when a `URLSessionTask` is cancelled. The `Notification` contains the `Request` associated with the `URLSessionTask`.
|
|
||||||
public static let didCancelTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCancelTask")
|
|
||||||
/// Posted when a `URLSessionTask` is completed. The `Notification` contains the `Request` associated with the `URLSessionTask`.
|
|
||||||
public static let didCompleteTaskNotification = Notification.Name(rawValue: "org.alamofire.notification.name.request.didCompleteTask")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension Notification {
|
|
||||||
/// The `Request` contained by the instance's `userInfo`, `nil` otherwise.
|
|
||||||
public var request: Request? {
|
|
||||||
userInfo?[String.requestKey] as? Request
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience initializer for a `Notification` containing a `Request` payload.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - name: The name of the notification.
|
|
||||||
/// - request: The `Request` payload.
|
|
||||||
init(name: Notification.Name, request: Request) {
|
|
||||||
self.init(name: name, object: nil, userInfo: [String.requestKey: request])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NotificationCenter {
|
|
||||||
/// Convenience function for posting notifications with `Request` payloads.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - name: The name of the notification.
|
|
||||||
/// - request: The `Request` payload.
|
|
||||||
func postNotification(named name: Notification.Name, with request: Request) {
|
|
||||||
let notification = Notification(name: name, request: request)
|
|
||||||
post(notification)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
/// User info dictionary key representing the `Request` associated with the notification.
|
|
||||||
fileprivate static let requestKey = "org.alamofire.notification.key.request"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `EventMonitor` that provides Alamofire's notifications.
|
|
||||||
public final class AlamofireNotifications: EventMonitor {
|
|
||||||
public func requestDidResume(_ request: Request) {
|
|
||||||
NotificationCenter.default.postNotification(named: Request.didResumeNotification, with: request)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func requestDidSuspend(_ request: Request) {
|
|
||||||
NotificationCenter.default.postNotification(named: Request.didSuspendNotification, with: request)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func requestDidCancel(_ request: Request) {
|
|
||||||
NotificationCenter.default.postNotification(named: Request.didCancelNotification, with: request)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func requestDidFinish(_ request: Request) {
|
|
||||||
NotificationCenter.default.postNotification(named: Request.didFinishNotification, with: request)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didResumeTask task: URLSessionTask) {
|
|
||||||
NotificationCenter.default.postNotification(named: Request.didResumeTaskNotification, with: request)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didSuspendTask task: URLSessionTask) {
|
|
||||||
NotificationCenter.default.postNotification(named: Request.didSuspendTaskNotification, with: request)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didCancelTask task: URLSessionTask) {
|
|
||||||
NotificationCenter.default.postNotification(named: Request.didCancelTaskNotification, with: request)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func request(_ request: Request, didCompleteTask task: URLSessionTask, with error: AFError?) {
|
|
||||||
NotificationCenter.default.postNotification(named: Request.didCompleteTaskNotification, with: request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// OperationQueue+Alamofire.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension OperationQueue {
|
|
||||||
/// Creates an instance using the provided parameters.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - qualityOfService: `QualityOfService` to be applied to the queue. `.default` by default.
|
|
||||||
/// - maxConcurrentOperationCount: Maximum concurrent operations.
|
|
||||||
/// `OperationQueue.defaultMaxConcurrentOperationCount` by default.
|
|
||||||
/// - underlyingQueue: Underlying `DispatchQueue`. `nil` by default.
|
|
||||||
/// - name: Name for the queue. `nil` by default.
|
|
||||||
/// - startSuspended: Whether the queue starts suspended. `false` by default.
|
|
||||||
convenience init(qualityOfService: QualityOfService = .default,
|
|
||||||
maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount,
|
|
||||||
underlyingQueue: DispatchQueue? = nil,
|
|
||||||
name: String? = nil,
|
|
||||||
startSuspended: Bool = false) {
|
|
||||||
self.init()
|
|
||||||
self.qualityOfService = qualityOfService
|
|
||||||
self.maxConcurrentOperationCount = maxConcurrentOperationCount
|
|
||||||
self.underlyingQueue = underlyingQueue
|
|
||||||
self.name = name
|
|
||||||
isSuspended = startSuspended
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,213 +0,0 @@
|
|||||||
//
|
|
||||||
// ParameterEncoder.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// A type that can encode any `Encodable` type into a `URLRequest`.
|
|
||||||
public protocol ParameterEncoder {
|
|
||||||
/// Encode the provided `Encodable` parameters into `request`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - parameters: The `Encodable` parameter value.
|
|
||||||
/// - request: The `URLRequest` into which to encode the parameters.
|
|
||||||
///
|
|
||||||
/// - Returns: A `URLRequest` with the result of the encoding.
|
|
||||||
/// - Throws: An `Error` when encoding fails. For Alamofire provided encoders, this will be an instance of
|
|
||||||
/// `AFError.parameterEncoderFailed` with an associated `ParameterEncoderFailureReason`.
|
|
||||||
func encode<Parameters: Encodable>(_ parameters: Parameters?, into request: URLRequest) throws -> URLRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `ParameterEncoder` that encodes types as JSON body data.
|
|
||||||
///
|
|
||||||
/// If no `Content-Type` header is already set on the provided `URLRequest`s, it's set to `application/json`.
|
|
||||||
open class JSONParameterEncoder: ParameterEncoder {
|
|
||||||
/// Returns an encoder with default parameters.
|
|
||||||
public static var `default`: JSONParameterEncoder { JSONParameterEncoder() }
|
|
||||||
|
|
||||||
/// Returns an encoder with `JSONEncoder.outputFormatting` set to `.prettyPrinted`.
|
|
||||||
public static var prettyPrinted: JSONParameterEncoder {
|
|
||||||
let encoder = JSONEncoder()
|
|
||||||
encoder.outputFormatting = .prettyPrinted
|
|
||||||
|
|
||||||
return JSONParameterEncoder(encoder: encoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an encoder with `JSONEncoder.outputFormatting` set to `.sortedKeys`.
|
|
||||||
@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
|
|
||||||
public static var sortedKeys: JSONParameterEncoder {
|
|
||||||
let encoder = JSONEncoder()
|
|
||||||
encoder.outputFormatting = .sortedKeys
|
|
||||||
|
|
||||||
return JSONParameterEncoder(encoder: encoder)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `JSONEncoder` used to encode parameters.
|
|
||||||
public let encoder: JSONEncoder
|
|
||||||
|
|
||||||
/// Creates an instance with the provided `JSONEncoder`.
|
|
||||||
///
|
|
||||||
/// - Parameter encoder: The `JSONEncoder`. `JSONEncoder()` by default.
|
|
||||||
public init(encoder: JSONEncoder = JSONEncoder()) {
|
|
||||||
self.encoder = encoder
|
|
||||||
}
|
|
||||||
|
|
||||||
open func encode<Parameters: Encodable>(_ parameters: Parameters?,
|
|
||||||
into request: URLRequest) throws -> URLRequest {
|
|
||||||
guard let parameters = parameters else { return request }
|
|
||||||
|
|
||||||
var request = request
|
|
||||||
|
|
||||||
do {
|
|
||||||
let data = try encoder.encode(parameters)
|
|
||||||
request.httpBody = data
|
|
||||||
if request.headers["Content-Type"] == nil {
|
|
||||||
request.headers.update(.contentType("application/json"))
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
|
|
||||||
}
|
|
||||||
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ParameterEncoder where Self == JSONParameterEncoder {
|
|
||||||
/// Provides a default `JSONParameterEncoder` instance.
|
|
||||||
public static var json: JSONParameterEncoder { JSONParameterEncoder() }
|
|
||||||
|
|
||||||
/// Creates a `JSONParameterEncoder` using the provided `JSONEncoder`.
|
|
||||||
///
|
|
||||||
/// - Parameter encoder: `JSONEncoder` used to encode parameters. `JSONEncoder()` by default.
|
|
||||||
/// - Returns: The `JSONParameterEncoder`.
|
|
||||||
public static func json(encoder: JSONEncoder = JSONEncoder()) -> JSONParameterEncoder {
|
|
||||||
JSONParameterEncoder(encoder: encoder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending
|
|
||||||
/// on the `Destination` set.
|
|
||||||
///
|
|
||||||
/// If no `Content-Type` header is already set on the provided `URLRequest`s, it will be set to
|
|
||||||
/// `application/x-www-form-urlencoded; charset=utf-8`.
|
|
||||||
///
|
|
||||||
/// Encoding behavior can be customized by passing an instance of `URLEncodedFormEncoder` to the initializer.
|
|
||||||
open class URLEncodedFormParameterEncoder: ParameterEncoder {
|
|
||||||
/// Defines where the URL-encoded string should be set for each `URLRequest`.
|
|
||||||
public enum Destination {
|
|
||||||
/// Applies the encoded query string to any existing query string for `.get`, `.head`, and `.delete` request.
|
|
||||||
/// Sets it to the `httpBody` for all other methods.
|
|
||||||
case methodDependent
|
|
||||||
/// Applies the encoded query string to any existing query string from the `URLRequest`.
|
|
||||||
case queryString
|
|
||||||
/// Applies the encoded query string to the `httpBody` of the `URLRequest`.
|
|
||||||
case httpBody
|
|
||||||
|
|
||||||
/// Determines whether the URL-encoded string should be applied to the `URLRequest`'s `url`.
|
|
||||||
///
|
|
||||||
/// - Parameter method: The `HTTPMethod`.
|
|
||||||
///
|
|
||||||
/// - Returns: Whether the URL-encoded string should be applied to a `URL`.
|
|
||||||
func encodesParametersInURL(for method: HTTPMethod) -> Bool {
|
|
||||||
switch self {
|
|
||||||
case .methodDependent: return [.get, .head, .delete].contains(method)
|
|
||||||
case .queryString: return true
|
|
||||||
case .httpBody: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an encoder with default parameters.
|
|
||||||
public static var `default`: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() }
|
|
||||||
|
|
||||||
/// The `URLEncodedFormEncoder` to use.
|
|
||||||
public let encoder: URLEncodedFormEncoder
|
|
||||||
|
|
||||||
/// The `Destination` for the URL-encoded string.
|
|
||||||
public let destination: Destination
|
|
||||||
|
|
||||||
/// Creates an instance with the provided `URLEncodedFormEncoder` instance and `Destination` value.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - encoder: The `URLEncodedFormEncoder`. `URLEncodedFormEncoder()` by default.
|
|
||||||
/// - destination: The `Destination`. `.methodDependent` by default.
|
|
||||||
public init(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), destination: Destination = .methodDependent) {
|
|
||||||
self.encoder = encoder
|
|
||||||
self.destination = destination
|
|
||||||
}
|
|
||||||
|
|
||||||
open func encode<Parameters: Encodable>(_ parameters: Parameters?,
|
|
||||||
into request: URLRequest) throws -> URLRequest {
|
|
||||||
guard let parameters = parameters else { return request }
|
|
||||||
|
|
||||||
var request = request
|
|
||||||
|
|
||||||
guard let url = request.url else {
|
|
||||||
throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let method = request.method else {
|
|
||||||
let rawValue = request.method?.rawValue ?? "nil"
|
|
||||||
throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.httpMethod(rawValue: rawValue)))
|
|
||||||
}
|
|
||||||
|
|
||||||
if destination.encodesParametersInURL(for: method),
|
|
||||||
var components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
|
|
||||||
let query: String = try Result<String, Error> { try encoder.encode(parameters) }
|
|
||||||
.mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get()
|
|
||||||
let newQueryString = [components.percentEncodedQuery, query].compactMap { $0 }.joinedWithAmpersands()
|
|
||||||
components.percentEncodedQuery = newQueryString.isEmpty ? nil : newQueryString
|
|
||||||
|
|
||||||
guard let newURL = components.url else {
|
|
||||||
throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url))
|
|
||||||
}
|
|
||||||
|
|
||||||
request.url = newURL
|
|
||||||
} else {
|
|
||||||
if request.headers["Content-Type"] == nil {
|
|
||||||
request.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8"))
|
|
||||||
}
|
|
||||||
|
|
||||||
request.httpBody = try Result<Data, Error> { try encoder.encode(parameters) }
|
|
||||||
.mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ParameterEncoder where Self == URLEncodedFormParameterEncoder {
|
|
||||||
/// Provides a default `URLEncodedFormParameterEncoder` instance.
|
|
||||||
public static var urlEncodedForm: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() }
|
|
||||||
|
|
||||||
/// Creates a `URLEncodedFormParameterEncoder` with the provided encoder and destination.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - encoder: `URLEncodedFormEncoder` used to encode the parameters. `URLEncodedFormEncoder()` by default.
|
|
||||||
/// - destination: `Destination` to which to encode the parameters. `.methodDependent` by default.
|
|
||||||
/// - Returns: The `URLEncodedFormParameterEncoder`.
|
|
||||||
public static func urlEncodedForm(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(),
|
|
||||||
destination: URLEncodedFormParameterEncoder.Destination = .methodDependent) -> URLEncodedFormParameterEncoder {
|
|
||||||
URLEncodedFormParameterEncoder(encoder: encoder, destination: destination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,346 +0,0 @@
|
|||||||
//
|
|
||||||
// ParameterEncoding.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// A dictionary of parameters to apply to a `URLRequest`.
|
|
||||||
public typealias Parameters = [String: Any]
|
|
||||||
|
|
||||||
/// A type used to define how a set of parameters are applied to a `URLRequest`.
|
|
||||||
public protocol ParameterEncoding {
|
|
||||||
/// Creates a `URLRequest` by encoding parameters and applying them on the passed request.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - urlRequest: `URLRequestConvertible` value onto which parameters will be encoded.
|
|
||||||
/// - parameters: `Parameters` to encode onto the request.
|
|
||||||
///
|
|
||||||
/// - Returns: The encoded `URLRequest`.
|
|
||||||
/// - Throws: Any `Error` produced during parameter encoding.
|
|
||||||
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP
|
|
||||||
/// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as
|
|
||||||
/// the HTTP body depends on the destination of the encoding.
|
|
||||||
///
|
|
||||||
/// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to
|
|
||||||
/// `application/x-www-form-urlencoded; charset=utf-8`.
|
|
||||||
///
|
|
||||||
/// There is no published specification for how to encode collection types. By default the convention of appending
|
|
||||||
/// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for
|
|
||||||
/// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the
|
|
||||||
/// square brackets appended to array keys.
|
|
||||||
///
|
|
||||||
/// `BoolEncoding` can be used to configure how boolean values are encoded. The default behavior is to encode
|
|
||||||
/// `true` as 1 and `false` as 0.
|
|
||||||
public struct URLEncoding: ParameterEncoding {
|
|
||||||
// MARK: Helper Types
|
|
||||||
|
|
||||||
/// Defines whether the url-encoded query string is applied to the existing query string or HTTP body of the
|
|
||||||
/// resulting URL request.
|
|
||||||
public enum Destination {
|
|
||||||
/// Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE` requests and
|
|
||||||
/// sets as the HTTP body for requests with any other HTTP method.
|
|
||||||
case methodDependent
|
|
||||||
/// Sets or appends encoded query string result to existing query string.
|
|
||||||
case queryString
|
|
||||||
/// Sets encoded query string result as the HTTP body of the URL request.
|
|
||||||
case httpBody
|
|
||||||
|
|
||||||
func encodesParametersInURL(for method: HTTPMethod) -> Bool {
|
|
||||||
switch self {
|
|
||||||
case .methodDependent: return [.get, .head, .delete].contains(method)
|
|
||||||
case .queryString: return true
|
|
||||||
case .httpBody: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configures how `Array` parameters are encoded.
|
|
||||||
public enum ArrayEncoding {
|
|
||||||
/// An empty set of square brackets is appended to the key for every value. This is the default behavior.
|
|
||||||
case brackets
|
|
||||||
/// No brackets are appended. The key is encoded as is.
|
|
||||||
case noBrackets
|
|
||||||
/// Brackets containing the item index are appended. This matches the jQuery and Node.js behavior.
|
|
||||||
case indexInBrackets
|
|
||||||
/// Provide a custom array key encoding with the given closure.
|
|
||||||
case custom((_ key: String, _ index: Int) -> String)
|
|
||||||
|
|
||||||
func encode(key: String, atIndex index: Int) -> String {
|
|
||||||
switch self {
|
|
||||||
case .brackets:
|
|
||||||
return "\(key)[]"
|
|
||||||
case .noBrackets:
|
|
||||||
return key
|
|
||||||
case .indexInBrackets:
|
|
||||||
return "\(key)[\(index)]"
|
|
||||||
case let .custom(encoding):
|
|
||||||
return encoding(key, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configures how `Bool` parameters are encoded.
|
|
||||||
public enum BoolEncoding {
|
|
||||||
/// Encode `true` as `1` and `false` as `0`. This is the default behavior.
|
|
||||||
case numeric
|
|
||||||
/// Encode `true` and `false` as string literals.
|
|
||||||
case literal
|
|
||||||
|
|
||||||
func encode(value: Bool) -> String {
|
|
||||||
switch self {
|
|
||||||
case .numeric:
|
|
||||||
return value ? "1" : "0"
|
|
||||||
case .literal:
|
|
||||||
return value ? "true" : "false"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Properties
|
|
||||||
|
|
||||||
/// Returns a default `URLEncoding` instance with a `.methodDependent` destination.
|
|
||||||
public static var `default`: URLEncoding { URLEncoding() }
|
|
||||||
|
|
||||||
/// Returns a `URLEncoding` instance with a `.queryString` destination.
|
|
||||||
public static var queryString: URLEncoding { URLEncoding(destination: .queryString) }
|
|
||||||
|
|
||||||
/// Returns a `URLEncoding` instance with an `.httpBody` destination.
|
|
||||||
public static var httpBody: URLEncoding { URLEncoding(destination: .httpBody) }
|
|
||||||
|
|
||||||
/// The destination defining where the encoded query string is to be applied to the URL request.
|
|
||||||
public let destination: Destination
|
|
||||||
|
|
||||||
/// The encoding to use for `Array` parameters.
|
|
||||||
public let arrayEncoding: ArrayEncoding
|
|
||||||
|
|
||||||
/// The encoding to use for `Bool` parameters.
|
|
||||||
public let boolEncoding: BoolEncoding
|
|
||||||
|
|
||||||
// MARK: Initialization
|
|
||||||
|
|
||||||
/// Creates an instance using the specified parameters.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - destination: `Destination` defining where the encoded query string will be applied. `.methodDependent` by
|
|
||||||
/// default.
|
|
||||||
/// - arrayEncoding: `ArrayEncoding` to use. `.brackets` by default.
|
|
||||||
/// - boolEncoding: `BoolEncoding` to use. `.numeric` by default.
|
|
||||||
public init(destination: Destination = .methodDependent,
|
|
||||||
arrayEncoding: ArrayEncoding = .brackets,
|
|
||||||
boolEncoding: BoolEncoding = .numeric) {
|
|
||||||
self.destination = destination
|
|
||||||
self.arrayEncoding = arrayEncoding
|
|
||||||
self.boolEncoding = boolEncoding
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Encoding
|
|
||||||
|
|
||||||
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
|
|
||||||
var urlRequest = try urlRequest.asURLRequest()
|
|
||||||
|
|
||||||
guard let parameters = parameters else { return urlRequest }
|
|
||||||
|
|
||||||
if let method = urlRequest.method, destination.encodesParametersInURL(for: method) {
|
|
||||||
guard let url = urlRequest.url else {
|
|
||||||
throw AFError.parameterEncodingFailed(reason: .missingURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
|
|
||||||
let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
|
|
||||||
urlComponents.percentEncodedQuery = percentEncodedQuery
|
|
||||||
urlRequest.url = urlComponents.url
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if urlRequest.headers["Content-Type"] == nil {
|
|
||||||
urlRequest.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8"))
|
|
||||||
}
|
|
||||||
|
|
||||||
urlRequest.httpBody = Data(query(parameters).utf8)
|
|
||||||
}
|
|
||||||
|
|
||||||
return urlRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a percent-escaped, URL encoded query string components from the given key-value pair recursively.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - key: Key of the query component.
|
|
||||||
/// - value: Value of the query component.
|
|
||||||
///
|
|
||||||
/// - Returns: The percent-escaped, URL encoded query string components.
|
|
||||||
public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
|
|
||||||
var components: [(String, String)] = []
|
|
||||||
switch value {
|
|
||||||
case let dictionary as [String: Any]:
|
|
||||||
for (nestedKey, value) in dictionary {
|
|
||||||
components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
|
|
||||||
}
|
|
||||||
case let array as [Any]:
|
|
||||||
for (index, value) in array.enumerated() {
|
|
||||||
components += queryComponents(fromKey: arrayEncoding.encode(key: key, atIndex: index), value: value)
|
|
||||||
}
|
|
||||||
case let number as NSNumber:
|
|
||||||
if number.isBool {
|
|
||||||
components.append((escape(key), escape(boolEncoding.encode(value: number.boolValue))))
|
|
||||||
} else {
|
|
||||||
components.append((escape(key), escape("\(number)")))
|
|
||||||
}
|
|
||||||
case let bool as Bool:
|
|
||||||
components.append((escape(key), escape(boolEncoding.encode(value: bool))))
|
|
||||||
default:
|
|
||||||
components.append((escape(key), escape("\(value)")))
|
|
||||||
}
|
|
||||||
return components
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a percent-escaped string following RFC 3986 for a query string key or value.
|
|
||||||
///
|
|
||||||
/// - Parameter string: `String` to be percent-escaped.
|
|
||||||
///
|
|
||||||
/// - Returns: The percent-escaped `String`.
|
|
||||||
public func escape(_ string: String) -> String {
|
|
||||||
string.addingPercentEncoding(withAllowedCharacters: .afURLQueryAllowed) ?? string
|
|
||||||
}
|
|
||||||
|
|
||||||
private func query(_ parameters: [String: Any]) -> String {
|
|
||||||
var components: [(String, String)] = []
|
|
||||||
|
|
||||||
for key in parameters.keys.sorted(by: <) {
|
|
||||||
let value = parameters[key]!
|
|
||||||
components += queryComponents(fromKey: key, value: value)
|
|
||||||
}
|
|
||||||
return components.map { "\($0)=\($1)" }.joined(separator: "&")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the
|
|
||||||
/// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`.
|
|
||||||
public struct JSONEncoding: ParameterEncoding {
|
|
||||||
public enum Error: Swift.Error {
|
|
||||||
case invalidJSONObject
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Properties
|
|
||||||
|
|
||||||
/// Returns a `JSONEncoding` instance with default writing options.
|
|
||||||
public static var `default`: JSONEncoding { JSONEncoding() }
|
|
||||||
|
|
||||||
/// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options.
|
|
||||||
public static var prettyPrinted: JSONEncoding { JSONEncoding(options: .prettyPrinted) }
|
|
||||||
|
|
||||||
/// The options for writing the parameters as JSON data.
|
|
||||||
public let options: JSONSerialization.WritingOptions
|
|
||||||
|
|
||||||
// MARK: Initialization
|
|
||||||
|
|
||||||
/// Creates an instance using the specified `WritingOptions`.
|
|
||||||
///
|
|
||||||
/// - Parameter options: `JSONSerialization.WritingOptions` to use.
|
|
||||||
public init(options: JSONSerialization.WritingOptions = []) {
|
|
||||||
self.options = options
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Encoding
|
|
||||||
|
|
||||||
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
|
|
||||||
var urlRequest = try urlRequest.asURLRequest()
|
|
||||||
|
|
||||||
guard let parameters = parameters else { return urlRequest }
|
|
||||||
|
|
||||||
guard JSONSerialization.isValidJSONObject(parameters) else {
|
|
||||||
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: Error.invalidJSONObject))
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
let data = try JSONSerialization.data(withJSONObject: parameters, options: options)
|
|
||||||
|
|
||||||
if urlRequest.headers["Content-Type"] == nil {
|
|
||||||
urlRequest.headers.update(.contentType("application/json"))
|
|
||||||
}
|
|
||||||
|
|
||||||
urlRequest.httpBody = data
|
|
||||||
} catch {
|
|
||||||
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
|
|
||||||
}
|
|
||||||
|
|
||||||
return urlRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encodes any JSON compatible object into a `URLRequest`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - urlRequest: `URLRequestConvertible` value into which the object will be encoded.
|
|
||||||
/// - jsonObject: `Any` value (must be JSON compatible` to be encoded into the `URLRequest`. `nil` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The encoded `URLRequest`.
|
|
||||||
/// - Throws: Any `Error` produced during encoding.
|
|
||||||
public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest {
|
|
||||||
var urlRequest = try urlRequest.asURLRequest()
|
|
||||||
|
|
||||||
guard let jsonObject = jsonObject else { return urlRequest }
|
|
||||||
|
|
||||||
guard JSONSerialization.isValidJSONObject(jsonObject) else {
|
|
||||||
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: Error.invalidJSONObject))
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options)
|
|
||||||
|
|
||||||
if urlRequest.headers["Content-Type"] == nil {
|
|
||||||
urlRequest.headers.update(.contentType("application/json"))
|
|
||||||
}
|
|
||||||
|
|
||||||
urlRequest.httpBody = data
|
|
||||||
} catch {
|
|
||||||
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
|
|
||||||
}
|
|
||||||
|
|
||||||
return urlRequest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension JSONEncoding.Error {
|
|
||||||
public var localizedDescription: String {
|
|
||||||
"""
|
|
||||||
Invalid JSON object provided for parameter or object encoding. \
|
|
||||||
This is most likely due to a value which can't be represented in Objective-C.
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension NSNumber {
|
|
||||||
fileprivate var isBool: Bool {
|
|
||||||
// Use Obj-C type encoding to check whether the underlying type is a `Bool`, as it's guaranteed as part of
|
|
||||||
// swift-corelibs-foundation, per [this discussion on the Swift forums](https://forums.swift.org/t/alamofire-on-linux-possible-but-not-release-ready/34553/22).
|
|
||||||
String(cString: objCType) == "c"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
168
SwiftProject/Pods/Alamofire/Source/Protected.swift
generated
168
SwiftProject/Pods/Alamofire/Source/Protected.swift
generated
@ -1,168 +0,0 @@
|
|||||||
//
|
|
||||||
// Protected.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
private protocol Lock {
|
|
||||||
func lock()
|
|
||||||
func unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Lock {
|
|
||||||
/// Executes a closure returning a value while acquiring the lock.
|
|
||||||
///
|
|
||||||
/// - Parameter closure: The closure to run.
|
|
||||||
///
|
|
||||||
/// - Returns: The value the closure generated.
|
|
||||||
func around<T>(_ closure: () throws -> T) rethrows -> T {
|
|
||||||
lock(); defer { unlock() }
|
|
||||||
return try closure()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute a closure while acquiring the lock.
|
|
||||||
///
|
|
||||||
/// - Parameter closure: The closure to run.
|
|
||||||
func around(_ closure: () throws -> Void) rethrows {
|
|
||||||
lock(); defer { unlock() }
|
|
||||||
try closure()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(Darwin)
|
|
||||||
/// An `os_unfair_lock` wrapper.
|
|
||||||
final class UnfairLock: Lock {
|
|
||||||
private let unfairLock: os_unfair_lock_t
|
|
||||||
|
|
||||||
init() {
|
|
||||||
unfairLock = .allocate(capacity: 1)
|
|
||||||
unfairLock.initialize(to: os_unfair_lock())
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
unfairLock.deinitialize(count: 1)
|
|
||||||
unfairLock.deallocate()
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func lock() {
|
|
||||||
os_unfair_lock_lock(unfairLock)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func unlock() {
|
|
||||||
os_unfair_lock_unlock(unfairLock)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#elseif canImport(Foundation)
|
|
||||||
extension NSLock: Lock {}
|
|
||||||
#else
|
|
||||||
#error("This platform needs a Lock-conforming type without Foundation.")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// A thread-safe wrapper around a value.
|
|
||||||
@dynamicMemberLookup
|
|
||||||
final class Protected<Value> {
|
|
||||||
#if canImport(Darwin)
|
|
||||||
private let lock = UnfairLock()
|
|
||||||
#elseif canImport(Foundation)
|
|
||||||
private let lock = NSLock()
|
|
||||||
#else
|
|
||||||
#error("This platform needs a Lock-conforming type without Foundation.")
|
|
||||||
#endif
|
|
||||||
private var value: Value
|
|
||||||
|
|
||||||
init(_ value: Value) {
|
|
||||||
self.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Synchronously read or transform the contained value.
|
|
||||||
///
|
|
||||||
/// - Parameter closure: The closure to execute.
|
|
||||||
///
|
|
||||||
/// - Returns: The return value of the closure passed.
|
|
||||||
func read<U>(_ closure: (Value) throws -> U) rethrows -> U {
|
|
||||||
try lock.around { try closure(self.value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Synchronously modify the protected value.
|
|
||||||
///
|
|
||||||
/// - Parameter closure: The closure to execute.
|
|
||||||
///
|
|
||||||
/// - Returns: The modified value.
|
|
||||||
@discardableResult
|
|
||||||
func write<U>(_ closure: (inout Value) throws -> U) rethrows -> U {
|
|
||||||
try lock.around { try closure(&self.value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Synchronously update the protected value.
|
|
||||||
///
|
|
||||||
/// - Parameter value: The `Value`.
|
|
||||||
func write(_ value: Value) {
|
|
||||||
write { $0 = value }
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript<Property>(dynamicMember keyPath: WritableKeyPath<Value, Property>) -> Property {
|
|
||||||
get { lock.around { value[keyPath: keyPath] } }
|
|
||||||
set { lock.around { value[keyPath: keyPath] = newValue } }
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript<Property>(dynamicMember keyPath: KeyPath<Value, Property>) -> Property {
|
|
||||||
lock.around { value[keyPath: keyPath] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Protected where Value == Request.MutableState {
|
|
||||||
/// Attempts to transition to the passed `State`.
|
|
||||||
///
|
|
||||||
/// - Parameter state: The `State` to attempt transition to.
|
|
||||||
///
|
|
||||||
/// - Returns: Whether the transition occurred.
|
|
||||||
func attemptToTransitionTo(_ state: Request.State) -> Bool {
|
|
||||||
lock.around {
|
|
||||||
guard value.state.canTransitionTo(state) else { return false }
|
|
||||||
|
|
||||||
value.state = state
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform a closure while locked with the provided `Request.State`.
|
|
||||||
///
|
|
||||||
/// - Parameter perform: The closure to perform while locked.
|
|
||||||
func withState(perform: (Request.State) -> Void) {
|
|
||||||
lock.around { perform(value.state) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Protected: Equatable where Value: Equatable {
|
|
||||||
static func ==(lhs: Protected<Value>, rhs: Protected<Value>) -> Bool {
|
|
||||||
lhs.read { left in rhs.read { right in left == right }}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Protected: Hashable where Value: Hashable {
|
|
||||||
func hash(into hasher: inout Hasher) {
|
|
||||||
read { hasher.combine($0) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
111
SwiftProject/Pods/Alamofire/Source/RedirectHandler.swift
generated
111
SwiftProject/Pods/Alamofire/Source/RedirectHandler.swift
generated
@ -1,111 +0,0 @@
|
|||||||
//
|
|
||||||
// RedirectHandler.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// A type that handles how an HTTP redirect response from a remote server should be redirected to the new request.
|
|
||||||
public protocol RedirectHandler {
|
|
||||||
/// Determines how the HTTP redirect response should be redirected to the new request.
|
|
||||||
///
|
|
||||||
/// The `completion` closure should be passed one of three possible options:
|
|
||||||
///
|
|
||||||
/// 1. The new request specified by the redirect (this is the most common use case).
|
|
||||||
/// 2. A modified version of the new request (you may want to route it somewhere else).
|
|
||||||
/// 3. A `nil` value to deny the redirect request and return the body of the redirect response.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - task: The `URLSessionTask` whose request resulted in a redirect.
|
|
||||||
/// - request: The `URLRequest` to the new location specified by the redirect response.
|
|
||||||
/// - response: The `HTTPURLResponse` containing the server's response to the original request.
|
|
||||||
/// - completion: The closure to execute containing the new `URLRequest`, a modified `URLRequest`, or `nil`.
|
|
||||||
func task(_ task: URLSessionTask,
|
|
||||||
willBeRedirectedTo request: URLRequest,
|
|
||||||
for response: HTTPURLResponse,
|
|
||||||
completion: @escaping (URLRequest?) -> Void)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// `Redirector` is a convenience `RedirectHandler` making it easy to follow, not follow, or modify a redirect.
|
|
||||||
public struct Redirector {
|
|
||||||
/// Defines the behavior of the `Redirector` type.
|
|
||||||
public enum Behavior {
|
|
||||||
/// Follow the redirect as defined in the response.
|
|
||||||
case follow
|
|
||||||
/// Do not follow the redirect defined in the response.
|
|
||||||
case doNotFollow
|
|
||||||
/// Modify the redirect request defined in the response.
|
|
||||||
case modify((URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a `Redirector` with a `.follow` `Behavior`.
|
|
||||||
public static let follow = Redirector(behavior: .follow)
|
|
||||||
/// Returns a `Redirector` with a `.doNotFollow` `Behavior`.
|
|
||||||
public static let doNotFollow = Redirector(behavior: .doNotFollow)
|
|
||||||
|
|
||||||
/// The `Behavior` of the `Redirector`.
|
|
||||||
public let behavior: Behavior
|
|
||||||
|
|
||||||
/// Creates a `Redirector` instance from the `Behavior`.
|
|
||||||
///
|
|
||||||
/// - Parameter behavior: The `Behavior`.
|
|
||||||
public init(behavior: Behavior) {
|
|
||||||
self.behavior = behavior
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension Redirector: RedirectHandler {
|
|
||||||
public func task(_ task: URLSessionTask,
|
|
||||||
willBeRedirectedTo request: URLRequest,
|
|
||||||
for response: HTTPURLResponse,
|
|
||||||
completion: @escaping (URLRequest?) -> Void) {
|
|
||||||
switch behavior {
|
|
||||||
case .follow:
|
|
||||||
completion(request)
|
|
||||||
case .doNotFollow:
|
|
||||||
completion(nil)
|
|
||||||
case let .modify(closure):
|
|
||||||
let request = closure(task, request, response)
|
|
||||||
completion(request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RedirectHandler where Self == Redirector {
|
|
||||||
/// Provides a `Redirector` which follows redirects. Equivalent to `Redirector.follow`.
|
|
||||||
public static var follow: Redirector { .follow }
|
|
||||||
|
|
||||||
/// Provides a `Redirector` which does not follow redirects. Equivalent to `Redirector.doNotFollow`.
|
|
||||||
public static var doNotFollow: Redirector { .doNotFollow }
|
|
||||||
|
|
||||||
/// Creates a `Redirector` which modifies the redirected `URLRequest` using the provided closure.
|
|
||||||
///
|
|
||||||
/// - Parameter closure: Closure used to modify the redirect.
|
|
||||||
/// - Returns: The `Redirector`.
|
|
||||||
public static func modify(using closure: @escaping (URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?) -> Redirector {
|
|
||||||
Redirector(behavior: .modify(closure))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2066
SwiftProject/Pods/Alamofire/Source/Request.swift
generated
2066
SwiftProject/Pods/Alamofire/Source/Request.swift
generated
File diff suppressed because it is too large
Load Diff
@ -1,153 +0,0 @@
|
|||||||
//
|
|
||||||
// RequestCompression.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
#if canImport(zlib)
|
|
||||||
import Foundation
|
|
||||||
import zlib
|
|
||||||
|
|
||||||
/// `RequestAdapter` which compresses outgoing `URLRequest` bodies using the `deflate` `Content-Encoding` and adds the
|
|
||||||
/// appropriate header.
|
|
||||||
///
|
|
||||||
/// - Note: Most requests to most APIs are small and so would only be slowed down by applying this adapter. Measure the
|
|
||||||
/// size of your request bodies and the performance impact of using this adapter before use. Using this adapter
|
|
||||||
/// with already compressed data, such as images, will, at best, have no effect. Additionally, body compression
|
|
||||||
/// is a synchronous operation, so measuring the performance impact may be important to determine whether you
|
|
||||||
/// want to use a dedicated `requestQueue` in your `Session` instance. Finally, not all servers support request
|
|
||||||
/// compression, so test with all of your server configurations before deploying.
|
|
||||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
||||||
public struct DeflateRequestCompressor: RequestInterceptor {
|
|
||||||
/// Type that determines the action taken when the `URLRequest` already has a `Content-Encoding` header.
|
|
||||||
public enum DuplicateHeaderBehavior {
|
|
||||||
/// Throws a `DuplicateHeaderError`. The default.
|
|
||||||
case error
|
|
||||||
/// Replaces the existing header value with `deflate`.
|
|
||||||
case replace
|
|
||||||
/// Silently skips compression when the header exists.
|
|
||||||
case skip
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `Error` produced when the outgoing `URLRequest` already has a `Content-Encoding` header, when the instance has
|
|
||||||
/// been configured to produce an error.
|
|
||||||
public struct DuplicateHeaderError: Error {}
|
|
||||||
|
|
||||||
/// Behavior to use when the outgoing `URLRequest` already has a `Content-Encoding` header.
|
|
||||||
public let duplicateHeaderBehavior: DuplicateHeaderBehavior
|
|
||||||
/// Closure which determines whether the outgoing body data should be compressed.
|
|
||||||
public let shouldCompressBodyData: (_ bodyData: Data) -> Bool
|
|
||||||
|
|
||||||
/// Creates an instance with the provided parameters.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - duplicateHeaderBehavior: `DuplicateHeaderBehavior` to use. `.error` by default.
|
|
||||||
/// - shouldCompressBodyData: Closure which determines whether the outgoing body data should be compressed. `true` by default.
|
|
||||||
public init(duplicateHeaderBehavior: DuplicateHeaderBehavior = .error,
|
|
||||||
shouldCompressBodyData: @escaping (_ bodyData: Data) -> Bool = { _ in true }) {
|
|
||||||
self.duplicateHeaderBehavior = duplicateHeaderBehavior
|
|
||||||
self.shouldCompressBodyData = shouldCompressBodyData
|
|
||||||
}
|
|
||||||
|
|
||||||
public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
||||||
// No need to compress unless we have body data. No support for compressing streams.
|
|
||||||
guard let bodyData = urlRequest.httpBody else {
|
|
||||||
completion(.success(urlRequest))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard shouldCompressBodyData(bodyData) else {
|
|
||||||
completion(.success(urlRequest))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if urlRequest.headers.value(for: "Content-Encoding") != nil {
|
|
||||||
switch duplicateHeaderBehavior {
|
|
||||||
case .error:
|
|
||||||
completion(.failure(DuplicateHeaderError()))
|
|
||||||
return
|
|
||||||
case .replace:
|
|
||||||
// Header will be replaced once the body data is compressed.
|
|
||||||
break
|
|
||||||
case .skip:
|
|
||||||
completion(.success(urlRequest))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var compressedRequest = urlRequest
|
|
||||||
|
|
||||||
do {
|
|
||||||
compressedRequest.httpBody = try deflate(bodyData)
|
|
||||||
compressedRequest.headers.update(.contentEncoding("deflate"))
|
|
||||||
completion(.success(compressedRequest))
|
|
||||||
} catch {
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func deflate(_ data: Data) throws -> Data {
|
|
||||||
var output = Data([0x78, 0x5E]) // Header
|
|
||||||
try output.append((data as NSData).compressed(using: .zlib) as Data)
|
|
||||||
var checksum = adler32Checksum(of: data).bigEndian
|
|
||||||
output.append(Data(bytes: &checksum, count: MemoryLayout<UInt32>.size))
|
|
||||||
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
func adler32Checksum(of data: Data) -> UInt32 {
|
|
||||||
#if swift(>=5.6)
|
|
||||||
data.withUnsafeBytes { buffer in
|
|
||||||
UInt32(adler32(1, buffer.baseAddress, UInt32(buffer.count)))
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
data.withUnsafeBytes { buffer in
|
|
||||||
let buffer = buffer.bindMemory(to: UInt8.self)
|
|
||||||
return UInt32(adler32(1, buffer.baseAddress, UInt32(buffer.count)))
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
|
|
||||||
extension RequestInterceptor where Self == DeflateRequestCompressor {
|
|
||||||
/// Create a `DeflateRequestCompressor` with default `duplicateHeaderBehavior` and `shouldCompressBodyData` values.
|
|
||||||
public static var deflateCompressor: DeflateRequestCompressor {
|
|
||||||
DeflateRequestCompressor()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `DeflateRequestCompressor` with the provided `DuplicateHeaderBehavior` and `shouldCompressBodyData`
|
|
||||||
/// closure.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - duplicateHeaderBehavior: `DuplicateHeaderBehavior` to use.
|
|
||||||
/// - shouldCompressBodyData: Closure which determines whether the outgoing body data should be compressed. `true` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `DeflateRequestCompressor`.
|
|
||||||
public static func deflateCompressor(
|
|
||||||
duplicateHeaderBehavior: DeflateRequestCompressor.DuplicateHeaderBehavior = .error,
|
|
||||||
shouldCompressBodyData: @escaping (_ bodyData: Data) -> Bool = { _ in true }
|
|
||||||
) -> DeflateRequestCompressor {
|
|
||||||
DeflateRequestCompressor(duplicateHeaderBehavior: duplicateHeaderBehavior,
|
|
||||||
shouldCompressBodyData: shouldCompressBodyData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@ -1,351 +0,0 @@
|
|||||||
//
|
|
||||||
// RequestInterceptor.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Stores all state associated with a `URLRequest` being adapted.
|
|
||||||
public struct RequestAdapterState {
|
|
||||||
/// The `UUID` of the `Request` associated with the `URLRequest` to adapt.
|
|
||||||
public let requestID: UUID
|
|
||||||
|
|
||||||
/// The `Session` associated with the `URLRequest` to adapt.
|
|
||||||
public let session: Session
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// A type that can inspect and optionally adapt a `URLRequest` in some manner if necessary.
|
|
||||||
public protocol RequestAdapter {
|
|
||||||
/// Inspects and adapts the specified `URLRequest` in some manner and calls the completion handler with the Result.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - urlRequest: The `URLRequest` to adapt.
|
|
||||||
/// - session: The `Session` that will execute the `URLRequest`.
|
|
||||||
/// - completion: The completion handler that must be called when adaptation is complete.
|
|
||||||
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
|
||||||
|
|
||||||
/// Inspects and adapts the specified `URLRequest` in some manner and calls the completion handler with the Result.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - urlRequest: The `URLRequest` to adapt.
|
|
||||||
/// - state: The `RequestAdapterState` associated with the `URLRequest`.
|
|
||||||
/// - completion: The completion handler that must be called when adaptation is complete.
|
|
||||||
func adapt(_ urlRequest: URLRequest, using state: RequestAdapterState, completion: @escaping (Result<URLRequest, Error>) -> Void)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RequestAdapter {
|
|
||||||
public func adapt(_ urlRequest: URLRequest, using state: RequestAdapterState, completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
||||||
adapt(urlRequest, for: state.session, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// Outcome of determination whether retry is necessary.
|
|
||||||
public enum RetryResult {
|
|
||||||
/// Retry should be attempted immediately.
|
|
||||||
case retry
|
|
||||||
/// Retry should be attempted after the associated `TimeInterval`.
|
|
||||||
case retryWithDelay(TimeInterval)
|
|
||||||
/// Do not retry.
|
|
||||||
case doNotRetry
|
|
||||||
/// Do not retry due to the associated `Error`.
|
|
||||||
case doNotRetryWithError(Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RetryResult {
|
|
||||||
var retryRequired: Bool {
|
|
||||||
switch self {
|
|
||||||
case .retry, .retryWithDelay: return true
|
|
||||||
default: return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var delay: TimeInterval? {
|
|
||||||
switch self {
|
|
||||||
case let .retryWithDelay(delay): return delay
|
|
||||||
default: return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var error: Error? {
|
|
||||||
guard case let .doNotRetryWithError(error) = self else { return nil }
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A type that determines whether a request should be retried after being executed by the specified session manager
|
|
||||||
/// and encountering an error.
|
|
||||||
public protocol RequestRetrier {
|
|
||||||
/// Determines whether the `Request` should be retried by calling the `completion` closure.
|
|
||||||
///
|
|
||||||
/// This operation is fully asynchronous. Any amount of time can be taken to determine whether the request needs
|
|
||||||
/// to be retried. The one requirement is that the completion closure is called to ensure the request is properly
|
|
||||||
/// cleaned up after.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: `Request` that failed due to the provided `Error`.
|
|
||||||
/// - session: `Session` that produced the `Request`.
|
|
||||||
/// - error: `Error` encountered while executing the `Request`.
|
|
||||||
/// - completion: Completion closure to be executed when a retry decision has been determined.
|
|
||||||
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// Type that provides both `RequestAdapter` and `RequestRetrier` functionality.
|
|
||||||
public protocol RequestInterceptor: RequestAdapter, RequestRetrier {}
|
|
||||||
|
|
||||||
extension RequestInterceptor {
|
|
||||||
public func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
||||||
completion(.success(urlRequest))
|
|
||||||
}
|
|
||||||
|
|
||||||
public func retry(_ request: Request,
|
|
||||||
for session: Session,
|
|
||||||
dueTo error: Error,
|
|
||||||
completion: @escaping (RetryResult) -> Void) {
|
|
||||||
completion(.doNotRetry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `RequestAdapter` closure definition.
|
|
||||||
public typealias AdaptHandler = (URLRequest, Session, _ completion: @escaping (Result<URLRequest, Error>) -> Void) -> Void
|
|
||||||
/// `RequestRetrier` closure definition.
|
|
||||||
public typealias RetryHandler = (Request, Session, Error, _ completion: @escaping (RetryResult) -> Void) -> Void
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// Closure-based `RequestAdapter`.
|
|
||||||
open class Adapter: RequestInterceptor {
|
|
||||||
private let adaptHandler: AdaptHandler
|
|
||||||
|
|
||||||
/// Creates an instance using the provided closure.
|
|
||||||
///
|
|
||||||
/// - Parameter adaptHandler: `AdaptHandler` closure to be executed when handling request adaptation.
|
|
||||||
public init(_ adaptHandler: @escaping AdaptHandler) {
|
|
||||||
self.adaptHandler = adaptHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
||||||
adaptHandler(urlRequest, session, completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func adapt(_ urlRequest: URLRequest, using state: RequestAdapterState, completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
||||||
adaptHandler(urlRequest, state.session, completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RequestAdapter where Self == Adapter {
|
|
||||||
/// Creates an `Adapter` using the provided `AdaptHandler` closure.
|
|
||||||
///
|
|
||||||
/// - Parameter closure: `AdaptHandler` to use to adapt the request.
|
|
||||||
/// - Returns: The `Adapter`.
|
|
||||||
public static func adapter(using closure: @escaping AdaptHandler) -> Adapter {
|
|
||||||
Adapter(closure)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// Closure-based `RequestRetrier`.
|
|
||||||
open class Retrier: RequestInterceptor {
|
|
||||||
private let retryHandler: RetryHandler
|
|
||||||
|
|
||||||
/// Creates an instance using the provided closure.
|
|
||||||
///
|
|
||||||
/// - Parameter retryHandler: `RetryHandler` closure to be executed when handling request retry.
|
|
||||||
public init(_ retryHandler: @escaping RetryHandler) {
|
|
||||||
self.retryHandler = retryHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
open func retry(_ request: Request,
|
|
||||||
for session: Session,
|
|
||||||
dueTo error: Error,
|
|
||||||
completion: @escaping (RetryResult) -> Void) {
|
|
||||||
retryHandler(request, session, error, completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RequestRetrier where Self == Retrier {
|
|
||||||
/// Creates a `Retrier` using the provided `RetryHandler` closure.
|
|
||||||
///
|
|
||||||
/// - Parameter closure: `RetryHandler` to use to retry the request.
|
|
||||||
/// - Returns: The `Retrier`.
|
|
||||||
public static func retrier(using closure: @escaping RetryHandler) -> Retrier {
|
|
||||||
Retrier(closure)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// `RequestInterceptor` which can use multiple `RequestAdapter` and `RequestRetrier` values.
|
|
||||||
open class Interceptor: RequestInterceptor {
|
|
||||||
/// All `RequestAdapter`s associated with the instance. These adapters will be run until one fails.
|
|
||||||
public let adapters: [RequestAdapter]
|
|
||||||
/// All `RequestRetrier`s associated with the instance. These retriers will be run one at a time until one triggers retry.
|
|
||||||
public let retriers: [RequestRetrier]
|
|
||||||
|
|
||||||
/// Creates an instance from `AdaptHandler` and `RetryHandler` closures.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - adaptHandler: `AdaptHandler` closure to be used.
|
|
||||||
/// - retryHandler: `RetryHandler` closure to be used.
|
|
||||||
public init(adaptHandler: @escaping AdaptHandler, retryHandler: @escaping RetryHandler) {
|
|
||||||
adapters = [Adapter(adaptHandler)]
|
|
||||||
retriers = [Retrier(retryHandler)]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an instance from `RequestAdapter` and `RequestRetrier` values.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - adapter: `RequestAdapter` value to be used.
|
|
||||||
/// - retrier: `RequestRetrier` value to be used.
|
|
||||||
public init(adapter: RequestAdapter, retrier: RequestRetrier) {
|
|
||||||
adapters = [adapter]
|
|
||||||
retriers = [retrier]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an instance from the arrays of `RequestAdapter` and `RequestRetrier` values.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - adapters: `RequestAdapter` values to be used.
|
|
||||||
/// - retriers: `RequestRetrier` values to be used.
|
|
||||||
/// - interceptors: `RequestInterceptor`s to be used.
|
|
||||||
public init(adapters: [RequestAdapter] = [], retriers: [RequestRetrier] = [], interceptors: [RequestInterceptor] = []) {
|
|
||||||
self.adapters = adapters + interceptors
|
|
||||||
self.retriers = retriers + interceptors
|
|
||||||
}
|
|
||||||
|
|
||||||
open func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
||||||
adapt(urlRequest, for: session, using: adapters, completion: completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func adapt(_ urlRequest: URLRequest,
|
|
||||||
for session: Session,
|
|
||||||
using adapters: [RequestAdapter],
|
|
||||||
completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
||||||
var pendingAdapters = adapters
|
|
||||||
|
|
||||||
guard !pendingAdapters.isEmpty else { completion(.success(urlRequest)); return }
|
|
||||||
|
|
||||||
let adapter = pendingAdapters.removeFirst()
|
|
||||||
|
|
||||||
adapter.adapt(urlRequest, for: session) { result in
|
|
||||||
switch result {
|
|
||||||
case let .success(urlRequest):
|
|
||||||
self.adapt(urlRequest, for: session, using: pendingAdapters, completion: completion)
|
|
||||||
case .failure:
|
|
||||||
completion(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open func adapt(_ urlRequest: URLRequest, using state: RequestAdapterState, completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
||||||
adapt(urlRequest, using: state, adapters: adapters, completion: completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func adapt(_ urlRequest: URLRequest,
|
|
||||||
using state: RequestAdapterState,
|
|
||||||
adapters: [RequestAdapter],
|
|
||||||
completion: @escaping (Result<URLRequest, Error>) -> Void) {
|
|
||||||
var pendingAdapters = adapters
|
|
||||||
|
|
||||||
guard !pendingAdapters.isEmpty else { completion(.success(urlRequest)); return }
|
|
||||||
|
|
||||||
let adapter = pendingAdapters.removeFirst()
|
|
||||||
|
|
||||||
adapter.adapt(urlRequest, using: state) { result in
|
|
||||||
switch result {
|
|
||||||
case let .success(urlRequest):
|
|
||||||
self.adapt(urlRequest, using: state, adapters: pendingAdapters, completion: completion)
|
|
||||||
case .failure:
|
|
||||||
completion(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open func retry(_ request: Request,
|
|
||||||
for session: Session,
|
|
||||||
dueTo error: Error,
|
|
||||||
completion: @escaping (RetryResult) -> Void) {
|
|
||||||
retry(request, for: session, dueTo: error, using: retriers, completion: completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func retry(_ request: Request,
|
|
||||||
for session: Session,
|
|
||||||
dueTo error: Error,
|
|
||||||
using retriers: [RequestRetrier],
|
|
||||||
completion: @escaping (RetryResult) -> Void) {
|
|
||||||
var pendingRetriers = retriers
|
|
||||||
|
|
||||||
guard !pendingRetriers.isEmpty else { completion(.doNotRetry); return }
|
|
||||||
|
|
||||||
let retrier = pendingRetriers.removeFirst()
|
|
||||||
|
|
||||||
retrier.retry(request, for: session, dueTo: error) { result in
|
|
||||||
switch result {
|
|
||||||
case .retry, .retryWithDelay, .doNotRetryWithError:
|
|
||||||
completion(result)
|
|
||||||
case .doNotRetry:
|
|
||||||
// Only continue to the next retrier if retry was not triggered and no error was encountered
|
|
||||||
self.retry(request, for: session, dueTo: error, using: pendingRetriers, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RequestInterceptor where Self == Interceptor {
|
|
||||||
/// Creates an `Interceptor` using the provided `AdaptHandler` and `RetryHandler` closures.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - adapter: `AdapterHandler`to use to adapt the request.
|
|
||||||
/// - retrier: `RetryHandler` to use to retry the request.
|
|
||||||
/// - Returns: The `Interceptor`.
|
|
||||||
public static func interceptor(adapter: @escaping AdaptHandler, retrier: @escaping RetryHandler) -> Interceptor {
|
|
||||||
Interceptor(adaptHandler: adapter, retryHandler: retrier)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an `Interceptor` using the provided `RequestAdapter` and `RequestRetrier` instances.
|
|
||||||
/// - Parameters:
|
|
||||||
/// - adapter: `RequestAdapter` to use to adapt the request
|
|
||||||
/// - retrier: `RequestRetrier` to use to retry the request.
|
|
||||||
/// - Returns: The `Interceptor`.
|
|
||||||
public static func interceptor(adapter: RequestAdapter, retrier: RequestRetrier) -> Interceptor {
|
|
||||||
Interceptor(adapter: adapter, retrier: retrier)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an `Interceptor` using the provided `RequestAdapter`s, `RequestRetrier`s, and `RequestInterceptor`s.
|
|
||||||
/// - Parameters:
|
|
||||||
/// - adapters: `RequestAdapter`s to use to adapt the request. These adapters will be run until one fails.
|
|
||||||
/// - retriers: `RequestRetrier`s to use to retry the request. These retriers will be run one at a time until
|
|
||||||
/// a retry is triggered.
|
|
||||||
/// - interceptors: `RequestInterceptor`s to use to intercept the request.
|
|
||||||
/// - Returns: The `Interceptor`.
|
|
||||||
public static func interceptor(adapters: [RequestAdapter] = [],
|
|
||||||
retriers: [RequestRetrier] = [],
|
|
||||||
interceptors: [RequestInterceptor] = []) -> Interceptor {
|
|
||||||
Interceptor(adapters: adapters, retriers: retriers, interceptors: interceptors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
149
SwiftProject/Pods/Alamofire/Source/RequestTaskMap.swift
generated
149
SwiftProject/Pods/Alamofire/Source/RequestTaskMap.swift
generated
@ -1,149 +0,0 @@
|
|||||||
//
|
|
||||||
// RequestTaskMap.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// A type that maintains a two way, one to one map of `URLSessionTask`s to `Request`s.
|
|
||||||
struct RequestTaskMap {
|
|
||||||
private typealias Events = (completed: Bool, metricsGathered: Bool)
|
|
||||||
|
|
||||||
private var tasksToRequests: [URLSessionTask: Request]
|
|
||||||
private var requestsToTasks: [Request: URLSessionTask]
|
|
||||||
private var taskEvents: [URLSessionTask: Events]
|
|
||||||
|
|
||||||
var requests: [Request] {
|
|
||||||
Array(tasksToRequests.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
init(tasksToRequests: [URLSessionTask: Request] = [:],
|
|
||||||
requestsToTasks: [Request: URLSessionTask] = [:],
|
|
||||||
taskEvents: [URLSessionTask: (completed: Bool, metricsGathered: Bool)] = [:]) {
|
|
||||||
self.tasksToRequests = tasksToRequests
|
|
||||||
self.requestsToTasks = requestsToTasks
|
|
||||||
self.taskEvents = taskEvents
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript(_ request: Request) -> URLSessionTask? {
|
|
||||||
get { requestsToTasks[request] }
|
|
||||||
set {
|
|
||||||
guard let newValue = newValue else {
|
|
||||||
guard let task = requestsToTasks[request] else {
|
|
||||||
fatalError("RequestTaskMap consistency error: no task corresponding to request found.")
|
|
||||||
}
|
|
||||||
|
|
||||||
requestsToTasks.removeValue(forKey: request)
|
|
||||||
tasksToRequests.removeValue(forKey: task)
|
|
||||||
taskEvents.removeValue(forKey: task)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
requestsToTasks[request] = newValue
|
|
||||||
tasksToRequests[newValue] = request
|
|
||||||
taskEvents[newValue] = (completed: false, metricsGathered: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subscript(_ task: URLSessionTask) -> Request? {
|
|
||||||
get { tasksToRequests[task] }
|
|
||||||
set {
|
|
||||||
guard let newValue = newValue else {
|
|
||||||
guard let request = tasksToRequests[task] else {
|
|
||||||
fatalError("RequestTaskMap consistency error: no request corresponding to task found.")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasksToRequests.removeValue(forKey: task)
|
|
||||||
requestsToTasks.removeValue(forKey: request)
|
|
||||||
taskEvents.removeValue(forKey: task)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tasksToRequests[task] = newValue
|
|
||||||
requestsToTasks[newValue] = task
|
|
||||||
taskEvents[task] = (completed: false, metricsGathered: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var count: Int {
|
|
||||||
precondition(tasksToRequests.count == requestsToTasks.count,
|
|
||||||
"RequestTaskMap.count invalid, requests.count: \(tasksToRequests.count) != tasks.count: \(requestsToTasks.count)")
|
|
||||||
|
|
||||||
return tasksToRequests.count
|
|
||||||
}
|
|
||||||
|
|
||||||
var eventCount: Int {
|
|
||||||
precondition(taskEvents.count == count, "RequestTaskMap.eventCount invalid, count: \(count) != taskEvents.count: \(taskEvents.count)")
|
|
||||||
|
|
||||||
return taskEvents.count
|
|
||||||
}
|
|
||||||
|
|
||||||
var isEmpty: Bool {
|
|
||||||
precondition(tasksToRequests.isEmpty == requestsToTasks.isEmpty,
|
|
||||||
"RequestTaskMap.isEmpty invalid, requests.isEmpty: \(tasksToRequests.isEmpty) != tasks.isEmpty: \(requestsToTasks.isEmpty)")
|
|
||||||
|
|
||||||
return tasksToRequests.isEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
var isEventsEmpty: Bool {
|
|
||||||
precondition(taskEvents.isEmpty == isEmpty, "RequestTaskMap.isEventsEmpty invalid, isEmpty: \(isEmpty) != taskEvents.isEmpty: \(taskEvents.isEmpty)")
|
|
||||||
|
|
||||||
return taskEvents.isEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func disassociateIfNecessaryAfterGatheringMetricsForTask(_ task: URLSessionTask) -> Bool {
|
|
||||||
guard let events = taskEvents[task] else {
|
|
||||||
fatalError("RequestTaskMap consistency error: no events corresponding to task found.")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (events.completed, events.metricsGathered) {
|
|
||||||
case (_, true): fatalError("RequestTaskMap consistency error: duplicate metricsGatheredForTask call.")
|
|
||||||
case (false, false): taskEvents[task] = (completed: false, metricsGathered: true); return false
|
|
||||||
case (true, false): self[task] = nil; return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mutating func disassociateIfNecessaryAfterCompletingTask(_ task: URLSessionTask) -> Bool {
|
|
||||||
guard let events = taskEvents[task] else {
|
|
||||||
fatalError("RequestTaskMap consistency error: no events corresponding to task found.")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (events.completed, events.metricsGathered) {
|
|
||||||
case (true, _): fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.")
|
|
||||||
#if os(Linux) || os(Android) // Linux doesn't gather metrics, so unconditionally remove the reference and return true.
|
|
||||||
default: self[task] = nil; return true
|
|
||||||
#else
|
|
||||||
case (false, false):
|
|
||||||
if #available(macOS 10.12, iOS 10, watchOS 7, tvOS 10, *) {
|
|
||||||
taskEvents[task] = (completed: true, metricsGathered: false); return false
|
|
||||||
} else {
|
|
||||||
// watchOS < 7 doesn't gather metrics, so unconditionally remove the reference and return true.
|
|
||||||
self[task] = nil; return true
|
|
||||||
}
|
|
||||||
case (false, true):
|
|
||||||
self[task] = nil; return true
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
453
SwiftProject/Pods/Alamofire/Source/Response.swift
generated
453
SwiftProject/Pods/Alamofire/Source/Response.swift
generated
@ -1,453 +0,0 @@
|
|||||||
//
|
|
||||||
// Response.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Default type of `DataResponse` returned by Alamofire, with an `AFError` `Failure` type.
|
|
||||||
public typealias AFDataResponse<Success> = DataResponse<Success, AFError>
|
|
||||||
/// Default type of `DownloadResponse` returned by Alamofire, with an `AFError` `Failure` type.
|
|
||||||
public typealias AFDownloadResponse<Success> = DownloadResponse<Success, AFError>
|
|
||||||
|
|
||||||
/// Type used to store all values associated with a serialized response of a `DataRequest` or `UploadRequest`.
|
|
||||||
public struct DataResponse<Success, Failure: Error> {
|
|
||||||
/// The URL request sent to the server.
|
|
||||||
public let request: URLRequest?
|
|
||||||
|
|
||||||
/// The server's response to the URL request.
|
|
||||||
public let response: HTTPURLResponse?
|
|
||||||
|
|
||||||
/// The data returned by the server.
|
|
||||||
public let data: Data?
|
|
||||||
|
|
||||||
/// The final metrics of the response.
|
|
||||||
///
|
|
||||||
/// - Note: Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.`
|
|
||||||
///
|
|
||||||
public let metrics: URLSessionTaskMetrics?
|
|
||||||
|
|
||||||
/// The time taken to serialize the response.
|
|
||||||
public let serializationDuration: TimeInterval
|
|
||||||
|
|
||||||
/// The result of response serialization.
|
|
||||||
public let result: Result<Success, Failure>
|
|
||||||
|
|
||||||
/// Returns the associated value of the result if it is a success, `nil` otherwise.
|
|
||||||
public var value: Success? { result.success }
|
|
||||||
|
|
||||||
/// Returns the associated error value if the result if it is a failure, `nil` otherwise.
|
|
||||||
public var error: Failure? { result.failure }
|
|
||||||
|
|
||||||
/// Creates a `DataResponse` instance with the specified parameters derived from the response serialization.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: The `URLRequest` sent to the server.
|
|
||||||
/// - response: The `HTTPURLResponse` from the server.
|
|
||||||
/// - data: The `Data` returned by the server.
|
|
||||||
/// - metrics: The `URLSessionTaskMetrics` of the `DataRequest` or `UploadRequest`.
|
|
||||||
/// - serializationDuration: The duration taken by serialization.
|
|
||||||
/// - result: The `Result` of response serialization.
|
|
||||||
public init(request: URLRequest?,
|
|
||||||
response: HTTPURLResponse?,
|
|
||||||
data: Data?,
|
|
||||||
metrics: URLSessionTaskMetrics?,
|
|
||||||
serializationDuration: TimeInterval,
|
|
||||||
result: Result<Success, Failure>) {
|
|
||||||
self.request = request
|
|
||||||
self.response = response
|
|
||||||
self.data = data
|
|
||||||
self.metrics = metrics
|
|
||||||
self.serializationDuration = serializationDuration
|
|
||||||
self.result = result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible {
|
|
||||||
/// The textual representation used when written to an output stream, which includes whether the result was a
|
|
||||||
/// success or failure.
|
|
||||||
public var description: String {
|
|
||||||
"\(result)"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The debug textual representation used when written to an output stream, which includes (if available) a summary
|
|
||||||
/// of the `URLRequest`, the request's headers and body (if decodable as a `String` below 100KB); the
|
|
||||||
/// `HTTPURLResponse`'s status code, headers, and body; the duration of the network and serialization actions; and
|
|
||||||
/// the `Result` of serialization.
|
|
||||||
public var debugDescription: String {
|
|
||||||
guard let urlRequest = request else { return "[Request]: None\n[Result]: \(result)" }
|
|
||||||
|
|
||||||
let requestDescription = DebugDescription.description(of: urlRequest)
|
|
||||||
|
|
||||||
let responseDescription = response.map { response in
|
|
||||||
let responseBodyDescription = DebugDescription.description(for: data, headers: response.headers)
|
|
||||||
|
|
||||||
return """
|
|
||||||
\(DebugDescription.description(of: response))
|
|
||||||
\(responseBodyDescription.indentingNewlines())
|
|
||||||
"""
|
|
||||||
} ?? "[Response]: None"
|
|
||||||
|
|
||||||
let networkDuration = metrics.map { "\($0.taskInterval.duration)s" } ?? "None"
|
|
||||||
|
|
||||||
return """
|
|
||||||
\(requestDescription)
|
|
||||||
\(responseDescription)
|
|
||||||
[Network Duration]: \(networkDuration)
|
|
||||||
[Serialization Duration]: \(serializationDuration)s
|
|
||||||
[Result]: \(result)
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension DataResponse {
|
|
||||||
/// Evaluates the specified closure when the result of this `DataResponse` is a success, passing the unwrapped
|
|
||||||
/// result value as a parameter.
|
|
||||||
///
|
|
||||||
/// Use the `map` method with a closure that does not throw. For example:
|
|
||||||
///
|
|
||||||
/// let possibleData: DataResponse<Data> = ...
|
|
||||||
/// let possibleInt = possibleData.map { $0.count }
|
|
||||||
///
|
|
||||||
/// - parameter transform: A closure that takes the success value of the instance's result.
|
|
||||||
///
|
|
||||||
/// - returns: A `DataResponse` whose result wraps the value returned by the given closure. If this instance's
|
|
||||||
/// result is a failure, returns a response wrapping the same failure.
|
|
||||||
public func map<NewSuccess>(_ transform: (Success) -> NewSuccess) -> DataResponse<NewSuccess, Failure> {
|
|
||||||
DataResponse<NewSuccess, Failure>(request: request,
|
|
||||||
response: response,
|
|
||||||
data: data,
|
|
||||||
metrics: metrics,
|
|
||||||
serializationDuration: serializationDuration,
|
|
||||||
result: result.map(transform))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result
|
|
||||||
/// value as a parameter.
|
|
||||||
///
|
|
||||||
/// Use the `tryMap` method with a closure that may throw an error. For example:
|
|
||||||
///
|
|
||||||
/// let possibleData: DataResponse<Data> = ...
|
|
||||||
/// let possibleObject = possibleData.tryMap {
|
|
||||||
/// try JSONSerialization.jsonObject(with: $0)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// - parameter transform: A closure that takes the success value of the instance's result.
|
|
||||||
///
|
|
||||||
/// - returns: A success or failure `DataResponse` depending on the result of the given closure. If this instance's
|
|
||||||
/// result is a failure, returns the same failure.
|
|
||||||
public func tryMap<NewSuccess>(_ transform: (Success) throws -> NewSuccess) -> DataResponse<NewSuccess, Error> {
|
|
||||||
DataResponse<NewSuccess, Error>(request: request,
|
|
||||||
response: response,
|
|
||||||
data: data,
|
|
||||||
metrics: metrics,
|
|
||||||
serializationDuration: serializationDuration,
|
|
||||||
result: result.tryMap(transform))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter.
|
|
||||||
///
|
|
||||||
/// Use the `mapError` function with a closure that does not throw. For example:
|
|
||||||
///
|
|
||||||
/// let possibleData: DataResponse<Data> = ...
|
|
||||||
/// let withMyError = possibleData.mapError { MyError.error($0) }
|
|
||||||
///
|
|
||||||
/// - Parameter transform: A closure that takes the error of the instance.
|
|
||||||
///
|
|
||||||
/// - Returns: A `DataResponse` instance containing the result of the transform.
|
|
||||||
public func mapError<NewFailure: Error>(_ transform: (Failure) -> NewFailure) -> DataResponse<Success, NewFailure> {
|
|
||||||
DataResponse<Success, NewFailure>(request: request,
|
|
||||||
response: response,
|
|
||||||
data: data,
|
|
||||||
metrics: metrics,
|
|
||||||
serializationDuration: serializationDuration,
|
|
||||||
result: result.mapError(transform))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter.
|
|
||||||
///
|
|
||||||
/// Use the `tryMapError` function with a closure that may throw an error. For example:
|
|
||||||
///
|
|
||||||
/// let possibleData: DataResponse<Data> = ...
|
|
||||||
/// let possibleObject = possibleData.tryMapError {
|
|
||||||
/// try someFailableFunction(taking: $0)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// - Parameter transform: A throwing closure that takes the error of the instance.
|
|
||||||
///
|
|
||||||
/// - Returns: A `DataResponse` instance containing the result of the transform.
|
|
||||||
public func tryMapError<NewFailure: Error>(_ transform: (Failure) throws -> NewFailure) -> DataResponse<Success, Error> {
|
|
||||||
DataResponse<Success, Error>(request: request,
|
|
||||||
response: response,
|
|
||||||
data: data,
|
|
||||||
metrics: metrics,
|
|
||||||
serializationDuration: serializationDuration,
|
|
||||||
result: result.tryMapError(transform))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// Used to store all data associated with a serialized response of a download request.
|
|
||||||
public struct DownloadResponse<Success, Failure: Error> {
|
|
||||||
/// The URL request sent to the server.
|
|
||||||
public let request: URLRequest?
|
|
||||||
|
|
||||||
/// The server's response to the URL request.
|
|
||||||
public let response: HTTPURLResponse?
|
|
||||||
|
|
||||||
/// The final destination URL of the data returned from the server after it is moved.
|
|
||||||
public let fileURL: URL?
|
|
||||||
|
|
||||||
/// The resume data generated if the request was cancelled.
|
|
||||||
public let resumeData: Data?
|
|
||||||
|
|
||||||
/// The final metrics of the response.
|
|
||||||
///
|
|
||||||
/// - Note: Due to `FB7624529`, collection of `URLSessionTaskMetrics` on watchOS is currently disabled.`
|
|
||||||
///
|
|
||||||
public let metrics: URLSessionTaskMetrics?
|
|
||||||
|
|
||||||
/// The time taken to serialize the response.
|
|
||||||
public let serializationDuration: TimeInterval
|
|
||||||
|
|
||||||
/// The result of response serialization.
|
|
||||||
public let result: Result<Success, Failure>
|
|
||||||
|
|
||||||
/// Returns the associated value of the result if it is a success, `nil` otherwise.
|
|
||||||
public var value: Success? { result.success }
|
|
||||||
|
|
||||||
/// Returns the associated error value if the result if it is a failure, `nil` otherwise.
|
|
||||||
public var error: Failure? { result.failure }
|
|
||||||
|
|
||||||
/// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: The `URLRequest` sent to the server.
|
|
||||||
/// - response: The `HTTPURLResponse` from the server.
|
|
||||||
/// - fileURL: The final destination URL of the data returned from the server after it is moved.
|
|
||||||
/// - resumeData: The resume `Data` generated if the request was cancelled.
|
|
||||||
/// - metrics: The `URLSessionTaskMetrics` of the `DownloadRequest`.
|
|
||||||
/// - serializationDuration: The duration taken by serialization.
|
|
||||||
/// - result: The `Result` of response serialization.
|
|
||||||
public init(request: URLRequest?,
|
|
||||||
response: HTTPURLResponse?,
|
|
||||||
fileURL: URL?,
|
|
||||||
resumeData: Data?,
|
|
||||||
metrics: URLSessionTaskMetrics?,
|
|
||||||
serializationDuration: TimeInterval,
|
|
||||||
result: Result<Success, Failure>) {
|
|
||||||
self.request = request
|
|
||||||
self.response = response
|
|
||||||
self.fileURL = fileURL
|
|
||||||
self.resumeData = resumeData
|
|
||||||
self.metrics = metrics
|
|
||||||
self.serializationDuration = serializationDuration
|
|
||||||
self.result = result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible {
|
|
||||||
/// The textual representation used when written to an output stream, which includes whether the result was a
|
|
||||||
/// success or failure.
|
|
||||||
public var description: String {
|
|
||||||
"\(result)"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The debug textual representation used when written to an output stream, which includes the URL request, the URL
|
|
||||||
/// response, the temporary and destination URLs, the resume data, the durations of the network and serialization
|
|
||||||
/// actions, and the response serialization result.
|
|
||||||
public var debugDescription: String {
|
|
||||||
guard let urlRequest = request else { return "[Request]: None\n[Result]: \(result)" }
|
|
||||||
|
|
||||||
let requestDescription = DebugDescription.description(of: urlRequest)
|
|
||||||
let responseDescription = response.map(DebugDescription.description(of:)) ?? "[Response]: None"
|
|
||||||
let networkDuration = metrics.map { "\($0.taskInterval.duration)s" } ?? "None"
|
|
||||||
let resumeDataDescription = resumeData.map { "\($0)" } ?? "None"
|
|
||||||
|
|
||||||
return """
|
|
||||||
\(requestDescription)
|
|
||||||
\(responseDescription)
|
|
||||||
[File URL]: \(fileURL?.path ?? "None")
|
|
||||||
[Resume Data]: \(resumeDataDescription)
|
|
||||||
[Network Duration]: \(networkDuration)
|
|
||||||
[Serialization Duration]: \(serializationDuration)s
|
|
||||||
[Result]: \(result)
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension DownloadResponse {
|
|
||||||
/// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped
|
|
||||||
/// result value as a parameter.
|
|
||||||
///
|
|
||||||
/// Use the `map` method with a closure that does not throw. For example:
|
|
||||||
///
|
|
||||||
/// let possibleData: DownloadResponse<Data> = ...
|
|
||||||
/// let possibleInt = possibleData.map { $0.count }
|
|
||||||
///
|
|
||||||
/// - parameter transform: A closure that takes the success value of the instance's result.
|
|
||||||
///
|
|
||||||
/// - returns: A `DownloadResponse` whose result wraps the value returned by the given closure. If this instance's
|
|
||||||
/// result is a failure, returns a response wrapping the same failure.
|
|
||||||
public func map<NewSuccess>(_ transform: (Success) -> NewSuccess) -> DownloadResponse<NewSuccess, Failure> {
|
|
||||||
DownloadResponse<NewSuccess, Failure>(request: request,
|
|
||||||
response: response,
|
|
||||||
fileURL: fileURL,
|
|
||||||
resumeData: resumeData,
|
|
||||||
metrics: metrics,
|
|
||||||
serializationDuration: serializationDuration,
|
|
||||||
result: result.map(transform))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped
|
|
||||||
/// result value as a parameter.
|
|
||||||
///
|
|
||||||
/// Use the `tryMap` method with a closure that may throw an error. For example:
|
|
||||||
///
|
|
||||||
/// let possibleData: DownloadResponse<Data> = ...
|
|
||||||
/// let possibleObject = possibleData.tryMap {
|
|
||||||
/// try JSONSerialization.jsonObject(with: $0)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// - parameter transform: A closure that takes the success value of the instance's result.
|
|
||||||
///
|
|
||||||
/// - returns: A success or failure `DownloadResponse` depending on the result of the given closure. If this
|
|
||||||
/// instance's result is a failure, returns the same failure.
|
|
||||||
public func tryMap<NewSuccess>(_ transform: (Success) throws -> NewSuccess) -> DownloadResponse<NewSuccess, Error> {
|
|
||||||
DownloadResponse<NewSuccess, Error>(request: request,
|
|
||||||
response: response,
|
|
||||||
fileURL: fileURL,
|
|
||||||
resumeData: resumeData,
|
|
||||||
metrics: metrics,
|
|
||||||
serializationDuration: serializationDuration,
|
|
||||||
result: result.tryMap(transform))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter.
|
|
||||||
///
|
|
||||||
/// Use the `mapError` function with a closure that does not throw. For example:
|
|
||||||
///
|
|
||||||
/// let possibleData: DownloadResponse<Data> = ...
|
|
||||||
/// let withMyError = possibleData.mapError { MyError.error($0) }
|
|
||||||
///
|
|
||||||
/// - Parameter transform: A closure that takes the error of the instance.
|
|
||||||
///
|
|
||||||
/// - Returns: A `DownloadResponse` instance containing the result of the transform.
|
|
||||||
public func mapError<NewFailure: Error>(_ transform: (Failure) -> NewFailure) -> DownloadResponse<Success, NewFailure> {
|
|
||||||
DownloadResponse<Success, NewFailure>(request: request,
|
|
||||||
response: response,
|
|
||||||
fileURL: fileURL,
|
|
||||||
resumeData: resumeData,
|
|
||||||
metrics: metrics,
|
|
||||||
serializationDuration: serializationDuration,
|
|
||||||
result: result.mapError(transform))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter.
|
|
||||||
///
|
|
||||||
/// Use the `tryMapError` function with a closure that may throw an error. For example:
|
|
||||||
///
|
|
||||||
/// let possibleData: DownloadResponse<Data> = ...
|
|
||||||
/// let possibleObject = possibleData.tryMapError {
|
|
||||||
/// try someFailableFunction(taking: $0)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// - Parameter transform: A throwing closure that takes the error of the instance.
|
|
||||||
///
|
|
||||||
/// - Returns: A `DownloadResponse` instance containing the result of the transform.
|
|
||||||
public func tryMapError<NewFailure: Error>(_ transform: (Failure) throws -> NewFailure) -> DownloadResponse<Success, Error> {
|
|
||||||
DownloadResponse<Success, Error>(request: request,
|
|
||||||
response: response,
|
|
||||||
fileURL: fileURL,
|
|
||||||
resumeData: resumeData,
|
|
||||||
metrics: metrics,
|
|
||||||
serializationDuration: serializationDuration,
|
|
||||||
result: result.tryMapError(transform))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum DebugDescription {
|
|
||||||
static func description(of request: URLRequest) -> String {
|
|
||||||
let requestSummary = "\(request.httpMethod!) \(request)"
|
|
||||||
let requestHeadersDescription = DebugDescription.description(for: request.headers)
|
|
||||||
let requestBodyDescription = DebugDescription.description(for: request.httpBody, headers: request.headers)
|
|
||||||
|
|
||||||
return """
|
|
||||||
[Request]: \(requestSummary)
|
|
||||||
\(requestHeadersDescription.indentingNewlines())
|
|
||||||
\(requestBodyDescription.indentingNewlines())
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
static func description(of response: HTTPURLResponse) -> String {
|
|
||||||
"""
|
|
||||||
[Response]:
|
|
||||||
[Status Code]: \(response.statusCode)
|
|
||||||
\(DebugDescription.description(for: response.headers).indentingNewlines())
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
static func description(for headers: HTTPHeaders) -> String {
|
|
||||||
guard !headers.isEmpty else { return "[Headers]: None" }
|
|
||||||
|
|
||||||
let headerDescription = "\(headers.sorted())".indentingNewlines()
|
|
||||||
return """
|
|
||||||
[Headers]:
|
|
||||||
\(headerDescription)
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
static func description(for data: Data?,
|
|
||||||
headers: HTTPHeaders,
|
|
||||||
allowingPrintableTypes printableTypes: [String] = ["json", "xml", "text"],
|
|
||||||
maximumLength: Int = 100_000) -> String {
|
|
||||||
guard let data = data, !data.isEmpty else { return "[Body]: None" }
|
|
||||||
|
|
||||||
guard
|
|
||||||
data.count <= maximumLength,
|
|
||||||
printableTypes.compactMap({ headers["Content-Type"]?.contains($0) }).contains(true)
|
|
||||||
else { return "[Body]: \(data.count) bytes" }
|
|
||||||
|
|
||||||
return """
|
|
||||||
[Body]:
|
|
||||||
\(String(decoding: data, as: UTF8.self)
|
|
||||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
.indentingNewlines())
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
fileprivate func indentingNewlines(by spaceCount: Int = 4) -> String {
|
|
||||||
let spaces = String(repeating: " ", count: spaceCount)
|
|
||||||
return replacingOccurrences(of: "\n", with: "\n\(spaces)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,120 +0,0 @@
|
|||||||
//
|
|
||||||
// Result+Alamofire.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Default type of `Result` returned by Alamofire, with an `AFError` `Failure` type.
|
|
||||||
public typealias AFResult<Success> = Result<Success, AFError>
|
|
||||||
|
|
||||||
// MARK: - Internal APIs
|
|
||||||
|
|
||||||
extension Result {
|
|
||||||
/// Returns whether the instance is `.success`.
|
|
||||||
var isSuccess: Bool {
|
|
||||||
guard case .success = self else { return false }
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the instance is `.failure`.
|
|
||||||
var isFailure: Bool {
|
|
||||||
!isSuccess
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the associated value if the result is a success, `nil` otherwise.
|
|
||||||
var success: Success? {
|
|
||||||
guard case let .success(value) = self else { return nil }
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the associated error value if the result is a failure, `nil` otherwise.
|
|
||||||
var failure: Failure? {
|
|
||||||
guard case let .failure(error) = self else { return nil }
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initializes a `Result` from value or error. Returns `.failure` if the error is non-nil, `.success` otherwise.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - value: A value.
|
|
||||||
/// - error: An `Error`.
|
|
||||||
init(value: Success, error: Failure?) {
|
|
||||||
if let error = error {
|
|
||||||
self = .failure(error)
|
|
||||||
} else {
|
|
||||||
self = .success(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
|
|
||||||
///
|
|
||||||
/// Use the `tryMap` method with a closure that may throw an error. For example:
|
|
||||||
///
|
|
||||||
/// let possibleData: Result<Data, Error> = .success(Data(...))
|
|
||||||
/// let possibleObject = possibleData.tryMap {
|
|
||||||
/// try JSONSerialization.jsonObject(with: $0)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// - parameter transform: A closure that takes the success value of the instance.
|
|
||||||
///
|
|
||||||
/// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the
|
|
||||||
/// same failure.
|
|
||||||
func tryMap<NewSuccess>(_ transform: (Success) throws -> NewSuccess) -> Result<NewSuccess, Error> {
|
|
||||||
switch self {
|
|
||||||
case let .success(value):
|
|
||||||
do {
|
|
||||||
return try .success(transform(value))
|
|
||||||
} catch {
|
|
||||||
return .failure(error)
|
|
||||||
}
|
|
||||||
case let .failure(error):
|
|
||||||
return .failure(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter.
|
|
||||||
///
|
|
||||||
/// Use the `tryMapError` function with a closure that may throw an error. For example:
|
|
||||||
///
|
|
||||||
/// let possibleData: Result<Data, Error> = .success(Data(...))
|
|
||||||
/// let possibleObject = possibleData.tryMapError {
|
|
||||||
/// try someFailableFunction(taking: $0)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// - Parameter transform: A throwing closure that takes the error of the instance.
|
|
||||||
///
|
|
||||||
/// - Returns: A `Result` instance containing the result of the transform. If this instance is a success, returns
|
|
||||||
/// the same success.
|
|
||||||
func tryMapError<NewFailure: Error>(_ transform: (Failure) throws -> NewFailure) -> Result<Success, Error> {
|
|
||||||
switch self {
|
|
||||||
case let .failure(error):
|
|
||||||
do {
|
|
||||||
return try .failure(transform(error))
|
|
||||||
} catch {
|
|
||||||
return .failure(error)
|
|
||||||
}
|
|
||||||
case let .success(value):
|
|
||||||
return .success(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
430
SwiftProject/Pods/Alamofire/Source/RetryPolicy.swift
generated
430
SwiftProject/Pods/Alamofire/Source/RetryPolicy.swift
generated
@ -1,430 +0,0 @@
|
|||||||
//
|
|
||||||
// RetryPolicy.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2019-2020 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// A retry policy that retries requests using an exponential backoff for allowed HTTP methods and HTTP status codes
|
|
||||||
/// as well as certain types of networking errors.
|
|
||||||
open class RetryPolicy: RequestInterceptor {
|
|
||||||
/// The default retry limit for retry policies.
|
|
||||||
public static let defaultRetryLimit: UInt = 2
|
|
||||||
|
|
||||||
/// The default exponential backoff base for retry policies (must be a minimum of 2).
|
|
||||||
public static let defaultExponentialBackoffBase: UInt = 2
|
|
||||||
|
|
||||||
/// The default exponential backoff scale for retry policies.
|
|
||||||
public static let defaultExponentialBackoffScale: Double = 0.5
|
|
||||||
|
|
||||||
/// The default HTTP methods to retry.
|
|
||||||
/// See [RFC 2616 - Section 9.1.2](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) for more information.
|
|
||||||
public static let defaultRetryableHTTPMethods: Set<HTTPMethod> = [.delete, // [Delete](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7) - not always idempotent
|
|
||||||
.get, // [GET](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3) - generally idempotent
|
|
||||||
.head, // [HEAD](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4) - generally idempotent
|
|
||||||
.options, // [OPTIONS](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2) - inherently idempotent
|
|
||||||
.put, // [PUT](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6) - not always idempotent
|
|
||||||
.trace // [TRACE](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8) - inherently idempotent
|
|
||||||
]
|
|
||||||
|
|
||||||
/// The default HTTP status codes to retry.
|
|
||||||
/// See [RFC 2616 - Section 10](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10) for more information.
|
|
||||||
public static let defaultRetryableHTTPStatusCodes: Set<Int> = [408, // [Request Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.9)
|
|
||||||
500, // [Internal Server Error](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1)
|
|
||||||
502, // [Bad Gateway](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.3)
|
|
||||||
503, // [Service Unavailable](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4)
|
|
||||||
504 // [Gateway Timeout](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.5)
|
|
||||||
]
|
|
||||||
|
|
||||||
/// The default URL error codes to retry.
|
|
||||||
public static let defaultRetryableURLErrorCodes: Set<URLError.Code> = [ // [Security] App Transport Security disallowed a connection because there is no secure network connection.
|
|
||||||
// - [Disabled] ATS settings do not change at runtime.
|
|
||||||
// .appTransportSecurityRequiresSecureConnection,
|
|
||||||
|
|
||||||
// [System] An app or app extension attempted to connect to a background session that is already connected to a
|
|
||||||
// process.
|
|
||||||
// - [Enabled] The other process could release the background session.
|
|
||||||
.backgroundSessionInUseByAnotherProcess,
|
|
||||||
|
|
||||||
// [System] The shared container identifier of the URL session configuration is needed but has not been set.
|
|
||||||
// - [Disabled] Cannot change at runtime.
|
|
||||||
// .backgroundSessionRequiresSharedContainer,
|
|
||||||
|
|
||||||
// [System] The app is suspended or exits while a background data task is processing.
|
|
||||||
// - [Enabled] App can be foregrounded or launched to recover.
|
|
||||||
.backgroundSessionWasDisconnected,
|
|
||||||
|
|
||||||
// [Network] The URL Loading system received bad data from the server.
|
|
||||||
// - [Enabled] Server could return valid data when retrying.
|
|
||||||
.badServerResponse,
|
|
||||||
|
|
||||||
// [Resource] A malformed URL prevented a URL request from being initiated.
|
|
||||||
// - [Disabled] URL was most likely constructed incorrectly.
|
|
||||||
// .badURL,
|
|
||||||
|
|
||||||
// [System] A connection was attempted while a phone call is active on a network that does not support
|
|
||||||
// simultaneous phone and data communication (EDGE or GPRS).
|
|
||||||
// - [Enabled] Phone call could be ended to allow request to recover.
|
|
||||||
.callIsActive,
|
|
||||||
|
|
||||||
// [Client] An asynchronous load has been canceled.
|
|
||||||
// - [Disabled] Request was cancelled by the client.
|
|
||||||
// .cancelled,
|
|
||||||
|
|
||||||
// [File System] A download task couldn’t close the downloaded file on disk.
|
|
||||||
// - [Disabled] File system error is unlikely to recover with retry.
|
|
||||||
// .cannotCloseFile,
|
|
||||||
|
|
||||||
// [Network] An attempt to connect to a host failed.
|
|
||||||
// - [Enabled] Server or DNS lookup could recover during retry.
|
|
||||||
.cannotConnectToHost,
|
|
||||||
|
|
||||||
// [File System] A download task couldn’t create the downloaded file on disk because of an I/O failure.
|
|
||||||
// - [Disabled] File system error is unlikely to recover with retry.
|
|
||||||
// .cannotCreateFile,
|
|
||||||
|
|
||||||
// [Data] Content data received during a connection request had an unknown content encoding.
|
|
||||||
// - [Disabled] Server is unlikely to modify the content encoding during a retry.
|
|
||||||
// .cannotDecodeContentData,
|
|
||||||
|
|
||||||
// [Data] Content data received during a connection request could not be decoded for a known content encoding.
|
|
||||||
// - [Disabled] Server is unlikely to modify the content encoding during a retry.
|
|
||||||
// .cannotDecodeRawData,
|
|
||||||
|
|
||||||
// [Network] The host name for a URL could not be resolved.
|
|
||||||
// - [Enabled] Server or DNS lookup could recover during retry.
|
|
||||||
.cannotFindHost,
|
|
||||||
|
|
||||||
// [Network] A request to load an item only from the cache could not be satisfied.
|
|
||||||
// - [Enabled] Cache could be populated during a retry.
|
|
||||||
.cannotLoadFromNetwork,
|
|
||||||
|
|
||||||
// [File System] A download task was unable to move a downloaded file on disk.
|
|
||||||
// - [Disabled] File system error is unlikely to recover with retry.
|
|
||||||
// .cannotMoveFile,
|
|
||||||
|
|
||||||
// [File System] A download task was unable to open the downloaded file on disk.
|
|
||||||
// - [Disabled] File system error is unlikely to recover with retry.
|
|
||||||
// .cannotOpenFile,
|
|
||||||
|
|
||||||
// [Data] A task could not parse a response.
|
|
||||||
// - [Disabled] Invalid response is unlikely to recover with retry.
|
|
||||||
// .cannotParseResponse,
|
|
||||||
|
|
||||||
// [File System] A download task was unable to remove a downloaded file from disk.
|
|
||||||
// - [Disabled] File system error is unlikely to recover with retry.
|
|
||||||
// .cannotRemoveFile,
|
|
||||||
|
|
||||||
// [File System] A download task was unable to write to the downloaded file on disk.
|
|
||||||
// - [Disabled] File system error is unlikely to recover with retry.
|
|
||||||
// .cannotWriteToFile,
|
|
||||||
|
|
||||||
// [Security] A client certificate was rejected.
|
|
||||||
// - [Disabled] Client certificate is unlikely to change with retry.
|
|
||||||
// .clientCertificateRejected,
|
|
||||||
|
|
||||||
// [Security] A client certificate was required to authenticate an SSL connection during a request.
|
|
||||||
// - [Disabled] Client certificate is unlikely to be provided with retry.
|
|
||||||
// .clientCertificateRequired,
|
|
||||||
|
|
||||||
// [Data] The length of the resource data exceeds the maximum allowed.
|
|
||||||
// - [Disabled] Resource will likely still exceed the length maximum on retry.
|
|
||||||
// .dataLengthExceedsMaximum,
|
|
||||||
|
|
||||||
// [System] The cellular network disallowed a connection.
|
|
||||||
// - [Enabled] WiFi connection could be established during retry.
|
|
||||||
.dataNotAllowed,
|
|
||||||
|
|
||||||
// [Network] The host address could not be found via DNS lookup.
|
|
||||||
// - [Enabled] DNS lookup could succeed during retry.
|
|
||||||
.dnsLookupFailed,
|
|
||||||
|
|
||||||
// [Data] A download task failed to decode an encoded file during the download.
|
|
||||||
// - [Enabled] Server could correct the decoding issue with retry.
|
|
||||||
.downloadDecodingFailedMidStream,
|
|
||||||
|
|
||||||
// [Data] A download task failed to decode an encoded file after downloading.
|
|
||||||
// - [Enabled] Server could correct the decoding issue with retry.
|
|
||||||
.downloadDecodingFailedToComplete,
|
|
||||||
|
|
||||||
// [File System] A file does not exist.
|
|
||||||
// - [Disabled] File system error is unlikely to recover with retry.
|
|
||||||
// .fileDoesNotExist,
|
|
||||||
|
|
||||||
// [File System] A request for an FTP file resulted in the server responding that the file is not a plain file,
|
|
||||||
// but a directory.
|
|
||||||
// - [Disabled] FTP directory is not likely to change to a file during a retry.
|
|
||||||
// .fileIsDirectory,
|
|
||||||
|
|
||||||
// [Network] A redirect loop has been detected or the threshold for number of allowable redirects has been
|
|
||||||
// exceeded (currently 16).
|
|
||||||
// - [Disabled] The redirect loop is unlikely to be resolved within the retry window.
|
|
||||||
// .httpTooManyRedirects,
|
|
||||||
|
|
||||||
// [System] The attempted connection required activating a data context while roaming, but international roaming
|
|
||||||
// is disabled.
|
|
||||||
// - [Enabled] WiFi connection could be established during retry.
|
|
||||||
.internationalRoamingOff,
|
|
||||||
|
|
||||||
// [Connectivity] A client or server connection was severed in the middle of an in-progress load.
|
|
||||||
// - [Enabled] A network connection could be established during retry.
|
|
||||||
.networkConnectionLost,
|
|
||||||
|
|
||||||
// [File System] A resource couldn’t be read because of insufficient permissions.
|
|
||||||
// - [Disabled] Permissions are unlikely to be granted during retry.
|
|
||||||
// .noPermissionsToReadFile,
|
|
||||||
|
|
||||||
// [Connectivity] A network resource was requested, but an internet connection has not been established and
|
|
||||||
// cannot be established automatically.
|
|
||||||
// - [Enabled] A network connection could be established during retry.
|
|
||||||
.notConnectedToInternet,
|
|
||||||
|
|
||||||
// [Resource] A redirect was specified by way of server response code, but the server did not accompany this
|
|
||||||
// code with a redirect URL.
|
|
||||||
// - [Disabled] The redirect URL is unlikely to be supplied during a retry.
|
|
||||||
// .redirectToNonExistentLocation,
|
|
||||||
|
|
||||||
// [Client] A body stream is needed but the client did not provide one.
|
|
||||||
// - [Disabled] The client will be unlikely to supply a body stream during retry.
|
|
||||||
// .requestBodyStreamExhausted,
|
|
||||||
|
|
||||||
// [Resource] A requested resource couldn’t be retrieved.
|
|
||||||
// - [Disabled] The resource is unlikely to become available during the retry window.
|
|
||||||
// .resourceUnavailable,
|
|
||||||
|
|
||||||
// [Security] An attempt to establish a secure connection failed for reasons that can’t be expressed more
|
|
||||||
// specifically.
|
|
||||||
// - [Enabled] The secure connection could be established during a retry given the lack of specificity
|
|
||||||
// provided by the error.
|
|
||||||
.secureConnectionFailed,
|
|
||||||
|
|
||||||
// [Security] A server certificate had a date which indicates it has expired, or is not yet valid.
|
|
||||||
// - [Enabled] The server certificate could become valid within the retry window.
|
|
||||||
.serverCertificateHasBadDate,
|
|
||||||
|
|
||||||
// [Security] A server certificate was not signed by any root server.
|
|
||||||
// - [Disabled] The server certificate is unlikely to change during the retry window.
|
|
||||||
// .serverCertificateHasUnknownRoot,
|
|
||||||
|
|
||||||
// [Security] A server certificate is not yet valid.
|
|
||||||
// - [Enabled] The server certificate could become valid within the retry window.
|
|
||||||
.serverCertificateNotYetValid,
|
|
||||||
|
|
||||||
// [Security] A server certificate was signed by a root server that isn’t trusted.
|
|
||||||
// - [Disabled] The server certificate is unlikely to become trusted within the retry window.
|
|
||||||
// .serverCertificateUntrusted,
|
|
||||||
|
|
||||||
// [Network] An asynchronous operation timed out.
|
|
||||||
// - [Enabled] The request timed out for an unknown reason and should be retried.
|
|
||||||
.timedOut
|
|
||||||
|
|
||||||
// [System] The URL Loading System encountered an error that it can’t interpret.
|
|
||||||
// - [Disabled] The error could not be interpreted and is unlikely to be recovered from during a retry.
|
|
||||||
// .unknown,
|
|
||||||
|
|
||||||
// [Resource] A properly formed URL couldn’t be handled by the framework.
|
|
||||||
// - [Disabled] The URL is unlikely to change during a retry.
|
|
||||||
// .unsupportedURL,
|
|
||||||
|
|
||||||
// [Client] Authentication is required to access a resource.
|
|
||||||
// - [Disabled] The user authentication is unlikely to be provided by retrying.
|
|
||||||
// .userAuthenticationRequired,
|
|
||||||
|
|
||||||
// [Client] An asynchronous request for authentication has been canceled by the user.
|
|
||||||
// - [Disabled] The user cancelled authentication and explicitly took action to not retry.
|
|
||||||
// .userCancelledAuthentication,
|
|
||||||
|
|
||||||
// [Resource] A server reported that a URL has a non-zero content length, but terminated the network connection
|
|
||||||
// gracefully without sending any data.
|
|
||||||
// - [Disabled] The server is unlikely to provide data during the retry window.
|
|
||||||
// .zeroByteResource,
|
|
||||||
]
|
|
||||||
|
|
||||||
/// The total number of times the request is allowed to be retried.
|
|
||||||
public let retryLimit: UInt
|
|
||||||
|
|
||||||
/// The base of the exponential backoff policy (should always be greater than or equal to 2).
|
|
||||||
public let exponentialBackoffBase: UInt
|
|
||||||
|
|
||||||
/// The scale of the exponential backoff.
|
|
||||||
public let exponentialBackoffScale: Double
|
|
||||||
|
|
||||||
/// The HTTP methods that are allowed to be retried.
|
|
||||||
public let retryableHTTPMethods: Set<HTTPMethod>
|
|
||||||
|
|
||||||
/// The HTTP status codes that are automatically retried by the policy.
|
|
||||||
public let retryableHTTPStatusCodes: Set<Int>
|
|
||||||
|
|
||||||
/// The URL error codes that are automatically retried by the policy.
|
|
||||||
public let retryableURLErrorCodes: Set<URLError.Code>
|
|
||||||
|
|
||||||
/// Creates a `RetryPolicy` from the specified parameters.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - retryLimit: The total number of times the request is allowed to be retried. `2` by default.
|
|
||||||
/// - exponentialBackoffBase: The base of the exponential backoff policy. `2` by default.
|
|
||||||
/// - exponentialBackoffScale: The scale of the exponential backoff. `0.5` by default.
|
|
||||||
/// - retryableHTTPMethods: The HTTP methods that are allowed to be retried.
|
|
||||||
/// `RetryPolicy.defaultRetryableHTTPMethods` by default.
|
|
||||||
/// - retryableHTTPStatusCodes: The HTTP status codes that are automatically retried by the policy.
|
|
||||||
/// `RetryPolicy.defaultRetryableHTTPStatusCodes` by default.
|
|
||||||
/// - retryableURLErrorCodes: The URL error codes that are automatically retried by the policy.
|
|
||||||
/// `RetryPolicy.defaultRetryableURLErrorCodes` by default.
|
|
||||||
public init(retryLimit: UInt = RetryPolicy.defaultRetryLimit,
|
|
||||||
exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase,
|
|
||||||
exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale,
|
|
||||||
retryableHTTPMethods: Set<HTTPMethod> = RetryPolicy.defaultRetryableHTTPMethods,
|
|
||||||
retryableHTTPStatusCodes: Set<Int> = RetryPolicy.defaultRetryableHTTPStatusCodes,
|
|
||||||
retryableURLErrorCodes: Set<URLError.Code> = RetryPolicy.defaultRetryableURLErrorCodes) {
|
|
||||||
precondition(exponentialBackoffBase >= 2, "The `exponentialBackoffBase` must be a minimum of 2.")
|
|
||||||
|
|
||||||
self.retryLimit = retryLimit
|
|
||||||
self.exponentialBackoffBase = exponentialBackoffBase
|
|
||||||
self.exponentialBackoffScale = exponentialBackoffScale
|
|
||||||
self.retryableHTTPMethods = retryableHTTPMethods
|
|
||||||
self.retryableHTTPStatusCodes = retryableHTTPStatusCodes
|
|
||||||
self.retryableURLErrorCodes = retryableURLErrorCodes
|
|
||||||
}
|
|
||||||
|
|
||||||
open func retry(_ request: Request,
|
|
||||||
for session: Session,
|
|
||||||
dueTo error: Error,
|
|
||||||
completion: @escaping (RetryResult) -> Void) {
|
|
||||||
if request.retryCount < retryLimit, shouldRetry(request: request, dueTo: error) {
|
|
||||||
completion(.retryWithDelay(pow(Double(exponentialBackoffBase), Double(request.retryCount)) * exponentialBackoffScale))
|
|
||||||
} else {
|
|
||||||
completion(.doNotRetry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines whether or not to retry the provided `Request`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - request: `Request` that failed due to the provided `Error`.
|
|
||||||
/// - error: `Error` encountered while executing the `Request`.
|
|
||||||
///
|
|
||||||
/// - Returns: `Bool` determining whether or not to retry the `Request`.
|
|
||||||
open func shouldRetry(request: Request, dueTo error: Error) -> Bool {
|
|
||||||
guard let httpMethod = request.request?.method, retryableHTTPMethods.contains(httpMethod) else { return false }
|
|
||||||
|
|
||||||
if let statusCode = request.response?.statusCode, retryableHTTPStatusCodes.contains(statusCode) {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
let errorCode = (error as? URLError)?.code
|
|
||||||
let afErrorCode = (error.asAFError?.underlyingError as? URLError)?.code
|
|
||||||
|
|
||||||
guard let code = errorCode ?? afErrorCode else { return false }
|
|
||||||
|
|
||||||
return retryableURLErrorCodes.contains(code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RequestInterceptor where Self == RetryPolicy {
|
|
||||||
/// Provides a default `RetryPolicy` instance.
|
|
||||||
public static var retryPolicy: RetryPolicy { RetryPolicy() }
|
|
||||||
|
|
||||||
/// Creates an `RetryPolicy` from the specified parameters.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - retryLimit: The total number of times the request is allowed to be retried. `2` by default.
|
|
||||||
/// - exponentialBackoffBase: The base of the exponential backoff policy. `2` by default.
|
|
||||||
/// - exponentialBackoffScale: The scale of the exponential backoff. `0.5` by default.
|
|
||||||
/// - retryableHTTPMethods: The HTTP methods that are allowed to be retried.
|
|
||||||
/// `RetryPolicy.defaultRetryableHTTPMethods` by default.
|
|
||||||
/// - retryableHTTPStatusCodes: The HTTP status codes that are automatically retried by the policy.
|
|
||||||
/// `RetryPolicy.defaultRetryableHTTPStatusCodes` by default.
|
|
||||||
/// - retryableURLErrorCodes: The URL error codes that are automatically retried by the policy.
|
|
||||||
/// `RetryPolicy.defaultRetryableURLErrorCodes` by default.
|
|
||||||
///
|
|
||||||
/// - Returns: The `RetryPolicy`
|
|
||||||
public static func retryPolicy(retryLimit: UInt = RetryPolicy.defaultRetryLimit,
|
|
||||||
exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase,
|
|
||||||
exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale,
|
|
||||||
retryableHTTPMethods: Set<HTTPMethod> = RetryPolicy.defaultRetryableHTTPMethods,
|
|
||||||
retryableHTTPStatusCodes: Set<Int> = RetryPolicy.defaultRetryableHTTPStatusCodes,
|
|
||||||
retryableURLErrorCodes: Set<URLError.Code> = RetryPolicy.defaultRetryableURLErrorCodes) -> RetryPolicy {
|
|
||||||
RetryPolicy(retryLimit: retryLimit,
|
|
||||||
exponentialBackoffBase: exponentialBackoffBase,
|
|
||||||
exponentialBackoffScale: exponentialBackoffScale,
|
|
||||||
retryableHTTPMethods: retryableHTTPMethods,
|
|
||||||
retryableHTTPStatusCodes: retryableHTTPStatusCodes,
|
|
||||||
retryableURLErrorCodes: retryableURLErrorCodes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// A retry policy that automatically retries idempotent requests for network connection lost errors. For more
|
|
||||||
/// information about retrying network connection lost errors, please refer to Apple's
|
|
||||||
/// [technical document](https://developer.apple.com/library/content/qa/qa1941/_index.html).
|
|
||||||
open class ConnectionLostRetryPolicy: RetryPolicy {
|
|
||||||
/// Creates a `ConnectionLostRetryPolicy` instance from the specified parameters.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - retryLimit: The total number of times the request is allowed to be retried.
|
|
||||||
/// `RetryPolicy.defaultRetryLimit` by default.
|
|
||||||
/// - exponentialBackoffBase: The base of the exponential backoff policy.
|
|
||||||
/// `RetryPolicy.defaultExponentialBackoffBase` by default.
|
|
||||||
/// - exponentialBackoffScale: The scale of the exponential backoff.
|
|
||||||
/// `RetryPolicy.defaultExponentialBackoffScale` by default.
|
|
||||||
/// - retryableHTTPMethods: The idempotent http methods to retry.
|
|
||||||
/// `RetryPolicy.defaultRetryableHTTPMethods` by default.
|
|
||||||
public init(retryLimit: UInt = RetryPolicy.defaultRetryLimit,
|
|
||||||
exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase,
|
|
||||||
exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale,
|
|
||||||
retryableHTTPMethods: Set<HTTPMethod> = RetryPolicy.defaultRetryableHTTPMethods) {
|
|
||||||
super.init(retryLimit: retryLimit,
|
|
||||||
exponentialBackoffBase: exponentialBackoffBase,
|
|
||||||
exponentialBackoffScale: exponentialBackoffScale,
|
|
||||||
retryableHTTPMethods: retryableHTTPMethods,
|
|
||||||
retryableHTTPStatusCodes: [],
|
|
||||||
retryableURLErrorCodes: [.networkConnectionLost])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RequestInterceptor where Self == ConnectionLostRetryPolicy {
|
|
||||||
/// Provides a default `ConnectionLostRetryPolicy` instance.
|
|
||||||
public static var connectionLostRetryPolicy: ConnectionLostRetryPolicy { ConnectionLostRetryPolicy() }
|
|
||||||
|
|
||||||
/// Creates a `ConnectionLostRetryPolicy` instance from the specified parameters.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - retryLimit: The total number of times the request is allowed to be retried.
|
|
||||||
/// `RetryPolicy.defaultRetryLimit` by default.
|
|
||||||
/// - exponentialBackoffBase: The base of the exponential backoff policy.
|
|
||||||
/// `RetryPolicy.defaultExponentialBackoffBase` by default.
|
|
||||||
/// - exponentialBackoffScale: The scale of the exponential backoff.
|
|
||||||
/// `RetryPolicy.defaultExponentialBackoffScale` by default.
|
|
||||||
/// - retryableHTTPMethods: The idempotent http methods to retry.
|
|
||||||
///
|
|
||||||
/// - Returns: The `ConnectionLostRetryPolicy`.
|
|
||||||
public static func connectionLostRetryPolicy(retryLimit: UInt = RetryPolicy.defaultRetryLimit,
|
|
||||||
exponentialBackoffBase: UInt = RetryPolicy.defaultExponentialBackoffBase,
|
|
||||||
exponentialBackoffScale: Double = RetryPolicy.defaultExponentialBackoffScale,
|
|
||||||
retryableHTTPMethods: Set<HTTPMethod> = RetryPolicy.defaultRetryableHTTPMethods) -> ConnectionLostRetryPolicy {
|
|
||||||
ConnectionLostRetryPolicy(retryLimit: retryLimit,
|
|
||||||
exponentialBackoffBase: exponentialBackoffBase,
|
|
||||||
exponentialBackoffScale: exponentialBackoffScale,
|
|
||||||
retryableHTTPMethods: retryableHTTPMethods)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,772 +0,0 @@
|
|||||||
//
|
|
||||||
// ServerTrustEvaluation.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Responsible for managing the mapping of `ServerTrustEvaluating` values to given hosts.
|
|
||||||
open class ServerTrustManager {
|
|
||||||
/// Determines whether all hosts for this `ServerTrustManager` must be evaluated. `true` by default.
|
|
||||||
public let allHostsMustBeEvaluated: Bool
|
|
||||||
|
|
||||||
/// The dictionary of policies mapped to a particular host.
|
|
||||||
public let evaluators: [String: ServerTrustEvaluating]
|
|
||||||
|
|
||||||
/// Initializes the `ServerTrustManager` instance with the given evaluators.
|
|
||||||
///
|
|
||||||
/// Since different servers and web services can have different leaf certificates, intermediate and even root
|
|
||||||
/// certificates, it is important to have the flexibility to specify evaluation policies on a per host basis. This
|
|
||||||
/// allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key
|
|
||||||
/// pinning for host3 and disabling evaluation for host4.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - allHostsMustBeEvaluated: The value determining whether all hosts for this instance must be evaluated. `true`
|
|
||||||
/// by default.
|
|
||||||
/// - evaluators: A dictionary of evaluators mapped to hosts.
|
|
||||||
public init(allHostsMustBeEvaluated: Bool = true, evaluators: [String: ServerTrustEvaluating]) {
|
|
||||||
self.allHostsMustBeEvaluated = allHostsMustBeEvaluated
|
|
||||||
self.evaluators = evaluators
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(Security)
|
|
||||||
/// Returns the `ServerTrustEvaluating` value for the given host, if one is set.
|
|
||||||
///
|
|
||||||
/// By default, this method will return the policy that perfectly matches the given host. Subclasses could override
|
|
||||||
/// this method and implement more complex mapping implementations such as wildcards.
|
|
||||||
///
|
|
||||||
/// - Parameter host: The host to use when searching for a matching policy.
|
|
||||||
///
|
|
||||||
/// - Returns: The `ServerTrustEvaluating` value for the given host if found, `nil` otherwise.
|
|
||||||
/// - Throws: `AFError.serverTrustEvaluationFailed` if `allHostsMustBeEvaluated` is `true` and no matching
|
|
||||||
/// evaluators are found.
|
|
||||||
open func serverTrustEvaluator(forHost host: String) throws -> ServerTrustEvaluating? {
|
|
||||||
guard let evaluator = evaluators[host] else {
|
|
||||||
if allHostsMustBeEvaluated {
|
|
||||||
throw AFError.serverTrustEvaluationFailed(reason: .noRequiredEvaluator(host: host))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return evaluator
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A protocol describing the API used to evaluate server trusts.
|
|
||||||
public protocol ServerTrustEvaluating {
|
|
||||||
#if !canImport(Security)
|
|
||||||
// Implement this once other platforms have API for evaluating server trusts.
|
|
||||||
#else
|
|
||||||
/// Evaluates the given `SecTrust` value for the given `host`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - trust: The `SecTrust` value to evaluate.
|
|
||||||
/// - host: The host for which to evaluate the `SecTrust` value.
|
|
||||||
///
|
|
||||||
/// - Returns: A `Bool` indicating whether the evaluator considers the `SecTrust` value valid for `host`.
|
|
||||||
func evaluate(_ trust: SecTrust, forHost host: String) throws
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Server Trust Evaluators
|
|
||||||
|
|
||||||
#if canImport(Security)
|
|
||||||
/// An evaluator which uses the default server trust evaluation while allowing you to control whether to validate the
|
|
||||||
/// host provided by the challenge. Applications are encouraged to always validate the host in production environments
|
|
||||||
/// to guarantee the validity of the server's certificate chain.
|
|
||||||
public final class DefaultTrustEvaluator: ServerTrustEvaluating {
|
|
||||||
private let validateHost: Bool
|
|
||||||
|
|
||||||
/// Creates a `DefaultTrustEvaluator`.
|
|
||||||
///
|
|
||||||
/// - Parameter validateHost: Determines whether or not the evaluator should validate the host. `true` by default.
|
|
||||||
public init(validateHost: Bool = true) {
|
|
||||||
self.validateHost = validateHost
|
|
||||||
}
|
|
||||||
|
|
||||||
public func evaluate(_ trust: SecTrust, forHost host: String) throws {
|
|
||||||
if validateHost {
|
|
||||||
try trust.af.performValidation(forHost: host)
|
|
||||||
}
|
|
||||||
|
|
||||||
try trust.af.performDefaultValidation(forHost: host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An evaluator which Uses the default and revoked server trust evaluations allowing you to control whether to validate
|
|
||||||
/// the host provided by the challenge as well as specify the revocation flags for testing for revoked certificates.
|
|
||||||
/// Apple platforms did not start testing for revoked certificates automatically until iOS 10.1, macOS 10.12 and tvOS
|
|
||||||
/// 10.1 which is demonstrated in our TLS tests. Applications are encouraged to always validate the host in production
|
|
||||||
/// environments to guarantee the validity of the server's certificate chain.
|
|
||||||
public final class RevocationTrustEvaluator: ServerTrustEvaluating {
|
|
||||||
/// Represents the options to be use when evaluating the status of a certificate.
|
|
||||||
/// Only Revocation Policy Constants are valid, and can be found in [Apple's documentation](https://developer.apple.com/documentation/security/certificate_key_and_trust_services/policies/1563600-revocation_policy_constants).
|
|
||||||
public struct Options: OptionSet {
|
|
||||||
/// Perform revocation checking using the CRL (Certification Revocation List) method.
|
|
||||||
public static let crl = Options(rawValue: kSecRevocationCRLMethod)
|
|
||||||
/// Consult only locally cached replies; do not use network access.
|
|
||||||
public static let networkAccessDisabled = Options(rawValue: kSecRevocationNetworkAccessDisabled)
|
|
||||||
/// Perform revocation checking using OCSP (Online Certificate Status Protocol).
|
|
||||||
public static let ocsp = Options(rawValue: kSecRevocationOCSPMethod)
|
|
||||||
/// Prefer CRL revocation checking over OCSP; by default, OCSP is preferred.
|
|
||||||
public static let preferCRL = Options(rawValue: kSecRevocationPreferCRL)
|
|
||||||
/// Require a positive response to pass the policy. If the flag is not set, revocation checking is done on a
|
|
||||||
/// "best attempt" basis, where failure to reach the server is not considered fatal.
|
|
||||||
public static let requirePositiveResponse = Options(rawValue: kSecRevocationRequirePositiveResponse)
|
|
||||||
/// Perform either OCSP or CRL checking. The checking is performed according to the method(s) specified in the
|
|
||||||
/// certificate and the value of `preferCRL`.
|
|
||||||
public static let any = Options(rawValue: kSecRevocationUseAnyAvailableMethod)
|
|
||||||
|
|
||||||
/// The raw value of the option.
|
|
||||||
public let rawValue: CFOptionFlags
|
|
||||||
|
|
||||||
/// Creates an `Options` value with the given `CFOptionFlags`.
|
|
||||||
///
|
|
||||||
/// - Parameter rawValue: The `CFOptionFlags` value to initialize with.
|
|
||||||
public init(rawValue: CFOptionFlags) {
|
|
||||||
self.rawValue = rawValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private let performDefaultValidation: Bool
|
|
||||||
private let validateHost: Bool
|
|
||||||
private let options: Options
|
|
||||||
|
|
||||||
/// Creates a `RevocationTrustEvaluator` using the provided parameters.
|
|
||||||
///
|
|
||||||
/// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
|
|
||||||
/// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - performDefaultValidation: Determines whether default validation should be performed in addition to
|
|
||||||
/// evaluating the pinned certificates. `true` by default.
|
|
||||||
/// - validateHost: Determines whether or not the evaluator should validate the host, in addition to
|
|
||||||
/// performing the default evaluation, even if `performDefaultValidation` is `false`.
|
|
||||||
/// `true` by default.
|
|
||||||
/// - options: The `Options` to use to check the revocation status of the certificate. `.any` by
|
|
||||||
/// default.
|
|
||||||
public init(performDefaultValidation: Bool = true, validateHost: Bool = true, options: Options = .any) {
|
|
||||||
self.performDefaultValidation = performDefaultValidation
|
|
||||||
self.validateHost = validateHost
|
|
||||||
self.options = options
|
|
||||||
}
|
|
||||||
|
|
||||||
public func evaluate(_ trust: SecTrust, forHost host: String) throws {
|
|
||||||
if performDefaultValidation {
|
|
||||||
try trust.af.performDefaultValidation(forHost: host)
|
|
||||||
}
|
|
||||||
|
|
||||||
if validateHost {
|
|
||||||
try trust.af.performValidation(forHost: host)
|
|
||||||
}
|
|
||||||
|
|
||||||
#if swift(>=5.9)
|
|
||||||
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, visionOS 1, *) {
|
|
||||||
try trust.af.evaluate(afterApplying: SecPolicy.af.revocation(options: options))
|
|
||||||
} else {
|
|
||||||
try trust.af.validate(policy: SecPolicy.af.revocation(options: options)) { status, result in
|
|
||||||
AFError.serverTrustEvaluationFailed(reason: .revocationCheckFailed(output: .init(host, trust, status, result), options: options))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) {
|
|
||||||
try trust.af.evaluate(afterApplying: SecPolicy.af.revocation(options: options))
|
|
||||||
} else {
|
|
||||||
try trust.af.validate(policy: SecPolicy.af.revocation(options: options)) { status, result in
|
|
||||||
AFError.serverTrustEvaluationFailed(reason: .revocationCheckFailed(output: .init(host, trust, status, result), options: options))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ServerTrustEvaluating where Self == RevocationTrustEvaluator {
|
|
||||||
/// Provides a default `RevocationTrustEvaluator` instance.
|
|
||||||
public static var revocationChecking: RevocationTrustEvaluator { RevocationTrustEvaluator() }
|
|
||||||
|
|
||||||
/// Creates a `RevocationTrustEvaluator` using the provided parameters.
|
|
||||||
///
|
|
||||||
/// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
|
|
||||||
/// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - performDefaultValidation: Determines whether default validation should be performed in addition to
|
|
||||||
/// evaluating the pinned certificates. `true` by default.
|
|
||||||
/// - validateHost: Determines whether or not the evaluator should validate the host, in addition
|
|
||||||
/// to performing the default evaluation, even if `performDefaultValidation` is
|
|
||||||
/// `false`. `true` by default.
|
|
||||||
/// - options: The `Options` to use to check the revocation status of the certificate. `.any`
|
|
||||||
/// by default.
|
|
||||||
/// - Returns: The `RevocationTrustEvaluator`.
|
|
||||||
public static func revocationChecking(performDefaultValidation: Bool = true,
|
|
||||||
validateHost: Bool = true,
|
|
||||||
options: RevocationTrustEvaluator.Options = .any) -> RevocationTrustEvaluator {
|
|
||||||
RevocationTrustEvaluator(performDefaultValidation: performDefaultValidation,
|
|
||||||
validateHost: validateHost,
|
|
||||||
options: options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Uses the pinned certificates to validate the server trust. The server trust is considered valid if one of the pinned
|
|
||||||
/// certificates match one of the server certificates. By validating both the certificate chain and host, certificate
|
|
||||||
/// pinning provides a very secure form of server trust validation mitigating most, if not all, MITM attacks.
|
|
||||||
/// Applications are encouraged to always validate the host and require a valid certificate chain in production
|
|
||||||
/// environments.
|
|
||||||
public final class PinnedCertificatesTrustEvaluator: ServerTrustEvaluating {
|
|
||||||
private let certificates: [SecCertificate]
|
|
||||||
private let acceptSelfSignedCertificates: Bool
|
|
||||||
private let performDefaultValidation: Bool
|
|
||||||
private let validateHost: Bool
|
|
||||||
|
|
||||||
/// Creates a `PinnedCertificatesTrustEvaluator` from the provided parameters.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - certificates: The certificates to use to evaluate the trust. All `cer`, `crt`, and `der`
|
|
||||||
/// certificates in `Bundle.main` by default.
|
|
||||||
/// - acceptSelfSignedCertificates: Adds the provided certificates as anchors for the trust evaluation, allowing
|
|
||||||
/// self-signed certificates to pass. `false` by default. THIS SETTING SHOULD BE
|
|
||||||
/// FALSE IN PRODUCTION!
|
|
||||||
/// - performDefaultValidation: Determines whether default validation should be performed in addition to
|
|
||||||
/// evaluating the pinned certificates. `true` by default.
|
|
||||||
/// - validateHost: Determines whether or not the evaluator should validate the host, in addition
|
|
||||||
/// to performing the default evaluation, even if `performDefaultValidation` is
|
|
||||||
/// `false`. `true` by default.
|
|
||||||
public init(certificates: [SecCertificate] = Bundle.main.af.certificates,
|
|
||||||
acceptSelfSignedCertificates: Bool = false,
|
|
||||||
performDefaultValidation: Bool = true,
|
|
||||||
validateHost: Bool = true) {
|
|
||||||
self.certificates = certificates
|
|
||||||
self.acceptSelfSignedCertificates = acceptSelfSignedCertificates
|
|
||||||
self.performDefaultValidation = performDefaultValidation
|
|
||||||
self.validateHost = validateHost
|
|
||||||
}
|
|
||||||
|
|
||||||
public func evaluate(_ trust: SecTrust, forHost host: String) throws {
|
|
||||||
guard !certificates.isEmpty else {
|
|
||||||
throw AFError.serverTrustEvaluationFailed(reason: .noCertificatesFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
if acceptSelfSignedCertificates {
|
|
||||||
try trust.af.setAnchorCertificates(certificates)
|
|
||||||
}
|
|
||||||
|
|
||||||
if performDefaultValidation {
|
|
||||||
try trust.af.performDefaultValidation(forHost: host)
|
|
||||||
}
|
|
||||||
|
|
||||||
if validateHost {
|
|
||||||
try trust.af.performValidation(forHost: host)
|
|
||||||
}
|
|
||||||
|
|
||||||
let serverCertificatesData = Set(trust.af.certificateData)
|
|
||||||
let pinnedCertificatesData = Set(certificates.af.data)
|
|
||||||
let pinnedCertificatesInServerData = !serverCertificatesData.isDisjoint(with: pinnedCertificatesData)
|
|
||||||
if !pinnedCertificatesInServerData {
|
|
||||||
throw AFError.serverTrustEvaluationFailed(reason: .certificatePinningFailed(host: host,
|
|
||||||
trust: trust,
|
|
||||||
pinnedCertificates: certificates,
|
|
||||||
serverCertificates: trust.af.certificates))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ServerTrustEvaluating where Self == PinnedCertificatesTrustEvaluator {
|
|
||||||
/// Provides a default `PinnedCertificatesTrustEvaluator` instance.
|
|
||||||
public static var pinnedCertificates: PinnedCertificatesTrustEvaluator { PinnedCertificatesTrustEvaluator() }
|
|
||||||
|
|
||||||
/// Creates a `PinnedCertificatesTrustEvaluator` using the provided parameters.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - certificates: The certificates to use to evaluate the trust. All `cer`, `crt`, and `der`
|
|
||||||
/// certificates in `Bundle.main` by default.
|
|
||||||
/// - acceptSelfSignedCertificates: Adds the provided certificates as anchors for the trust evaluation, allowing
|
|
||||||
/// self-signed certificates to pass. `false` by default. THIS SETTING SHOULD BE
|
|
||||||
/// FALSE IN PRODUCTION!
|
|
||||||
/// - performDefaultValidation: Determines whether default validation should be performed in addition to
|
|
||||||
/// evaluating the pinned certificates. `true` by default.
|
|
||||||
/// - validateHost: Determines whether or not the evaluator should validate the host, in addition
|
|
||||||
/// to performing the default evaluation, even if `performDefaultValidation` is
|
|
||||||
/// `false`. `true` by default.
|
|
||||||
public static func pinnedCertificates(certificates: [SecCertificate] = Bundle.main.af.certificates,
|
|
||||||
acceptSelfSignedCertificates: Bool = false,
|
|
||||||
performDefaultValidation: Bool = true,
|
|
||||||
validateHost: Bool = true) -> PinnedCertificatesTrustEvaluator {
|
|
||||||
PinnedCertificatesTrustEvaluator(certificates: certificates,
|
|
||||||
acceptSelfSignedCertificates: acceptSelfSignedCertificates,
|
|
||||||
performDefaultValidation: performDefaultValidation,
|
|
||||||
validateHost: validateHost)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Uses the pinned public keys to validate the server trust. The server trust is considered valid if one of the pinned
|
|
||||||
/// public keys match one of the server certificate public keys. By validating both the certificate chain and host,
|
|
||||||
/// public key pinning provides a very secure form of server trust validation mitigating most, if not all, MITM attacks.
|
|
||||||
/// Applications are encouraged to always validate the host and require a valid certificate chain in production
|
|
||||||
/// environments.
|
|
||||||
public final class PublicKeysTrustEvaluator: ServerTrustEvaluating {
|
|
||||||
private let keys: [SecKey]
|
|
||||||
private let performDefaultValidation: Bool
|
|
||||||
private let validateHost: Bool
|
|
||||||
|
|
||||||
/// Creates a `PublicKeysTrustEvaluator` from the provided parameters.
|
|
||||||
///
|
|
||||||
/// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
|
|
||||||
/// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - keys: The `SecKey`s to use to validate public keys. Defaults to the public keys of all
|
|
||||||
/// certificates included in the main bundle.
|
|
||||||
/// - performDefaultValidation: Determines whether default validation should be performed in addition to
|
|
||||||
/// evaluating the pinned certificates. `true` by default.
|
|
||||||
/// - validateHost: Determines whether or not the evaluator should validate the host, in addition to
|
|
||||||
/// performing the default evaluation, even if `performDefaultValidation` is `false`.
|
|
||||||
/// `true` by default.
|
|
||||||
public init(keys: [SecKey] = Bundle.main.af.publicKeys,
|
|
||||||
performDefaultValidation: Bool = true,
|
|
||||||
validateHost: Bool = true) {
|
|
||||||
self.keys = keys
|
|
||||||
self.performDefaultValidation = performDefaultValidation
|
|
||||||
self.validateHost = validateHost
|
|
||||||
}
|
|
||||||
|
|
||||||
public func evaluate(_ trust: SecTrust, forHost host: String) throws {
|
|
||||||
guard !keys.isEmpty else {
|
|
||||||
throw AFError.serverTrustEvaluationFailed(reason: .noPublicKeysFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
if performDefaultValidation {
|
|
||||||
try trust.af.performDefaultValidation(forHost: host)
|
|
||||||
}
|
|
||||||
|
|
||||||
if validateHost {
|
|
||||||
try trust.af.performValidation(forHost: host)
|
|
||||||
}
|
|
||||||
|
|
||||||
let pinnedKeysInServerKeys: Bool = {
|
|
||||||
for serverPublicKey in trust.af.publicKeys {
|
|
||||||
if keys.contains(serverPublicKey) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}()
|
|
||||||
|
|
||||||
if !pinnedKeysInServerKeys {
|
|
||||||
throw AFError.serverTrustEvaluationFailed(reason: .publicKeyPinningFailed(host: host,
|
|
||||||
trust: trust,
|
|
||||||
pinnedKeys: keys,
|
|
||||||
serverKeys: trust.af.publicKeys))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ServerTrustEvaluating where Self == PublicKeysTrustEvaluator {
|
|
||||||
/// Provides a default `PublicKeysTrustEvaluator` instance.
|
|
||||||
public static var publicKeys: PublicKeysTrustEvaluator { PublicKeysTrustEvaluator() }
|
|
||||||
|
|
||||||
/// Creates a `PublicKeysTrustEvaluator` from the provided parameters.
|
|
||||||
///
|
|
||||||
/// - Note: Default and host validation will fail when using this evaluator with self-signed certificates. Use
|
|
||||||
/// `PinnedCertificatesTrustEvaluator` if you need to use self-signed certificates.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - keys: The `SecKey`s to use to validate public keys. Defaults to the public keys of all
|
|
||||||
/// certificates included in the main bundle.
|
|
||||||
/// - performDefaultValidation: Determines whether default validation should be performed in addition to
|
|
||||||
/// evaluating the pinned certificates. `true` by default.
|
|
||||||
/// - validateHost: Determines whether or not the evaluator should validate the host, in addition to
|
|
||||||
/// performing the default evaluation, even if `performDefaultValidation` is `false`.
|
|
||||||
/// `true` by default.
|
|
||||||
public static func publicKeys(keys: [SecKey] = Bundle.main.af.publicKeys,
|
|
||||||
performDefaultValidation: Bool = true,
|
|
||||||
validateHost: Bool = true) -> PublicKeysTrustEvaluator {
|
|
||||||
PublicKeysTrustEvaluator(keys: keys, performDefaultValidation: performDefaultValidation, validateHost: validateHost)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Uses the provided evaluators to validate the server trust. The trust is only considered valid if all of the
|
|
||||||
/// evaluators consider it valid.
|
|
||||||
public final class CompositeTrustEvaluator: ServerTrustEvaluating {
|
|
||||||
private let evaluators: [ServerTrustEvaluating]
|
|
||||||
|
|
||||||
/// Creates a `CompositeTrustEvaluator` from the provided evaluators.
|
|
||||||
///
|
|
||||||
/// - Parameter evaluators: The `ServerTrustEvaluating` values used to evaluate the server trust.
|
|
||||||
public init(evaluators: [ServerTrustEvaluating]) {
|
|
||||||
self.evaluators = evaluators
|
|
||||||
}
|
|
||||||
|
|
||||||
public func evaluate(_ trust: SecTrust, forHost host: String) throws {
|
|
||||||
try evaluators.evaluate(trust, forHost: host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ServerTrustEvaluating where Self == CompositeTrustEvaluator {
|
|
||||||
/// Creates a `CompositeTrustEvaluator` from the provided evaluators.
|
|
||||||
///
|
|
||||||
/// - Parameter evaluators: The `ServerTrustEvaluating` values used to evaluate the server trust.
|
|
||||||
public static func composite(evaluators: [ServerTrustEvaluating]) -> CompositeTrustEvaluator {
|
|
||||||
CompositeTrustEvaluator(evaluators: evaluators)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disables all evaluation which in turn will always consider any server trust as valid.
|
|
||||||
///
|
|
||||||
/// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test
|
|
||||||
/// certificates, as outlined in [this Apple tech note](https://developer.apple.com/library/archive/qa/qa1948/_index.html).
|
|
||||||
///
|
|
||||||
/// **THIS EVALUATOR SHOULD NEVER BE USED IN PRODUCTION!**
|
|
||||||
@available(*, deprecated, renamed: "DisabledTrustEvaluator", message: "DisabledEvaluator has been renamed DisabledTrustEvaluator.")
|
|
||||||
public typealias DisabledEvaluator = DisabledTrustEvaluator
|
|
||||||
|
|
||||||
/// Disables all evaluation which in turn will always consider any server trust as valid.
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// - Note: Instead of disabling server trust evaluation, it's a better idea to configure systems to properly trust test
|
|
||||||
/// certificates, as outlined in [this Apple tech note](https://developer.apple.com/library/archive/qa/qa1948/_index.html).
|
|
||||||
///
|
|
||||||
/// **THIS EVALUATOR SHOULD NEVER BE USED IN PRODUCTION!**
|
|
||||||
public final class DisabledTrustEvaluator: ServerTrustEvaluating {
|
|
||||||
/// Creates an instance.
|
|
||||||
public init() {}
|
|
||||||
|
|
||||||
public func evaluate(_ trust: SecTrust, forHost host: String) throws {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Extensions
|
|
||||||
|
|
||||||
extension Array where Element == ServerTrustEvaluating {
|
|
||||||
#if os(Linux) || os(Windows) || os(Android)
|
|
||||||
// Add this same convenience method for Linux/Windows.
|
|
||||||
#else
|
|
||||||
/// Evaluates the given `SecTrust` value for the given `host`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - trust: The `SecTrust` value to evaluate.
|
|
||||||
/// - host: The host for which to evaluate the `SecTrust` value.
|
|
||||||
///
|
|
||||||
/// - Returns: Whether or not the evaluator considers the `SecTrust` value valid for `host`.
|
|
||||||
public func evaluate(_ trust: SecTrust, forHost host: String) throws {
|
|
||||||
for evaluator in self {
|
|
||||||
try evaluator.evaluate(trust, forHost: host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Bundle: AlamofireExtended {}
|
|
||||||
extension AlamofireExtension where ExtendedType: Bundle {
|
|
||||||
/// Returns all valid `cer`, `crt`, and `der` certificates in the bundle.
|
|
||||||
public var certificates: [SecCertificate] {
|
|
||||||
paths(forResourcesOfTypes: [".cer", ".CER", ".crt", ".CRT", ".der", ".DER"]).compactMap { path in
|
|
||||||
guard
|
|
||||||
let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData,
|
|
||||||
let certificate = SecCertificateCreateWithData(nil, certificateData) else { return nil }
|
|
||||||
|
|
||||||
return certificate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all public keys for the valid certificates in the bundle.
|
|
||||||
public var publicKeys: [SecKey] {
|
|
||||||
certificates.af.publicKeys
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all pathnames for the resources identified by the provided file extensions.
|
|
||||||
///
|
|
||||||
/// - Parameter types: The filename extensions locate.
|
|
||||||
///
|
|
||||||
/// - Returns: All pathnames for the given filename extensions.
|
|
||||||
public func paths(forResourcesOfTypes types: [String]) -> [String] {
|
|
||||||
Array(Set(types.flatMap { type.paths(forResourcesOfType: $0, inDirectory: nil) }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SecTrust: AlamofireExtended {}
|
|
||||||
extension AlamofireExtension where ExtendedType == SecTrust {
|
|
||||||
/// Evaluates `self` after applying the `SecPolicy` value provided.
|
|
||||||
///
|
|
||||||
/// - Parameter policy: The `SecPolicy` to apply to `self` before evaluation.
|
|
||||||
///
|
|
||||||
/// - Throws: Any `Error` from applying the `SecPolicy` or from evaluation.
|
|
||||||
@available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *)
|
|
||||||
public func evaluate(afterApplying policy: SecPolicy) throws {
|
|
||||||
try apply(policy: policy).af.evaluate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to validate `self` using the `SecPolicy` provided and transforming any error produced using the closure passed.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - policy: The `SecPolicy` used to evaluate `self`.
|
|
||||||
/// - errorProducer: The closure used transform the failed `OSStatus` and `SecTrustResultType`.
|
|
||||||
/// - Throws: Any `Error` from applying the `policy`, or the result of `errorProducer` if validation fails.
|
|
||||||
@available(iOS, introduced: 10, deprecated: 12, renamed: "evaluate(afterApplying:)")
|
|
||||||
@available(macOS, introduced: 10.12, deprecated: 10.14, renamed: "evaluate(afterApplying:)")
|
|
||||||
@available(tvOS, introduced: 10, deprecated: 12, renamed: "evaluate(afterApplying:)")
|
|
||||||
@available(watchOS, introduced: 3, deprecated: 5, renamed: "evaluate(afterApplying:)")
|
|
||||||
public func validate(policy: SecPolicy, errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws {
|
|
||||||
try apply(policy: policy).af.validate(errorProducer: errorProducer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Applies a `SecPolicy` to `self`, throwing if it fails.
|
|
||||||
///
|
|
||||||
/// - Parameter policy: The `SecPolicy`.
|
|
||||||
///
|
|
||||||
/// - Returns: `self`, with the policy applied.
|
|
||||||
/// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.policyApplicationFailed` reason.
|
|
||||||
public func apply(policy: SecPolicy) throws -> SecTrust {
|
|
||||||
let status = SecTrustSetPolicies(type, policy)
|
|
||||||
|
|
||||||
guard status.af.isSuccess else {
|
|
||||||
throw AFError.serverTrustEvaluationFailed(reason: .policyApplicationFailed(trust: type,
|
|
||||||
policy: policy,
|
|
||||||
status: status))
|
|
||||||
}
|
|
||||||
|
|
||||||
return type
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate `self`, throwing an `Error` if evaluation fails.
|
|
||||||
///
|
|
||||||
/// - Throws: `AFError.serverTrustEvaluationFailed` with reason `.trustValidationFailed` and associated error from
|
|
||||||
/// the underlying evaluation.
|
|
||||||
@available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *)
|
|
||||||
public func evaluate() throws {
|
|
||||||
var error: CFError?
|
|
||||||
let evaluationSucceeded = SecTrustEvaluateWithError(type, &error)
|
|
||||||
|
|
||||||
if !evaluationSucceeded {
|
|
||||||
throw AFError.serverTrustEvaluationFailed(reason: .trustEvaluationFailed(error: error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validate `self`, passing any failure values through `errorProducer`.
|
|
||||||
///
|
|
||||||
/// - Parameter errorProducer: The closure used to transform the failed `OSStatus` and `SecTrustResultType` into an
|
|
||||||
/// `Error`.
|
|
||||||
/// - Throws: The `Error` produced by the `errorProducer` closure.
|
|
||||||
@available(iOS, introduced: 10, deprecated: 12, renamed: "evaluate()")
|
|
||||||
@available(macOS, introduced: 10.12, deprecated: 10.14, renamed: "evaluate()")
|
|
||||||
@available(tvOS, introduced: 10, deprecated: 12, renamed: "evaluate()")
|
|
||||||
@available(watchOS, introduced: 3, deprecated: 5, renamed: "evaluate()")
|
|
||||||
public func validate(errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws {
|
|
||||||
var result = SecTrustResultType.invalid
|
|
||||||
let status = SecTrustEvaluate(type, &result)
|
|
||||||
|
|
||||||
guard status.af.isSuccess && result.af.isSuccess else {
|
|
||||||
throw errorProducer(status, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets a custom certificate chain on `self`, allowing full validation of a self-signed certificate and its chain.
|
|
||||||
///
|
|
||||||
/// - Parameter certificates: The `SecCertificate`s to add to the chain.
|
|
||||||
/// - Throws: Any error produced when applying the new certificate chain.
|
|
||||||
public func setAnchorCertificates(_ certificates: [SecCertificate]) throws {
|
|
||||||
// Add additional anchor certificates.
|
|
||||||
let status = SecTrustSetAnchorCertificates(type, certificates as CFArray)
|
|
||||||
guard status.af.isSuccess else {
|
|
||||||
throw AFError.serverTrustEvaluationFailed(reason: .settingAnchorCertificatesFailed(status: status,
|
|
||||||
certificates: certificates))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trust only the set anchor certs.
|
|
||||||
let onlyStatus = SecTrustSetAnchorCertificatesOnly(type, true)
|
|
||||||
guard onlyStatus.af.isSuccess else {
|
|
||||||
throw AFError.serverTrustEvaluationFailed(reason: .settingAnchorCertificatesFailed(status: onlyStatus,
|
|
||||||
certificates: certificates))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The public keys contained in `self`.
|
|
||||||
public var publicKeys: [SecKey] {
|
|
||||||
certificates.af.publicKeys
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `SecCertificate`s contained in `self`.
|
|
||||||
public var certificates: [SecCertificate] {
|
|
||||||
#if swift(>=5.9)
|
|
||||||
if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, visionOS 1, *) {
|
|
||||||
return (SecTrustCopyCertificateChain(type) as? [SecCertificate]) ?? []
|
|
||||||
} else {
|
|
||||||
return (0..<SecTrustGetCertificateCount(type)).compactMap { index in
|
|
||||||
SecTrustGetCertificateAtIndex(type, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elseif swift(>=5.5.1) // Xcode 13.1 / 2021 SDKs.
|
|
||||||
if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) {
|
|
||||||
return (SecTrustCopyCertificateChain(type) as? [SecCertificate]) ?? []
|
|
||||||
} else {
|
|
||||||
return (0..<SecTrustGetCertificateCount(type)).compactMap { index in
|
|
||||||
SecTrustGetCertificateAtIndex(type, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
(0..<SecTrustGetCertificateCount(type)).compactMap { index in
|
|
||||||
SecTrustGetCertificateAtIndex(type, index)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `Data` values for all certificates contained in `self`.
|
|
||||||
public var certificateData: [Data] {
|
|
||||||
certificates.af.data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates `self` after applying `SecPolicy.af.default`. This evaluation does not validate the hostname.
|
|
||||||
///
|
|
||||||
/// - Parameter host: The hostname, used only in the error output if validation fails.
|
|
||||||
/// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.defaultEvaluationFailed` reason.
|
|
||||||
public func performDefaultValidation(forHost host: String) throws {
|
|
||||||
#if swift(>=5.9)
|
|
||||||
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, visionOS 1, *) {
|
|
||||||
try evaluate(afterApplying: SecPolicy.af.default)
|
|
||||||
} else {
|
|
||||||
try validate(policy: SecPolicy.af.default) { status, result in
|
|
||||||
AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, type, status, result)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) {
|
|
||||||
try evaluate(afterApplying: SecPolicy.af.default)
|
|
||||||
} else {
|
|
||||||
try validate(policy: SecPolicy.af.default) { status, result in
|
|
||||||
AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, type, status, result)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates `self` after applying `SecPolicy.af.hostname(host)`, which performs the default validation as well as
|
|
||||||
/// hostname validation.
|
|
||||||
///
|
|
||||||
/// - Parameter host: The hostname to use in the validation.
|
|
||||||
/// - Throws: An `AFError.serverTrustEvaluationFailed` instance with a `.defaultEvaluationFailed` reason.
|
|
||||||
public func performValidation(forHost host: String) throws {
|
|
||||||
#if swift(>=5.9)
|
|
||||||
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, visionOS 1, *) {
|
|
||||||
try evaluate(afterApplying: SecPolicy.af.hostname(host))
|
|
||||||
} else {
|
|
||||||
try validate(policy: SecPolicy.af.hostname(host)) { status, result in
|
|
||||||
AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, type, status, result)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if #available(iOS 12, macOS 10.14, tvOS 12, watchOS 5, *) {
|
|
||||||
try evaluate(afterApplying: SecPolicy.af.hostname(host))
|
|
||||||
} else {
|
|
||||||
try validate(policy: SecPolicy.af.hostname(host)) { status, result in
|
|
||||||
AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, type, status, result)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SecPolicy: AlamofireExtended {}
|
|
||||||
extension AlamofireExtension where ExtendedType == SecPolicy {
|
|
||||||
/// Creates a `SecPolicy` instance which will validate server certificates but not require a host name match.
|
|
||||||
public static let `default` = SecPolicyCreateSSL(true, nil)
|
|
||||||
|
|
||||||
/// Creates a `SecPolicy` instance which will validate server certificates and much match the provided hostname.
|
|
||||||
///
|
|
||||||
/// - Parameter hostname: The hostname to validate against.
|
|
||||||
///
|
|
||||||
/// - Returns: The `SecPolicy`.
|
|
||||||
public static func hostname(_ hostname: String) -> SecPolicy {
|
|
||||||
SecPolicyCreateSSL(true, hostname as CFString)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a `SecPolicy` which checks the revocation of certificates.
|
|
||||||
///
|
|
||||||
/// - Parameter options: The `RevocationTrustEvaluator.Options` for evaluation.
|
|
||||||
///
|
|
||||||
/// - Returns: The `SecPolicy`.
|
|
||||||
/// - Throws: An `AFError.serverTrustEvaluationFailed` error with reason `.revocationPolicyCreationFailed`
|
|
||||||
/// if the policy cannot be created.
|
|
||||||
public static func revocation(options: RevocationTrustEvaluator.Options) throws -> SecPolicy {
|
|
||||||
guard let policy = SecPolicyCreateRevocation(options.rawValue) else {
|
|
||||||
throw AFError.serverTrustEvaluationFailed(reason: .revocationPolicyCreationFailed)
|
|
||||||
}
|
|
||||||
|
|
||||||
return policy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Array: AlamofireExtended {}
|
|
||||||
extension AlamofireExtension where ExtendedType == [SecCertificate] {
|
|
||||||
/// All `Data` values for the contained `SecCertificate`s.
|
|
||||||
public var data: [Data] {
|
|
||||||
type.map { SecCertificateCopyData($0) as Data }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All public `SecKey` values for the contained `SecCertificate`s.
|
|
||||||
public var publicKeys: [SecKey] {
|
|
||||||
type.compactMap(\.af.publicKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SecCertificate: AlamofireExtended {}
|
|
||||||
extension AlamofireExtension where ExtendedType == SecCertificate {
|
|
||||||
/// The public key for `self`, if it can be extracted.
|
|
||||||
///
|
|
||||||
/// - Note: On 2020 OSes and newer, only RSA and ECDSA keys are supported.
|
|
||||||
///
|
|
||||||
public var publicKey: SecKey? {
|
|
||||||
let policy = SecPolicyCreateBasicX509()
|
|
||||||
var trust: SecTrust?
|
|
||||||
let trustCreationStatus = SecTrustCreateWithCertificates(type, policy, &trust)
|
|
||||||
|
|
||||||
guard let createdTrust = trust, trustCreationStatus == errSecSuccess else { return nil }
|
|
||||||
|
|
||||||
#if swift(>=5.9)
|
|
||||||
if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, visionOS 1, *) {
|
|
||||||
return SecTrustCopyKey(createdTrust)
|
|
||||||
} else {
|
|
||||||
return SecTrustCopyPublicKey(createdTrust)
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if #available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) {
|
|
||||||
return SecTrustCopyKey(createdTrust)
|
|
||||||
} else {
|
|
||||||
return SecTrustCopyPublicKey(createdTrust)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension OSStatus: AlamofireExtended {}
|
|
||||||
extension AlamofireExtension where ExtendedType == OSStatus {
|
|
||||||
/// Returns whether `self` is `errSecSuccess`.
|
|
||||||
public var isSuccess: Bool { type == errSecSuccess }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension SecTrustResultType: AlamofireExtended {}
|
|
||||||
extension AlamofireExtension where ExtendedType == SecTrustResultType {
|
|
||||||
/// Returns whether `self is `.unspecified` or `.proceed`.
|
|
||||||
public var isSuccess: Bool {
|
|
||||||
type == .unspecified || type == .proceed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
1264
SwiftProject/Pods/Alamofire/Source/Session.swift
generated
1264
SwiftProject/Pods/Alamofire/Source/Session.swift
generated
File diff suppressed because it is too large
Load Diff
355
SwiftProject/Pods/Alamofire/Source/SessionDelegate.swift
generated
355
SwiftProject/Pods/Alamofire/Source/SessionDelegate.swift
generated
@ -1,355 +0,0 @@
|
|||||||
//
|
|
||||||
// SessionDelegate.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Class which implements the various `URLSessionDelegate` methods to connect various Alamofire features.
|
|
||||||
open class SessionDelegate: NSObject {
|
|
||||||
private let fileManager: FileManager
|
|
||||||
|
|
||||||
weak var stateProvider: SessionStateProvider?
|
|
||||||
var eventMonitor: EventMonitor?
|
|
||||||
|
|
||||||
/// Creates an instance from the given `FileManager`.
|
|
||||||
///
|
|
||||||
/// - Parameter fileManager: `FileManager` to use for underlying file management, such as moving downloaded files.
|
|
||||||
/// `.default` by default.
|
|
||||||
public init(fileManager: FileManager = .default) {
|
|
||||||
self.fileManager = fileManager
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal method to find and cast requests while maintaining some integrity checking.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - task: The `URLSessionTask` for which to find the associated `Request`.
|
|
||||||
/// - type: The `Request` subclass type to cast any `Request` associate with `task`.
|
|
||||||
func request<R: Request>(for task: URLSessionTask, as type: R.Type) -> R? {
|
|
||||||
guard let provider = stateProvider else {
|
|
||||||
assertionFailure("StateProvider is nil.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return provider.request(for: task) as? R
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type which provides various `Session` state values.
|
|
||||||
protocol SessionStateProvider: AnyObject {
|
|
||||||
var serverTrustManager: ServerTrustManager? { get }
|
|
||||||
var redirectHandler: RedirectHandler? { get }
|
|
||||||
var cachedResponseHandler: CachedResponseHandler? { get }
|
|
||||||
|
|
||||||
func request(for task: URLSessionTask) -> Request?
|
|
||||||
func didGatherMetricsForTask(_ task: URLSessionTask)
|
|
||||||
func didCompleteTask(_ task: URLSessionTask, completion: @escaping () -> Void)
|
|
||||||
func credential(for task: URLSessionTask, in protectionSpace: URLProtectionSpace) -> URLCredential?
|
|
||||||
func cancelRequestsForSessionInvalidation(with error: Error?)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: URLSessionDelegate
|
|
||||||
|
|
||||||
extension SessionDelegate: URLSessionDelegate {
|
|
||||||
open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
|
|
||||||
eventMonitor?.urlSession(session, didBecomeInvalidWithError: error)
|
|
||||||
|
|
||||||
stateProvider?.cancelRequestsForSessionInvalidation(with: error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: URLSessionTaskDelegate
|
|
||||||
|
|
||||||
extension SessionDelegate: URLSessionTaskDelegate {
|
|
||||||
/// Result of a `URLAuthenticationChallenge` evaluation.
|
|
||||||
typealias ChallengeEvaluation = (disposition: URLSession.AuthChallengeDisposition, credential: URLCredential?, error: AFError?)
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
didReceive challenge: URLAuthenticationChallenge,
|
|
||||||
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
|
||||||
eventMonitor?.urlSession(session, task: task, didReceive: challenge)
|
|
||||||
|
|
||||||
let evaluation: ChallengeEvaluation
|
|
||||||
switch challenge.protectionSpace.authenticationMethod {
|
|
||||||
case NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest, NSURLAuthenticationMethodNTLM,
|
|
||||||
NSURLAuthenticationMethodNegotiate:
|
|
||||||
evaluation = attemptCredentialAuthentication(for: challenge, belongingTo: task)
|
|
||||||
#if canImport(Security)
|
|
||||||
case NSURLAuthenticationMethodServerTrust:
|
|
||||||
evaluation = attemptServerTrustAuthentication(with: challenge)
|
|
||||||
case NSURLAuthenticationMethodClientCertificate:
|
|
||||||
evaluation = attemptCredentialAuthentication(for: challenge, belongingTo: task)
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
evaluation = (.performDefaultHandling, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let error = evaluation.error {
|
|
||||||
stateProvider?.request(for: task)?.didFailTask(task, earlyWithError: error)
|
|
||||||
}
|
|
||||||
|
|
||||||
completionHandler(evaluation.disposition, evaluation.credential)
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(Security)
|
|
||||||
/// Evaluates the server trust `URLAuthenticationChallenge` received.
|
|
||||||
///
|
|
||||||
/// - Parameter challenge: The `URLAuthenticationChallenge`.
|
|
||||||
///
|
|
||||||
/// - Returns: The `ChallengeEvaluation`.
|
|
||||||
func attemptServerTrustAuthentication(with challenge: URLAuthenticationChallenge) -> ChallengeEvaluation {
|
|
||||||
let host = challenge.protectionSpace.host
|
|
||||||
|
|
||||||
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
|
|
||||||
let trust = challenge.protectionSpace.serverTrust
|
|
||||||
else {
|
|
||||||
return (.performDefaultHandling, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
guard let evaluator = try stateProvider?.serverTrustManager?.serverTrustEvaluator(forHost: host) else {
|
|
||||||
return (.performDefaultHandling, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
try evaluator.evaluate(trust, forHost: host)
|
|
||||||
|
|
||||||
return (.useCredential, URLCredential(trust: trust), nil)
|
|
||||||
} catch {
|
|
||||||
return (.cancelAuthenticationChallenge, nil, error.asAFError(or: .serverTrustEvaluationFailed(reason: .customEvaluationFailed(error: error))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Evaluates the credential-based authentication `URLAuthenticationChallenge` received for `task`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - challenge: The `URLAuthenticationChallenge`.
|
|
||||||
/// - task: The `URLSessionTask` which received the challenge.
|
|
||||||
///
|
|
||||||
/// - Returns: The `ChallengeEvaluation`.
|
|
||||||
func attemptCredentialAuthentication(for challenge: URLAuthenticationChallenge,
|
|
||||||
belongingTo task: URLSessionTask) -> ChallengeEvaluation {
|
|
||||||
guard challenge.previousFailureCount == 0 else {
|
|
||||||
return (.rejectProtectionSpace, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let credential = stateProvider?.credential(for: task, in: challenge.protectionSpace) else {
|
|
||||||
return (.performDefaultHandling, nil, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (.useCredential, credential, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
didSendBodyData bytesSent: Int64,
|
|
||||||
totalBytesSent: Int64,
|
|
||||||
totalBytesExpectedToSend: Int64) {
|
|
||||||
eventMonitor?.urlSession(session,
|
|
||||||
task: task,
|
|
||||||
didSendBodyData: bytesSent,
|
|
||||||
totalBytesSent: totalBytesSent,
|
|
||||||
totalBytesExpectedToSend: totalBytesExpectedToSend)
|
|
||||||
|
|
||||||
stateProvider?.request(for: task)?.updateUploadProgress(totalBytesSent: totalBytesSent,
|
|
||||||
totalBytesExpectedToSend: totalBytesExpectedToSend)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) {
|
|
||||||
eventMonitor?.urlSession(session, taskNeedsNewBodyStream: task)
|
|
||||||
|
|
||||||
guard let request = request(for: task, as: UploadRequest.self) else {
|
|
||||||
assertionFailure("needNewBodyStream did not find UploadRequest.")
|
|
||||||
completionHandler(nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
completionHandler(request.inputStream())
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
task: URLSessionTask,
|
|
||||||
willPerformHTTPRedirection response: HTTPURLResponse,
|
|
||||||
newRequest request: URLRequest,
|
|
||||||
completionHandler: @escaping (URLRequest?) -> Void) {
|
|
||||||
eventMonitor?.urlSession(session, task: task, willPerformHTTPRedirection: response, newRequest: request)
|
|
||||||
|
|
||||||
if let redirectHandler = stateProvider?.request(for: task)?.redirectHandler ?? stateProvider?.redirectHandler {
|
|
||||||
redirectHandler.task(task, willBeRedirectedTo: request, for: response, completion: completionHandler)
|
|
||||||
} else {
|
|
||||||
completionHandler(request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
|
|
||||||
eventMonitor?.urlSession(session, task: task, didFinishCollecting: metrics)
|
|
||||||
|
|
||||||
stateProvider?.request(for: task)?.didGatherMetrics(metrics)
|
|
||||||
|
|
||||||
stateProvider?.didGatherMetricsForTask(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
|
||||||
eventMonitor?.urlSession(session, task: task, didCompleteWithError: error)
|
|
||||||
|
|
||||||
let request = stateProvider?.request(for: task)
|
|
||||||
|
|
||||||
stateProvider?.didCompleteTask(task) {
|
|
||||||
request?.didCompleteTask(task, with: error.map { $0.asAFError(or: .sessionTaskFailed(error: $0)) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
|
|
||||||
open func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {
|
|
||||||
eventMonitor?.urlSession(session, taskIsWaitingForConnectivity: task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: URLSessionDataDelegate
|
|
||||||
|
|
||||||
extension SessionDelegate: URLSessionDataDelegate {
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
dataTask: URLSessionDataTask,
|
|
||||||
didReceive response: URLResponse,
|
|
||||||
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
|
|
||||||
eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: response)
|
|
||||||
|
|
||||||
guard let response = response as? HTTPURLResponse else { completionHandler(.allow); return }
|
|
||||||
|
|
||||||
if let request = request(for: dataTask, as: DataRequest.self) {
|
|
||||||
request.didReceiveResponse(response, completionHandler: completionHandler)
|
|
||||||
} else if let request = request(for: dataTask, as: DataStreamRequest.self) {
|
|
||||||
request.didReceiveResponse(response, completionHandler: completionHandler)
|
|
||||||
} else {
|
|
||||||
assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive response")
|
|
||||||
completionHandler(.allow)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
|
||||||
eventMonitor?.urlSession(session, dataTask: dataTask, didReceive: data)
|
|
||||||
|
|
||||||
if let request = request(for: dataTask, as: DataRequest.self) {
|
|
||||||
request.didReceive(data: data)
|
|
||||||
} else if let request = request(for: dataTask, as: DataStreamRequest.self) {
|
|
||||||
request.didReceive(data: data)
|
|
||||||
} else {
|
|
||||||
assertionFailure("dataTask did not find DataRequest or DataStreamRequest in didReceive data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
dataTask: URLSessionDataTask,
|
|
||||||
willCacheResponse proposedResponse: CachedURLResponse,
|
|
||||||
completionHandler: @escaping (CachedURLResponse?) -> Void) {
|
|
||||||
eventMonitor?.urlSession(session, dataTask: dataTask, willCacheResponse: proposedResponse)
|
|
||||||
|
|
||||||
if let handler = stateProvider?.request(for: dataTask)?.cachedResponseHandler ?? stateProvider?.cachedResponseHandler {
|
|
||||||
handler.dataTask(dataTask, willCacheResponse: proposedResponse, completion: completionHandler)
|
|
||||||
} else {
|
|
||||||
completionHandler(proposedResponse)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: URLSessionDownloadDelegate
|
|
||||||
|
|
||||||
extension SessionDelegate: URLSessionDownloadDelegate {
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didResumeAtOffset fileOffset: Int64,
|
|
||||||
expectedTotalBytes: Int64) {
|
|
||||||
eventMonitor?.urlSession(session,
|
|
||||||
downloadTask: downloadTask,
|
|
||||||
didResumeAtOffset: fileOffset,
|
|
||||||
expectedTotalBytes: expectedTotalBytes)
|
|
||||||
guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else {
|
|
||||||
assertionFailure("downloadTask did not find DownloadRequest.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadRequest.updateDownloadProgress(bytesWritten: fileOffset,
|
|
||||||
totalBytesExpectedToWrite: expectedTotalBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession,
|
|
||||||
downloadTask: URLSessionDownloadTask,
|
|
||||||
didWriteData bytesWritten: Int64,
|
|
||||||
totalBytesWritten: Int64,
|
|
||||||
totalBytesExpectedToWrite: Int64) {
|
|
||||||
eventMonitor?.urlSession(session,
|
|
||||||
downloadTask: downloadTask,
|
|
||||||
didWriteData: bytesWritten,
|
|
||||||
totalBytesWritten: totalBytesWritten,
|
|
||||||
totalBytesExpectedToWrite: totalBytesExpectedToWrite)
|
|
||||||
guard let downloadRequest = request(for: downloadTask, as: DownloadRequest.self) else {
|
|
||||||
assertionFailure("downloadTask did not find DownloadRequest.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadRequest.updateDownloadProgress(bytesWritten: bytesWritten,
|
|
||||||
totalBytesExpectedToWrite: totalBytesExpectedToWrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
open func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
|
|
||||||
eventMonitor?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
|
|
||||||
|
|
||||||
guard let request = request(for: downloadTask, as: DownloadRequest.self) else {
|
|
||||||
assertionFailure("downloadTask did not find DownloadRequest.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let (destination, options): (URL, DownloadRequest.Options)
|
|
||||||
if let response = request.response {
|
|
||||||
(destination, options) = request.destination(location, response)
|
|
||||||
} else {
|
|
||||||
// If there's no response this is likely a local file download, so generate the temporary URL directly.
|
|
||||||
(destination, options) = (DownloadRequest.defaultDestinationURL(location), [])
|
|
||||||
}
|
|
||||||
|
|
||||||
eventMonitor?.request(request, didCreateDestinationURL: destination)
|
|
||||||
|
|
||||||
do {
|
|
||||||
if options.contains(.removePreviousFile), fileManager.fileExists(atPath: destination.path) {
|
|
||||||
try fileManager.removeItem(at: destination)
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.contains(.createIntermediateDirectories) {
|
|
||||||
let directory = destination.deletingLastPathComponent()
|
|
||||||
try fileManager.createDirectory(at: directory, withIntermediateDirectories: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
try fileManager.moveItem(at: location, to: destination)
|
|
||||||
|
|
||||||
request.didFinishDownloading(using: downloadTask, with: .success(destination))
|
|
||||||
} catch {
|
|
||||||
request.didFinishDownloading(using: downloadTask, with: .failure(.downloadedFileMoveFailed(error: error,
|
|
||||||
source: location,
|
|
||||||
destination: destination)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
//
|
|
||||||
// StringEncoding+Alamofire.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension String.Encoding {
|
|
||||||
/// Creates an encoding from the IANA charset name.
|
|
||||||
///
|
|
||||||
/// - Notes: These mappings match those [provided by CoreFoundation](https://opensource.apple.com/source/CF/CF-476.18/CFStringUtilities.c.auto.html)
|
|
||||||
///
|
|
||||||
/// - Parameter name: IANA charset name.
|
|
||||||
init?(ianaCharsetName name: String) {
|
|
||||||
switch name.lowercased() {
|
|
||||||
case "utf-8":
|
|
||||||
self = .utf8
|
|
||||||
case "iso-8859-1":
|
|
||||||
self = .isoLatin1
|
|
||||||
case "unicode-1-1", "iso-10646-ucs-2", "utf-16":
|
|
||||||
self = .utf16
|
|
||||||
case "utf-16be":
|
|
||||||
self = .utf16BigEndian
|
|
||||||
case "utf-16le":
|
|
||||||
self = .utf16LittleEndian
|
|
||||||
case "utf-32":
|
|
||||||
self = .utf32
|
|
||||||
case "utf-32be":
|
|
||||||
self = .utf32BigEndian
|
|
||||||
case "utf-32le":
|
|
||||||
self = .utf32LittleEndian
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
//
|
|
||||||
// URLConvertible+URLRequestConvertible.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Types adopting the `URLConvertible` protocol can be used to construct `URL`s, which can then be used to construct
|
|
||||||
/// `URLRequests`.
|
|
||||||
public protocol URLConvertible {
|
|
||||||
/// Returns a `URL` from the conforming instance or throws.
|
|
||||||
///
|
|
||||||
/// - Returns: The `URL` created from the instance.
|
|
||||||
/// - Throws: Any error thrown while creating the `URL`.
|
|
||||||
func asURL() throws -> URL
|
|
||||||
}
|
|
||||||
|
|
||||||
extension String: URLConvertible {
|
|
||||||
/// Returns a `URL` if `self` can be used to initialize a `URL` instance, otherwise throws.
|
|
||||||
///
|
|
||||||
/// - Returns: The `URL` initialized with `self`.
|
|
||||||
/// - Throws: An `AFError.invalidURL` instance.
|
|
||||||
public func asURL() throws -> URL {
|
|
||||||
guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) }
|
|
||||||
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension URL: URLConvertible {
|
|
||||||
/// Returns `self`.
|
|
||||||
public func asURL() throws -> URL { self }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension URLComponents: URLConvertible {
|
|
||||||
/// Returns a `URL` if the `self`'s `url` is not nil, otherwise throws.
|
|
||||||
///
|
|
||||||
/// - Returns: The `URL` from the `url` property.
|
|
||||||
/// - Throws: An `AFError.invalidURL` instance.
|
|
||||||
public func asURL() throws -> URL {
|
|
||||||
guard let url = url else { throw AFError.invalidURL(url: self) }
|
|
||||||
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
/// Types adopting the `URLRequestConvertible` protocol can be used to safely construct `URLRequest`s.
|
|
||||||
public protocol URLRequestConvertible {
|
|
||||||
/// Returns a `URLRequest` or throws if an `Error` was encountered.
|
|
||||||
///
|
|
||||||
/// - Returns: A `URLRequest`.
|
|
||||||
/// - Throws: Any error thrown while constructing the `URLRequest`.
|
|
||||||
func asURLRequest() throws -> URLRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
extension URLRequestConvertible {
|
|
||||||
/// The `URLRequest` returned by discarding any `Error` encountered.
|
|
||||||
public var urlRequest: URLRequest? { try? asURLRequest() }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension URLRequest: URLRequestConvertible {
|
|
||||||
/// Returns `self`.
|
|
||||||
public func asURLRequest() throws -> URLRequest { self }
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension URLRequest {
|
|
||||||
/// Creates an instance with the specified `url`, `method`, and `headers`.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - url: The `URLConvertible` value.
|
|
||||||
/// - method: The `HTTPMethod`.
|
|
||||||
/// - headers: The `HTTPHeaders`, `nil` by default.
|
|
||||||
/// - Throws: Any error thrown while converting the `URLConvertible` to a `URL`.
|
|
||||||
public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws {
|
|
||||||
let url = try url.asURL()
|
|
||||||
|
|
||||||
self.init(url: url)
|
|
||||||
|
|
||||||
httpMethod = method.rawValue
|
|
||||||
allHTTPHeaderFields = headers?.dictionary
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,39 +0,0 @@
|
|||||||
//
|
|
||||||
// URLRequest+Alamofire.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2019 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension URLRequest {
|
|
||||||
/// Returns the `httpMethod` as Alamofire's `HTTPMethod` type.
|
|
||||||
public var method: HTTPMethod? {
|
|
||||||
get { httpMethod.map(HTTPMethod.init) }
|
|
||||||
set { httpMethod = newValue?.rawValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
public func validate() throws {
|
|
||||||
if method == .get, let bodyData = httpBody {
|
|
||||||
throw AFError.urlRequestValidationFailed(reason: .bodyDataInGETRequest(bodyData))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
//
|
|
||||||
// URLSessionConfiguration+Alamofire.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension URLSessionConfiguration: AlamofireExtended {}
|
|
||||||
extension AlamofireExtension where ExtendedType: URLSessionConfiguration {
|
|
||||||
/// Alamofire's default configuration. Same as `URLSessionConfiguration.default` but adds Alamofire default
|
|
||||||
/// `Accept-Language`, `Accept-Encoding`, and `User-Agent` headers.
|
|
||||||
public static var `default`: URLSessionConfiguration {
|
|
||||||
let configuration = URLSessionConfiguration.default
|
|
||||||
configuration.headers = .default
|
|
||||||
|
|
||||||
return configuration
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `.ephemeral` configuration with Alamofire's default `Accept-Language`, `Accept-Encoding`, and `User-Agent`
|
|
||||||
/// headers.
|
|
||||||
public static var ephemeral: URLSessionConfiguration {
|
|
||||||
let configuration = URLSessionConfiguration.ephemeral
|
|
||||||
configuration.headers = .default
|
|
||||||
|
|
||||||
return configuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
302
SwiftProject/Pods/Alamofire/Source/Validation.swift
generated
302
SwiftProject/Pods/Alamofire/Source/Validation.swift
generated
@ -1,302 +0,0 @@
|
|||||||
//
|
|
||||||
// Validation.swift
|
|
||||||
//
|
|
||||||
// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension Request {
|
|
||||||
// MARK: Helper Types
|
|
||||||
|
|
||||||
fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason
|
|
||||||
|
|
||||||
/// Used to represent whether a validation succeeded or failed.
|
|
||||||
public typealias ValidationResult = Result<Void, Error>
|
|
||||||
|
|
||||||
fileprivate struct MIMEType {
|
|
||||||
let type: String
|
|
||||||
let subtype: String
|
|
||||||
|
|
||||||
var isWildcard: Bool { type == "*" && subtype == "*" }
|
|
||||||
|
|
||||||
init?(_ string: String) {
|
|
||||||
let components: [String] = {
|
|
||||||
let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)]
|
|
||||||
|
|
||||||
return split.components(separatedBy: "/")
|
|
||||||
}()
|
|
||||||
|
|
||||||
if let type = components.first, let subtype = components.last {
|
|
||||||
self.type = type
|
|
||||||
self.subtype = subtype
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func matches(_ mime: MIMEType) -> Bool {
|
|
||||||
switch (type, subtype) {
|
|
||||||
case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"):
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Properties
|
|
||||||
|
|
||||||
fileprivate var acceptableStatusCodes: Range<Int> { 200..<300 }
|
|
||||||
|
|
||||||
fileprivate var acceptableContentTypes: [String] {
|
|
||||||
if let accept = request?.value(forHTTPHeaderField: "Accept") {
|
|
||||||
return accept.components(separatedBy: ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
return ["*/*"]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Status Code
|
|
||||||
|
|
||||||
fileprivate func validate<S: Sequence>(statusCode acceptableStatusCodes: S,
|
|
||||||
response: HTTPURLResponse)
|
|
||||||
-> ValidationResult
|
|
||||||
where S.Iterator.Element == Int {
|
|
||||||
if acceptableStatusCodes.contains(response.statusCode) {
|
|
||||||
return .success(())
|
|
||||||
} else {
|
|
||||||
let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode)
|
|
||||||
return .failure(AFError.responseValidationFailed(reason: reason))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Content Type
|
|
||||||
|
|
||||||
fileprivate func validate<S: Sequence>(contentType acceptableContentTypes: S,
|
|
||||||
response: HTTPURLResponse,
|
|
||||||
data: Data?)
|
|
||||||
-> ValidationResult
|
|
||||||
where S.Iterator.Element == String {
|
|
||||||
guard let data = data, !data.isEmpty else { return .success(()) }
|
|
||||||
|
|
||||||
return validate(contentType: acceptableContentTypes, response: response)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func validate<S: Sequence>(contentType acceptableContentTypes: S,
|
|
||||||
response: HTTPURLResponse)
|
|
||||||
-> ValidationResult
|
|
||||||
where S.Iterator.Element == String {
|
|
||||||
guard
|
|
||||||
let responseContentType = response.mimeType,
|
|
||||||
let responseMIMEType = MIMEType(responseContentType)
|
|
||||||
else {
|
|
||||||
for contentType in acceptableContentTypes {
|
|
||||||
if let mimeType = MIMEType(contentType), mimeType.isWildcard {
|
|
||||||
return .success(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let error: AFError = {
|
|
||||||
let reason: ErrorReason = .missingContentType(acceptableContentTypes: acceptableContentTypes.sorted())
|
|
||||||
return AFError.responseValidationFailed(reason: reason)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return .failure(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
for contentType in acceptableContentTypes {
|
|
||||||
if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) {
|
|
||||||
return .success(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let error: AFError = {
|
|
||||||
let reason: ErrorReason = .unacceptableContentType(acceptableContentTypes: acceptableContentTypes.sorted(),
|
|
||||||
responseContentType: responseContentType)
|
|
||||||
|
|
||||||
return AFError.responseValidationFailed(reason: reason)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return .failure(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension DataRequest {
|
|
||||||
/// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the
|
|
||||||
/// request was valid.
|
|
||||||
public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult
|
|
||||||
|
|
||||||
/// Validates that the response has a status code in the specified sequence.
|
|
||||||
///
|
|
||||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
|
||||||
///
|
|
||||||
/// - Parameter acceptableStatusCodes: `Sequence` of acceptable response status codes.
|
|
||||||
///
|
|
||||||
/// - Returns: The instance.
|
|
||||||
@discardableResult
|
|
||||||
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
|
|
||||||
validate { [unowned self] _, response, _ in
|
|
||||||
self.validate(statusCode: acceptableStatusCodes, response: response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates that the response has a content type in the specified sequence.
|
|
||||||
///
|
|
||||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
|
||||||
///
|
|
||||||
/// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
|
|
||||||
///
|
|
||||||
/// - returns: The request.
|
|
||||||
@discardableResult
|
|
||||||
public func validate<S: Sequence>(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String {
|
|
||||||
validate { [unowned self] _, response, data in
|
|
||||||
self.validate(contentType: acceptableContentTypes(), response: response, data: data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
|
|
||||||
/// type matches any specified in the Accept HTTP header field.
|
|
||||||
///
|
|
||||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
|
||||||
///
|
|
||||||
/// - returns: The request.
|
|
||||||
@discardableResult
|
|
||||||
public func validate() -> Self {
|
|
||||||
let contentTypes: () -> [String] = { [unowned self] in
|
|
||||||
acceptableContentTypes
|
|
||||||
}
|
|
||||||
return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension DataStreamRequest {
|
|
||||||
/// A closure used to validate a request that takes a `URLRequest` and `HTTPURLResponse` and returns whether the
|
|
||||||
/// request was valid.
|
|
||||||
public typealias Validation = (_ request: URLRequest?, _ response: HTTPURLResponse) -> ValidationResult
|
|
||||||
|
|
||||||
/// Validates that the response has a status code in the specified sequence.
|
|
||||||
///
|
|
||||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
|
||||||
///
|
|
||||||
/// - Parameter acceptableStatusCodes: `Sequence` of acceptable response status codes.
|
|
||||||
///
|
|
||||||
/// - Returns: The instance.
|
|
||||||
@discardableResult
|
|
||||||
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
|
|
||||||
validate { [unowned self] _, response in
|
|
||||||
self.validate(statusCode: acceptableStatusCodes, response: response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates that the response has a content type in the specified sequence.
|
|
||||||
///
|
|
||||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
|
||||||
///
|
|
||||||
/// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
|
|
||||||
///
|
|
||||||
/// - returns: The request.
|
|
||||||
@discardableResult
|
|
||||||
public func validate<S: Sequence>(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String {
|
|
||||||
validate { [unowned self] _, response in
|
|
||||||
self.validate(contentType: acceptableContentTypes(), response: response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
|
|
||||||
/// type matches any specified in the Accept HTTP header field.
|
|
||||||
///
|
|
||||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
|
||||||
///
|
|
||||||
/// - Returns: The instance.
|
|
||||||
@discardableResult
|
|
||||||
public func validate() -> Self {
|
|
||||||
let contentTypes: () -> [String] = { [unowned self] in
|
|
||||||
acceptableContentTypes
|
|
||||||
}
|
|
||||||
return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
extension DownloadRequest {
|
|
||||||
/// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a
|
|
||||||
/// destination URL, and returns whether the request was valid.
|
|
||||||
public typealias Validation = (_ request: URLRequest?,
|
|
||||||
_ response: HTTPURLResponse,
|
|
||||||
_ fileURL: URL?)
|
|
||||||
-> ValidationResult
|
|
||||||
|
|
||||||
/// Validates that the response has a status code in the specified sequence.
|
|
||||||
///
|
|
||||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
|
||||||
///
|
|
||||||
/// - Parameter acceptableStatusCodes: `Sequence` of acceptable response status codes.
|
|
||||||
///
|
|
||||||
/// - Returns: The instance.
|
|
||||||
@discardableResult
|
|
||||||
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
|
|
||||||
validate { [unowned self] _, response, _ in
|
|
||||||
self.validate(statusCode: acceptableStatusCodes, response: response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates that the response has a content type in the specified sequence.
|
|
||||||
///
|
|
||||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
|
||||||
///
|
|
||||||
/// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
|
|
||||||
///
|
|
||||||
/// - returns: The request.
|
|
||||||
@discardableResult
|
|
||||||
public func validate<S: Sequence>(contentType acceptableContentTypes: @escaping @autoclosure () -> S) -> Self where S.Iterator.Element == String {
|
|
||||||
validate { [unowned self] _, response, fileURL in
|
|
||||||
guard let validFileURL = fileURL else {
|
|
||||||
return .failure(AFError.responseValidationFailed(reason: .dataFileNil))
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
let data = try Data(contentsOf: validFileURL)
|
|
||||||
return self.validate(contentType: acceptableContentTypes(), response: response, data: data)
|
|
||||||
} catch {
|
|
||||||
return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
|
|
||||||
/// type matches any specified in the Accept HTTP header field.
|
|
||||||
///
|
|
||||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
|
||||||
///
|
|
||||||
/// - returns: The request.
|
|
||||||
@discardableResult
|
|
||||||
public func validate() -> Self {
|
|
||||||
let contentTypes = { [unowned self] in
|
|
||||||
acceptableContentTypes
|
|
||||||
}
|
|
||||||
return validate(statusCode: acceptableStatusCodes).validate(contentType: contentTypes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
6
SwiftProject/Pods/Manifest.lock
generated
6
SwiftProject/Pods/Manifest.lock
generated
@ -815,7 +815,6 @@ PODS:
|
|||||||
- abseil/base/base_internal
|
- abseil/base/base_internal
|
||||||
- abseil/base/config
|
- abseil/base/config
|
||||||
- abseil/meta/type_traits
|
- abseil/meta/type_traits
|
||||||
- Alamofire (5.8.1)
|
|
||||||
- BoringSSL-GRPC (0.0.32):
|
- BoringSSL-GRPC (0.0.32):
|
||||||
- BoringSSL-GRPC/Implementation (= 0.0.32)
|
- BoringSSL-GRPC/Implementation (= 0.0.32)
|
||||||
- BoringSSL-GRPC/Interface (= 0.0.32)
|
- BoringSSL-GRPC/Interface (= 0.0.32)
|
||||||
@ -1058,7 +1057,6 @@ PODS:
|
|||||||
- TZImagePickerController/Location (3.8.4)
|
- TZImagePickerController/Location (3.8.4)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Alamofire
|
|
||||||
- DeviceKit
|
- DeviceKit
|
||||||
- FacebookCore
|
- FacebookCore
|
||||||
- Firebase/AnalyticsWithoutAdIdSupport
|
- Firebase/AnalyticsWithoutAdIdSupport
|
||||||
@ -1076,7 +1074,6 @@ DEPENDENCIES:
|
|||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
- abseil
|
- abseil
|
||||||
- Alamofire
|
|
||||||
- BoringSSL-GRPC
|
- BoringSSL-GRPC
|
||||||
- DeviceKit
|
- DeviceKit
|
||||||
- FacebookCore
|
- FacebookCore
|
||||||
@ -1118,7 +1115,6 @@ SPEC REPOS:
|
|||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
abseil: ebec4f56469dd7ce9ab08683c0319a68aa0ad86e
|
abseil: ebec4f56469dd7ce9ab08683c0319a68aa0ad86e
|
||||||
Alamofire: 3ca42e259043ee0dc5c0cdd76c4bc568b8e42af7
|
|
||||||
BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6
|
BoringSSL-GRPC: 1e2348957acdbcad360b80a264a90799984b2ba6
|
||||||
DeviceKit: e36aaf2a0d142ef0b4fac2007649a4414af234be
|
DeviceKit: e36aaf2a0d142ef0b4fac2007649a4414af234be
|
||||||
FacebookCore: ba86524b66cfa86d0f8e65d08faa8504a9f732dd
|
FacebookCore: ba86524b66cfa86d0f8e65d08faa8504a9f732dd
|
||||||
@ -1158,6 +1154,6 @@ SPEC CHECKSUMS:
|
|||||||
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
|
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
|
||||||
TZImagePickerController: f1c9f1cae6ac0e30b31aaa9698f9bf4a7cf5b84f
|
TZImagePickerController: f1c9f1cae6ac0e30b31aaa9698f9bf4a7cf5b84f
|
||||||
|
|
||||||
PODFILE CHECKSUM: ff22192ba29fe62575b8eaf5e55d1e1892047ee0
|
PODFILE CHECKSUM: cdc4cc3bc05e95dd4ba77e4128af08fd8e18aa1f
|
||||||
|
|
||||||
COCOAPODS: 1.15.2
|
COCOAPODS: 1.15.2
|
||||||
|
|||||||
82202
SwiftProject/Pods/Pods.xcodeproj/project.pbxproj
generated
82202
SwiftProject/Pods/Pods.xcodeproj/project.pbxproj
generated
File diff suppressed because it is too large
Load Diff
@ -1,58 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1500"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "EAAA1AD3A8A1B59AB91319EE40752C6D"
|
|
||||||
BuildableName = "Alamofire.framework"
|
|
||||||
BlueprintName = "Alamofire"
|
|
||||||
ReferencedContainer = "container:Pods.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@ -4,11 +4,6 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>SchemeUserState</key>
|
<key>SchemeUserState</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>Alamofire.xcscheme</key>
|
|
||||||
<dict>
|
|
||||||
<key>isShown</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
<key>BoringSSL-GRPC-openssl_grpc.xcscheme</key>
|
<key>BoringSSL-GRPC-openssl_grpc.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>${PRODUCT_NAME}</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>FMWK</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>5.8.1</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string></string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
@interface PodsDummy_Alamofire : NSObject
|
|
||||||
@end
|
|
||||||
@implementation PodsDummy_Alamofire
|
|
||||||
@end
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
#ifdef __OBJC__
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#else
|
|
||||||
#ifndef FOUNDATION_EXPORT
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
#define FOUNDATION_EXPORT extern "C"
|
|
||||||
#else
|
|
||||||
#define FOUNDATION_EXPORT extern
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
#ifdef __OBJC__
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#else
|
|
||||||
#ifndef FOUNDATION_EXPORT
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
#define FOUNDATION_EXPORT extern "C"
|
|
||||||
#else
|
|
||||||
#define FOUNDATION_EXPORT extern
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
FOUNDATION_EXPORT double AlamofireVersionNumber;
|
|
||||||
FOUNDATION_EXPORT const unsigned char AlamofireVersionString[];
|
|
||||||
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
|
||||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
|
||||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
|
||||||
OTHER_LDFLAGS = $(inherited) -framework "CFNetwork"
|
|
||||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
|
||||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
|
||||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
|
||||||
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
|
||||||
PODS_ROOT = ${SRCROOT}
|
|
||||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire
|
|
||||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
|
||||||
SKIP_INSTALL = YES
|
|
||||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
framework module Alamofire {
|
|
||||||
umbrella header "Alamofire-umbrella.h"
|
|
||||||
|
|
||||||
export *
|
|
||||||
module * { export * }
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
|
||||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
|
||||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
|
||||||
OTHER_LDFLAGS = $(inherited) -framework "CFNetwork"
|
|
||||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
|
||||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
|
||||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
|
||||||
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
|
||||||
PODS_ROOT = ${SRCROOT}
|
|
||||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire
|
|
||||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
|
||||||
SKIP_INSTALL = YES
|
|
||||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
|
||||||
@ -1,29 +1,6 @@
|
|||||||
# Acknowledgements
|
# Acknowledgements
|
||||||
This application makes use of the following third party libraries:
|
This application makes use of the following third party libraries:
|
||||||
|
|
||||||
## Alamofire
|
|
||||||
|
|
||||||
Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
## BoringSSL-GRPC
|
## BoringSSL-GRPC
|
||||||
|
|
||||||
BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
|
BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
|
||||||
|
|||||||
@ -12,35 +12,6 @@
|
|||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>PSGroupSpecifier</string>
|
<string>PSGroupSpecifier</string>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>FooterText</key>
|
|
||||||
<string>Copyright (c) 2014-2022 Alamofire Software Foundation (http://alamofire.org/)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
</string>
|
|
||||||
<key>License</key>
|
|
||||||
<string>MIT</string>
|
|
||||||
<key>Title</key>
|
|
||||||
<string>Alamofire</string>
|
|
||||||
<key>Type</key>
|
|
||||||
<string>PSGroupSpecifier</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>FooterText</key>
|
<key>FooterText</key>
|
||||||
<string>BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
|
<string>BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
${PODS_ROOT}/Target Support Files/Pods-SwiftProject/Pods-SwiftProject-frameworks.sh
|
${PODS_ROOT}/Target Support Files/Pods-SwiftProject/Pods-SwiftProject-frameworks.sh
|
||||||
${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework
|
|
||||||
${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework
|
${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework
|
||||||
${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework
|
${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework
|
||||||
${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework
|
${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework
|
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl_grpc.framework
|
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl_grpc.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DeviceKit.framework
|
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DeviceKit.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework
|
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
${PODS_ROOT}/Target Support Files/Pods-SwiftProject/Pods-SwiftProject-frameworks.sh
|
${PODS_ROOT}/Target Support Files/Pods-SwiftProject/Pods-SwiftProject-frameworks.sh
|
||||||
${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework
|
|
||||||
${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework
|
${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework
|
||||||
${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework
|
${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework
|
||||||
${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework
|
${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework
|
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl_grpc.framework
|
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl_grpc.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DeviceKit.framework
|
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DeviceKit.framework
|
||||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework
|
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework
|
||||||
|
|||||||
@ -176,7 +176,6 @@ code_sign_if_enabled() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if [[ "$CONFIGURATION" == "Debug" ]]; then
|
if [[ "$CONFIGURATION" == "Debug" ]]; then
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
|
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework"
|
||||||
@ -215,7 +214,6 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
|
|||||||
install_framework "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework"
|
||||||
fi
|
fi
|
||||||
if [[ "$CONFIGURATION" == "Release" ]]; then
|
if [[ "$CONFIGURATION" == "Release" ]]; then
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
|
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/DeviceKit/DeviceKit.framework"
|
||||||
install_framework "${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework"
|
install_framework "${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework"
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL-GRPC" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit" "${PODS_CONFIGURATION_BUILD_DIR}/FBSDKCoreKit" "${PODS_CONFIGURATION_BUILD_DIR}/FacebookCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAppCheckInterop" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfigInterop" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSessions" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSharedSwift" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/KeychainAccess" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/LLCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RecaptchaInterop" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/TZImagePickerController" "${PODS_CONFIGURATION_BUILD_DIR}/abseil" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core" "${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_XCFRAMEWORKS_BUILD_DIR}/FirebaseAnalytics/WithoutAdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/WithoutAdIdSupport"
|
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL-GRPC" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit" "${PODS_CONFIGURATION_BUILD_DIR}/FBSDKCoreKit" "${PODS_CONFIGURATION_BUILD_DIR}/FacebookCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAppCheckInterop" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfigInterop" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSessions" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSharedSwift" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/KeychainAccess" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/LLCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RecaptchaInterop" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/TZImagePickerController" "${PODS_CONFIGURATION_BUILD_DIR}/abseil" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core" "${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_XCFRAMEWORKS_BUILD_DIR}/FirebaseAnalytics/WithoutAdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/WithoutAdIdSupport"
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) FBSDKCOCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) FBSDKCOCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1
|
||||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL-GRPC/openssl_grpc.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit/DeviceKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FacebookCore/FacebookCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting/FirebaseABTesting.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAppCheckInterop/FirebaseAppCheckInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth/FirebaseAuth.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/FirebaseFirestore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal/FirebaseFirestoreInternal.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig/FirebaseRemoteConfig.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfigInterop/FirebaseRemoteConfigInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSessions/FirebaseSessions.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSharedSwift/FirebaseSharedSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KeychainAccess/KeychainAccess.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LLCycleScrollView/LLCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RecaptchaInterop/RecaptchaInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD/SVProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TZImagePickerController/TZImagePickerController.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/abseil/absl.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++/grpcpp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core/grpc.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources
|
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL-GRPC/openssl_grpc.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit/DeviceKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FacebookCore/FacebookCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting/FirebaseABTesting.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAppCheckInterop/FirebaseAppCheckInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth/FirebaseAuth.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/FirebaseFirestore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal/FirebaseFirestoreInternal.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig/FirebaseRemoteConfig.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfigInterop/FirebaseRemoteConfigInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSessions/FirebaseSessions.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSharedSwift/FirebaseSharedSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KeychainAccess/KeychainAccess.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LLCycleScrollView/LLCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RecaptchaInterop/RecaptchaInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD/SVProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TZImagePickerController/TZImagePickerController.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/abseil/absl.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++/grpcpp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core/grpc.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources
|
||||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
|
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"stdc++" -l"z" -framework "Accelerate" -framework "Alamofire" -framework "CFNetwork" -framework "CoreTelephony" -framework "DeviceKit" -framework "FBLPromises" -framework "FBSDKCoreKit" -framework "FacebookCore" -framework "FirebaseABTesting" -framework "FirebaseAnalytics" -framework "FirebaseAppCheckInterop" -framework "FirebaseAuth" -framework "FirebaseCore" -framework "FirebaseCoreExtension" -framework "FirebaseCoreInternal" -framework "FirebaseCrashlytics" -framework "FirebaseFirestore" -framework "FirebaseFirestoreInternal" -framework "FirebaseInstallations" -framework "FirebaseRemoteConfig" -framework "FirebaseRemoteConfigInterop" -framework "FirebaseSessions" -framework "FirebaseSharedSwift" -framework "Foundation" -framework "GTMSessionFetcher" -framework "GoogleAppMeasurement" -framework "GoogleDataTransport" -framework "GoogleUtilities" -framework "ImageIO" -framework "KeychainAccess" -framework "Kingfisher" -framework "LLCycleScrollView" -framework "Photos" -framework "PhotosUI" -framework "Promises" -framework "QuartzCore" -framework "RecaptchaInterop" -framework "SDWebImage" -framework "SVProgressHUD" -framework "SafariServices" -framework "Security" -framework "SnapKit" -framework "StoreKit" -framework "SystemConfiguration" -framework "TZImagePickerController" -framework "UIKit" -framework "absl" -framework "grpc" -framework "grpcpp" -framework "leveldb" -framework "nanopb" -framework "openssl_grpc" -weak_framework "Accelerate" -weak_framework "Accounts" -weak_framework "AudioToolbox" -weak_framework "Combine" -weak_framework "CoreGraphics" -weak_framework "CoreLocation" -weak_framework "FirebaseFirestoreInternal" -weak_framework "Foundation" -weak_framework "QuartzCore" -weak_framework "Security" -weak_framework "Social" -weak_framework "SwiftUI" -weak_framework "UIKit"
|
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"stdc++" -l"z" -framework "Accelerate" -framework "CFNetwork" -framework "CoreTelephony" -framework "DeviceKit" -framework "FBLPromises" -framework "FBSDKCoreKit" -framework "FacebookCore" -framework "FirebaseABTesting" -framework "FirebaseAnalytics" -framework "FirebaseAppCheckInterop" -framework "FirebaseAuth" -framework "FirebaseCore" -framework "FirebaseCoreExtension" -framework "FirebaseCoreInternal" -framework "FirebaseCrashlytics" -framework "FirebaseFirestore" -framework "FirebaseFirestoreInternal" -framework "FirebaseInstallations" -framework "FirebaseRemoteConfig" -framework "FirebaseRemoteConfigInterop" -framework "FirebaseSessions" -framework "FirebaseSharedSwift" -framework "Foundation" -framework "GTMSessionFetcher" -framework "GoogleAppMeasurement" -framework "GoogleDataTransport" -framework "GoogleUtilities" -framework "ImageIO" -framework "KeychainAccess" -framework "Kingfisher" -framework "LLCycleScrollView" -framework "Photos" -framework "PhotosUI" -framework "Promises" -framework "QuartzCore" -framework "RecaptchaInterop" -framework "SDWebImage" -framework "SVProgressHUD" -framework "SafariServices" -framework "Security" -framework "SnapKit" -framework "StoreKit" -framework "SystemConfiguration" -framework "TZImagePickerController" -framework "UIKit" -framework "absl" -framework "grpc" -framework "grpcpp" -framework "leveldb" -framework "nanopb" -framework "openssl_grpc" -weak_framework "Accelerate" -weak_framework "Accounts" -weak_framework "AudioToolbox" -weak_framework "Combine" -weak_framework "CoreGraphics" -weak_framework "CoreLocation" -weak_framework "FirebaseFirestoreInternal" -weak_framework "Foundation" -weak_framework "QuartzCore" -weak_framework "Security" -weak_framework "Social" -weak_framework "SwiftUI" -weak_framework "UIKit"
|
||||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL-GRPC" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit" "${PODS_CONFIGURATION_BUILD_DIR}/FBSDKCoreKit" "${PODS_CONFIGURATION_BUILD_DIR}/FacebookCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAppCheckInterop" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfigInterop" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSessions" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSharedSwift" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/KeychainAccess" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/LLCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RecaptchaInterop" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/TZImagePickerController" "${PODS_CONFIGURATION_BUILD_DIR}/abseil" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core" "${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_XCFRAMEWORKS_BUILD_DIR}/FirebaseAnalytics/WithoutAdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/WithoutAdIdSupport"
|
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL-GRPC" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit" "${PODS_CONFIGURATION_BUILD_DIR}/FBSDKCoreKit" "${PODS_CONFIGURATION_BUILD_DIR}/FacebookCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAppCheckInterop" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfigInterop" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSessions" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSharedSwift" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities" "${PODS_CONFIGURATION_BUILD_DIR}/KeychainAccess" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/LLCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RecaptchaInterop" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/TZImagePickerController" "${PODS_CONFIGURATION_BUILD_DIR}/abseil" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core" "${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb" "${PODS_ROOT}/FirebaseAnalytics/Frameworks" "${PODS_ROOT}/GoogleAppMeasurement/Frameworks" "${PODS_XCFRAMEWORKS_BUILD_DIR}/FirebaseAnalytics/WithoutAdIdSupport" "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAppMeasurement/WithoutAdIdSupport"
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) FBSDKCOCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1
|
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) FBSDKCOCOAPODS=1 $(inherited) PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1
|
||||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL-GRPC/openssl_grpc.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit/DeviceKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FacebookCore/FacebookCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting/FirebaseABTesting.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAppCheckInterop/FirebaseAppCheckInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth/FirebaseAuth.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/FirebaseFirestore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal/FirebaseFirestoreInternal.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig/FirebaseRemoteConfig.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfigInterop/FirebaseRemoteConfigInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSessions/FirebaseSessions.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSharedSwift/FirebaseSharedSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KeychainAccess/KeychainAccess.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LLCycleScrollView/LLCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RecaptchaInterop/RecaptchaInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD/SVProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TZImagePickerController/TZImagePickerController.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/abseil/absl.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++/grpcpp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core/grpc.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources
|
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL-GRPC/openssl_grpc.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DeviceKit/DeviceKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FacebookCore/FacebookCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseABTesting/FirebaseABTesting.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAppCheckInterop/FirebaseAppCheckInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth/FirebaseAuth.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/FirebaseFirestore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestoreInternal/FirebaseFirestoreInternal.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfig/FirebaseRemoteConfig.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseRemoteConfigInterop/FirebaseRemoteConfigInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSessions/FirebaseSessions.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/FirebaseSharedSwift/FirebaseSharedSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KeychainAccess/KeychainAccess.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LLCycleScrollView/LLCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromisesSwift/Promises.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RecaptchaInterop/RecaptchaInterop.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SVProgressHUD/SVProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TZImagePickerController/TZImagePickerController.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/abseil/absl.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-C++/grpcpp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core/grpc.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Firebase" $(inherited) ${PODS_ROOT}/Firebase/CoreOnly/Sources
|
||||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
|
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"stdc++" -l"z" -framework "Accelerate" -framework "Alamofire" -framework "CFNetwork" -framework "CoreTelephony" -framework "DeviceKit" -framework "FBLPromises" -framework "FBSDKCoreKit" -framework "FacebookCore" -framework "FirebaseABTesting" -framework "FirebaseAnalytics" -framework "FirebaseAppCheckInterop" -framework "FirebaseAuth" -framework "FirebaseCore" -framework "FirebaseCoreExtension" -framework "FirebaseCoreInternal" -framework "FirebaseCrashlytics" -framework "FirebaseFirestore" -framework "FirebaseFirestoreInternal" -framework "FirebaseInstallations" -framework "FirebaseRemoteConfig" -framework "FirebaseRemoteConfigInterop" -framework "FirebaseSessions" -framework "FirebaseSharedSwift" -framework "Foundation" -framework "GTMSessionFetcher" -framework "GoogleAppMeasurement" -framework "GoogleDataTransport" -framework "GoogleUtilities" -framework "ImageIO" -framework "KeychainAccess" -framework "Kingfisher" -framework "LLCycleScrollView" -framework "Photos" -framework "PhotosUI" -framework "Promises" -framework "QuartzCore" -framework "RecaptchaInterop" -framework "SDWebImage" -framework "SVProgressHUD" -framework "SafariServices" -framework "Security" -framework "SnapKit" -framework "StoreKit" -framework "SystemConfiguration" -framework "TZImagePickerController" -framework "UIKit" -framework "absl" -framework "grpc" -framework "grpcpp" -framework "leveldb" -framework "nanopb" -framework "openssl_grpc" -weak_framework "Accelerate" -weak_framework "Accounts" -weak_framework "AudioToolbox" -weak_framework "Combine" -weak_framework "CoreGraphics" -weak_framework "CoreLocation" -weak_framework "FirebaseFirestoreInternal" -weak_framework "Foundation" -weak_framework "QuartzCore" -weak_framework "Security" -weak_framework "Social" -weak_framework "SwiftUI" -weak_framework "UIKit"
|
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"sqlite3" -l"stdc++" -l"z" -framework "Accelerate" -framework "CFNetwork" -framework "CoreTelephony" -framework "DeviceKit" -framework "FBLPromises" -framework "FBSDKCoreKit" -framework "FacebookCore" -framework "FirebaseABTesting" -framework "FirebaseAnalytics" -framework "FirebaseAppCheckInterop" -framework "FirebaseAuth" -framework "FirebaseCore" -framework "FirebaseCoreExtension" -framework "FirebaseCoreInternal" -framework "FirebaseCrashlytics" -framework "FirebaseFirestore" -framework "FirebaseFirestoreInternal" -framework "FirebaseInstallations" -framework "FirebaseRemoteConfig" -framework "FirebaseRemoteConfigInterop" -framework "FirebaseSessions" -framework "FirebaseSharedSwift" -framework "Foundation" -framework "GTMSessionFetcher" -framework "GoogleAppMeasurement" -framework "GoogleDataTransport" -framework "GoogleUtilities" -framework "ImageIO" -framework "KeychainAccess" -framework "Kingfisher" -framework "LLCycleScrollView" -framework "Photos" -framework "PhotosUI" -framework "Promises" -framework "QuartzCore" -framework "RecaptchaInterop" -framework "SDWebImage" -framework "SVProgressHUD" -framework "SafariServices" -framework "Security" -framework "SnapKit" -framework "StoreKit" -framework "SystemConfiguration" -framework "TZImagePickerController" -framework "UIKit" -framework "absl" -framework "grpc" -framework "grpcpp" -framework "leveldb" -framework "nanopb" -framework "openssl_grpc" -weak_framework "Accelerate" -weak_framework "Accounts" -weak_framework "AudioToolbox" -weak_framework "Combine" -weak_framework "CoreGraphics" -weak_framework "CoreLocation" -weak_framework "FirebaseFirestoreInternal" -weak_framework "Foundation" -weak_framework "QuartzCore" -weak_framework "Security" -weak_framework "Social" -weak_framework "SwiftUI" -weak_framework "UIKit"
|
||||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||||
|
|||||||
Binary file not shown.
@ -6,7 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Alamofire
|
//import Alamofire
|
||||||
|
|
||||||
class RootTabBarController: UITabBarController {
|
class RootTabBarController: UITabBarController {
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user