mirror of
https://github.com/hmalik144/EasyCC_iOs_-legacy-.git
synced 2025-12-10 02:35:21 +00:00
Easy interface Currency Converter for iOS
This commit is contained in:
9
Podfile
Normal file
9
Podfile
Normal file
@@ -0,0 +1,9 @@
|
||||
# Uncomment the next line to define a global platform for your project
|
||||
platform :ios, '9.0'
|
||||
use_frameworks!
|
||||
|
||||
target 'easyCC' do
|
||||
|
||||
pod ‘Alamofire’,'~> 4.7'
|
||||
|
||||
end
|
||||
16
Podfile.lock
Normal file
16
Podfile.lock
Normal file
@@ -0,0 +1,16 @@
|
||||
PODS:
|
||||
- Alamofire (4.7.3)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Alamofire (~> 4.7)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- Alamofire
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: c7287b6e5d7da964a70935e5db17046b7fde6568
|
||||
|
||||
PODFILE CHECKSUM: 2287d4bb366efcfb07dc93dc27fa82c8712e6c50
|
||||
|
||||
COCOAPODS: 1.5.3
|
||||
19
Pods/Alamofire/LICENSE
generated
Normal file
19
Pods/Alamofire/LICENSE
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
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.
|
||||
242
Pods/Alamofire/README.md
generated
Normal file
242
Pods/Alamofire/README.md
generated
Normal file
@@ -0,0 +1,242 @@
|
||||

|
||||
|
||||
[](https://travis-ci.org/Alamofire/Alamofire)
|
||||
[](https://img.shields.io/cocoapods/v/Alamofire.svg)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](https://alamofire.github.io/Alamofire)
|
||||
[](https://twitter.com/AlamofireSF)
|
||||
[](https://gitter.im/Alamofire/Alamofire?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
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)
|
||||
- [Usage](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md)
|
||||
- **Intro -** [Making a Request](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#making-a-request), [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), [Parameter Encoding](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#parameter-encoding), [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-manager), [Session Delegate](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#session-delegate), [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)
|
||||
- **Model Objects -** [Custom Response Serialization](https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#custom-response-serialization)
|
||||
- **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] URL / JSON / plist 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)
|
||||
|
||||
## 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
|
||||
|
||||
- iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
|
||||
- Xcode 8.3+
|
||||
- Swift 3.1+
|
||||
|
||||
## Migration Guides
|
||||
|
||||
- [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**, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire). (Tag 'alamofire')
|
||||
- If you'd like to **ask a general question**, use [Stack Overflow](https://stackoverflow.com/questions/tagged/alamofire).
|
||||
- If you **found a bug**, open an issue.
|
||||
- If you **have a feature request**, open an issue.
|
||||
- If you **want to contribute**, submit a pull request.
|
||||
|
||||
## Installation
|
||||
|
||||
### CocoaPods
|
||||
|
||||
[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command:
|
||||
|
||||
```bash
|
||||
$ gem install cocoapods
|
||||
```
|
||||
|
||||
> CocoaPods 1.1+ is required to build Alamofire 4.0+.
|
||||
|
||||
To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`:
|
||||
|
||||
```ruby
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
platform :ios, '10.0'
|
||||
use_frameworks!
|
||||
|
||||
target '<Your Target Name>' do
|
||||
pod 'Alamofire', '~> 4.7'
|
||||
end
|
||||
```
|
||||
|
||||
Then, run the following command:
|
||||
|
||||
```bash
|
||||
$ pod install
|
||||
```
|
||||
|
||||
### Carthage
|
||||
|
||||
[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
|
||||
|
||||
You can install Carthage with [Homebrew](https://brew.sh/) using the following command:
|
||||
|
||||
```bash
|
||||
$ brew update
|
||||
$ brew install carthage
|
||||
```
|
||||
|
||||
To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`:
|
||||
|
||||
```ogdl
|
||||
github "Alamofire/Alamofire" ~> 4.7
|
||||
```
|
||||
|
||||
Run `carthage update` to build the framework and drag the built `Alamofire.framework` into your Xcode project.
|
||||
|
||||
### 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. It is in early development, but Alamofire does support its use on supported platforms.
|
||||
|
||||
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 3
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/Alamofire/Alamofire.git", majorVersion: 4)
|
||||
]
|
||||
```
|
||||
|
||||
#### Swift 4
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.0.0")
|
||||
]
|
||||
```
|
||||
|
||||
### 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 OS X.
|
||||
|
||||
> You can verify which one you selected by inspecting the build log for your project. The build target for `Alamofire` will be listed as either `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.
|
||||
|
||||
## 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 test case
|
||||
- `rdar://26870455` - Background URL Session Configurations do not work in the simulator
|
||||
- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest`
|
||||
- [`rdar://36082113`](http://openradar.appspot.com/radar?id=4942308441063424) - `URLSessionTaskMetrics` failing to link on watchOS 3.0+
|
||||
|
||||
## 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 on 9/1/17 in Xcode 9 beta 6).
|
||||
|
||||
## 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.
|
||||
|
||||
### What logic belongs in a Router vs. a Request Adapter?
|
||||
|
||||
Simple, static data such as paths, parameters and common headers belong in the `Router`. Dynamic data such as an `Authorization` header whose value can changed based on an authentication system belongs in a `RequestAdapter`.
|
||||
|
||||
The reason the dynamic data MUST be placed into the `RequestAdapter` is to support retry operations. When a `Request` is retried, the original request is not rebuilt meaning the `Router` will not be called again. The `RequestAdapter` is called again allowing the dynamic data to be updated on the original request before retrying the `Request`.
|
||||
|
||||
## 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.
|
||||
|
||||
## Donations
|
||||
|
||||
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 us members to gain some legal protections and also allow us to put donations to use, tax free.
|
||||
Donating to 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 today to help us reach our goal would be greatly appreciated.
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=W34WPEE74APJQ)
|
||||
|
||||
## License
|
||||
|
||||
Alamofire is released under the MIT license. [See LICENSE](https://github.com/Alamofire/Alamofire/blob/master/LICENSE) for details.
|
||||
460
Pods/Alamofire/Source/AFError.swift
generated
Normal file
460
Pods/Alamofire/Source/AFError.swift
generated
Normal file
@@ -0,0 +1,460 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// `AFError` is the error type returned by Alamofire. It encompasses a few different types of errors, each with
|
||||
/// their own associated reasons.
|
||||
///
|
||||
/// - invalidURL: Returned when a `URLConvertible` type fails to create a valid `URL`.
|
||||
/// - parameterEncodingFailed: Returned when a parameter encoding object throws an error during the encoding process.
|
||||
/// - multipartEncodingFailed: Returned when some step in the multipart encoding process fails.
|
||||
/// - responseValidationFailed: Returned when a `validate()` call fails.
|
||||
/// - responseSerializationFailed: Returned when a response serializer encounters an error in the serialization process.
|
||||
public enum AFError: Error {
|
||||
/// The underlying reason the parameter encoding error occurred.
|
||||
///
|
||||
/// - missingURL: The URL request did not have a URL to encode.
|
||||
/// - jsonEncodingFailed: JSON serialization failed with an underlying system error during the
|
||||
/// encoding process.
|
||||
/// - propertyListEncodingFailed: Property list serialization failed with an underlying system error during
|
||||
/// encoding process.
|
||||
public enum ParameterEncodingFailureReason {
|
||||
case missingURL
|
||||
case jsonEncodingFailed(error: Error)
|
||||
case propertyListEncodingFailed(error: Error)
|
||||
}
|
||||
|
||||
/// The underlying reason the multipart encoding error occurred.
|
||||
///
|
||||
/// - bodyPartURLInvalid: The `fileURL` provided for reading an encodable body part isn't a
|
||||
/// file URL.
|
||||
/// - bodyPartFilenameInvalid: The filename of the `fileURL` provided has either an empty
|
||||
/// `lastPathComponent` or `pathExtension.
|
||||
/// - bodyPartFileNotReachable: The file at the `fileURL` provided was not reachable.
|
||||
/// - bodyPartFileNotReachableWithError: Attempting to check the reachability of the `fileURL` provided threw
|
||||
/// an error.
|
||||
/// - bodyPartFileIsDirectory: The file at the `fileURL` provided is actually a directory.
|
||||
/// - bodyPartFileSizeNotAvailable: The size of the file at the `fileURL` provided was not returned by
|
||||
/// the system.
|
||||
/// - bodyPartFileSizeQueryFailedWithError: The attempt to find the size of the file at the `fileURL` provided
|
||||
/// threw an error.
|
||||
/// - bodyPartInputStreamCreationFailed: An `InputStream` could not be created for the provided `fileURL`.
|
||||
/// - outputStreamCreationFailed: An `OutputStream` could not be created when attempting to write the
|
||||
/// encoded data to disk.
|
||||
/// - outputStreamFileAlreadyExists: The encoded body data could not be writtent disk because a file
|
||||
/// already exists at the provided `fileURL`.
|
||||
/// - outputStreamURLInvalid: The `fileURL` provided for writing the encoded body data to disk is
|
||||
/// not a file URL.
|
||||
/// - outputStreamWriteFailed: The attempt to write the encoded body data to disk failed with an
|
||||
/// underlying error.
|
||||
/// - inputStreamReadFailed: The attempt to read an encoded body part `InputStream` failed with
|
||||
/// underlying system error.
|
||||
public enum MultipartEncodingFailureReason {
|
||||
case bodyPartURLInvalid(url: URL)
|
||||
case bodyPartFilenameInvalid(in: URL)
|
||||
case bodyPartFileNotReachable(at: URL)
|
||||
case bodyPartFileNotReachableWithError(atURL: URL, error: Error)
|
||||
case bodyPartFileIsDirectory(at: URL)
|
||||
case bodyPartFileSizeNotAvailable(at: URL)
|
||||
case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error)
|
||||
case bodyPartInputStreamCreationFailed(for: URL)
|
||||
|
||||
case outputStreamCreationFailed(for: URL)
|
||||
case outputStreamFileAlreadyExists(at: URL)
|
||||
case outputStreamURLInvalid(url: URL)
|
||||
case outputStreamWriteFailed(error: Error)
|
||||
|
||||
case inputStreamReadFailed(error: Error)
|
||||
}
|
||||
|
||||
/// The underlying reason the response validation error occurred.
|
||||
///
|
||||
/// - dataFileNil: The data file containing the server response did not exist.
|
||||
/// - dataFileReadFailed: The data file containing the server response could not be read.
|
||||
/// - missingContentType: The response did not contain a `Content-Type` and the `acceptableContentTypes`
|
||||
/// provided did not contain wildcard type.
|
||||
/// - unacceptableContentType: The response `Content-Type` did not match any type in the provided
|
||||
/// `acceptableContentTypes`.
|
||||
/// - unacceptableStatusCode: The response status code was not acceptable.
|
||||
public enum ResponseValidationFailureReason {
|
||||
case dataFileNil
|
||||
case dataFileReadFailed(at: URL)
|
||||
case missingContentType(acceptableContentTypes: [String])
|
||||
case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String)
|
||||
case unacceptableStatusCode(code: Int)
|
||||
}
|
||||
|
||||
/// The underlying reason the response serialization error occurred.
|
||||
///
|
||||
/// - inputDataNil: The server response contained no data.
|
||||
/// - inputDataNilOrZeroLength: The server response contained no data or the data was zero length.
|
||||
/// - inputFileNil: The file containing the server response did not exist.
|
||||
/// - inputFileReadFailed: The file containing the server response could not be read.
|
||||
/// - stringSerializationFailed: String serialization failed using the provided `String.Encoding`.
|
||||
/// - jsonSerializationFailed: JSON serialization failed with an underlying system error.
|
||||
/// - propertyListSerializationFailed: Property list serialization failed with an underlying system error.
|
||||
public enum ResponseSerializationFailureReason {
|
||||
case inputDataNil
|
||||
case inputDataNilOrZeroLength
|
||||
case inputFileNil
|
||||
case inputFileReadFailed(at: URL)
|
||||
case stringSerializationFailed(encoding: String.Encoding)
|
||||
case jsonSerializationFailed(error: Error)
|
||||
case propertyListSerializationFailed(error: Error)
|
||||
}
|
||||
|
||||
case invalidURL(url: URLConvertible)
|
||||
case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
|
||||
case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
|
||||
case responseValidationFailed(reason: ResponseValidationFailureReason)
|
||||
case responseSerializationFailed(reason: ResponseSerializationFailureReason)
|
||||
}
|
||||
|
||||
// MARK: - Adapt Error
|
||||
|
||||
struct AdaptError: Error {
|
||||
let error: Error
|
||||
}
|
||||
|
||||
extension Error {
|
||||
var underlyingAdaptError: Error? { return (self as? AdaptError)?.error }
|
||||
}
|
||||
|
||||
// MARK: - Error Booleans
|
||||
|
||||
extension AFError {
|
||||
/// Returns whether the AFError is an invalid URL error.
|
||||
public var isInvalidURLError: Bool {
|
||||
if case .invalidURL = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
/// Returns whether the AFError is a parameter encoding error. When `true`, the `underlyingError` property will
|
||||
/// contain the associated value.
|
||||
public var isParameterEncodingError: Bool {
|
||||
if case .parameterEncodingFailed = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
/// Returns whether the AFError is a multipart encoding error. When `true`, the `url` and `underlyingError` properties
|
||||
/// will contain the associated values.
|
||||
public var isMultipartEncodingError: Bool {
|
||||
if case .multipartEncodingFailed = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
/// Returns whether the `AFError` is a response validation error. When `true`, the `acceptableContentTypes`,
|
||||
/// `responseContentType`, and `responseCode` properties will contain the associated values.
|
||||
public var isResponseValidationError: Bool {
|
||||
if case .responseValidationFailed = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
/// Returns whether the `AFError` is a response serialization error. When `true`, the `failedStringEncoding` and
|
||||
/// `underlyingError` properties will contain the associated values.
|
||||
public var isResponseSerializationError: Bool {
|
||||
if case .responseSerializationFailed = self { return true }
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Convenience Properties
|
||||
|
||||
extension AFError {
|
||||
/// The `URLConvertible` associated with the error.
|
||||
public var urlConvertible: URLConvertible? {
|
||||
switch self {
|
||||
case .invalidURL(let url):
|
||||
return url
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The `URL` associated with the error.
|
||||
public var url: URL? {
|
||||
switch self {
|
||||
case .multipartEncodingFailed(let reason):
|
||||
return reason.url
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Error` returned by a system framework associated with a `.parameterEncodingFailed`,
|
||||
/// `.multipartEncodingFailed` or `.responseSerializationFailed` error.
|
||||
public var underlyingError: Error? {
|
||||
switch self {
|
||||
case .parameterEncodingFailed(let reason):
|
||||
return reason.underlyingError
|
||||
case .multipartEncodingFailed(let reason):
|
||||
return reason.underlyingError
|
||||
case .responseSerializationFailed(let reason):
|
||||
return reason.underlyingError
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The acceptable `Content-Type`s of a `.responseValidationFailed` error.
|
||||
public var acceptableContentTypes: [String]? {
|
||||
switch self {
|
||||
case .responseValidationFailed(let reason):
|
||||
return reason.acceptableContentTypes
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The response `Content-Type` of a `.responseValidationFailed` error.
|
||||
public var responseContentType: String? {
|
||||
switch self {
|
||||
case .responseValidationFailed(let reason):
|
||||
return reason.responseContentType
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The response code of a `.responseValidationFailed` error.
|
||||
public var responseCode: Int? {
|
||||
switch self {
|
||||
case .responseValidationFailed(let reason):
|
||||
return reason.responseCode
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// The `String.Encoding` associated with a failed `.stringResponse()` call.
|
||||
public var failedStringEncoding: String.Encoding? {
|
||||
switch self {
|
||||
case .responseSerializationFailed(let reason):
|
||||
return reason.failedStringEncoding
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ParameterEncodingFailureReason {
|
||||
var underlyingError: Error? {
|
||||
switch self {
|
||||
case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error):
|
||||
return error
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.MultipartEncodingFailureReason {
|
||||
var url: URL? {
|
||||
switch self {
|
||||
case .bodyPartURLInvalid(let url), .bodyPartFilenameInvalid(let url), .bodyPartFileNotReachable(let url),
|
||||
.bodyPartFileIsDirectory(let url), .bodyPartFileSizeNotAvailable(let url),
|
||||
.bodyPartInputStreamCreationFailed(let url), .outputStreamCreationFailed(let url),
|
||||
.outputStreamFileAlreadyExists(let url), .outputStreamURLInvalid(let url),
|
||||
.bodyPartFileNotReachableWithError(let url, _), .bodyPartFileSizeQueryFailedWithError(let url, _):
|
||||
return url
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var underlyingError: Error? {
|
||||
switch self {
|
||||
case .bodyPartFileNotReachableWithError(_, let error), .bodyPartFileSizeQueryFailedWithError(_, let error),
|
||||
.outputStreamWriteFailed(let error), .inputStreamReadFailed(let error):
|
||||
return error
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ResponseValidationFailureReason {
|
||||
var acceptableContentTypes: [String]? {
|
||||
switch self {
|
||||
case .missingContentType(let types), .unacceptableContentType(let types, _):
|
||||
return types
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var responseContentType: String? {
|
||||
switch self {
|
||||
case .unacceptableContentType(_, let responseType):
|
||||
return responseType
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var responseCode: Int? {
|
||||
switch self {
|
||||
case .unacceptableStatusCode(let code):
|
||||
return code
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ResponseSerializationFailureReason {
|
||||
var failedStringEncoding: String.Encoding? {
|
||||
switch self {
|
||||
case .stringSerializationFailed(let encoding):
|
||||
return encoding
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var underlyingError: Error? {
|
||||
switch self {
|
||||
case .jsonSerializationFailed(let error), .propertyListSerializationFailed(let error):
|
||||
return error
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Error Descriptions
|
||||
|
||||
extension AFError: LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .invalidURL(let url):
|
||||
return "URL is not valid: \(url)"
|
||||
case .parameterEncodingFailed(let reason):
|
||||
return reason.localizedDescription
|
||||
case .multipartEncodingFailed(let reason):
|
||||
return reason.localizedDescription
|
||||
case .responseValidationFailed(let reason):
|
||||
return reason.localizedDescription
|
||||
case .responseSerializationFailed(let reason):
|
||||
return reason.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ParameterEncodingFailureReason {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .missingURL:
|
||||
return "URL request to encode was missing a URL"
|
||||
case .jsonEncodingFailed(let error):
|
||||
return "JSON could not be encoded because of error:\n\(error.localizedDescription)"
|
||||
case .propertyListEncodingFailed(let error):
|
||||
return "PropertyList could not be encoded because of error:\n\(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.MultipartEncodingFailureReason {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .bodyPartURLInvalid(let url):
|
||||
return "The URL provided is not a file URL: \(url)"
|
||||
case .bodyPartFilenameInvalid(let url):
|
||||
return "The URL provided does not have a valid filename: \(url)"
|
||||
case .bodyPartFileNotReachable(let url):
|
||||
return "The URL provided is not reachable: \(url)"
|
||||
case .bodyPartFileNotReachableWithError(let url, let error):
|
||||
return (
|
||||
"The system returned an error while checking the provided URL for " +
|
||||
"reachability.\nURL: \(url)\nError: \(error)"
|
||||
)
|
||||
case .bodyPartFileIsDirectory(let url):
|
||||
return "The URL provided is a directory: \(url)"
|
||||
case .bodyPartFileSizeNotAvailable(let url):
|
||||
return "Could not fetch the file size from the provided URL: \(url)"
|
||||
case .bodyPartFileSizeQueryFailedWithError(let url, let error):
|
||||
return (
|
||||
"The system returned an error while attempting to fetch the file size from the " +
|
||||
"provided URL.\nURL: \(url)\nError: \(error)"
|
||||
)
|
||||
case .bodyPartInputStreamCreationFailed(let url):
|
||||
return "Failed to create an InputStream for the provided URL: \(url)"
|
||||
case .outputStreamCreationFailed(let url):
|
||||
return "Failed to create an OutputStream for URL: \(url)"
|
||||
case .outputStreamFileAlreadyExists(let url):
|
||||
return "A file already exists at the provided URL: \(url)"
|
||||
case .outputStreamURLInvalid(let url):
|
||||
return "The provided OutputStream URL is invalid: \(url)"
|
||||
case .outputStreamWriteFailed(let error):
|
||||
return "OutputStream write failed with error: \(error)"
|
||||
case .inputStreamReadFailed(let error):
|
||||
return "InputStream read failed with error: \(error)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ResponseSerializationFailureReason {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .inputDataNil:
|
||||
return "Response could not be serialized, input data was nil."
|
||||
case .inputDataNilOrZeroLength:
|
||||
return "Response could not be serialized, input data was nil or zero length."
|
||||
case .inputFileNil:
|
||||
return "Response could not be serialized, input file was nil."
|
||||
case .inputFileReadFailed(let url):
|
||||
return "Response could not be serialized, input file could not be read: \(url)."
|
||||
case .stringSerializationFailed(let encoding):
|
||||
return "String could not be serialized with encoding: \(encoding)."
|
||||
case .jsonSerializationFailed(let error):
|
||||
return "JSON could not be serialized because of error:\n\(error.localizedDescription)"
|
||||
case .propertyListSerializationFailed(let error):
|
||||
return "PropertyList could not be serialized because of error:\n\(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AFError.ResponseValidationFailureReason {
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .dataFileNil:
|
||||
return "Response could not be validated, data file was nil."
|
||||
case .dataFileReadFailed(let url):
|
||||
return "Response could not be validated, data file could not be read: \(url)."
|
||||
case .missingContentType(let types):
|
||||
return (
|
||||
"Response Content-Type was missing and acceptable content types " +
|
||||
"(\(types.joined(separator: ","))) do not match \"*/*\"."
|
||||
)
|
||||
case .unacceptableContentType(let acceptableTypes, let responseType):
|
||||
return (
|
||||
"Response Content-Type \"\(responseType)\" does not match any acceptable types: " +
|
||||
"\(acceptableTypes.joined(separator: ","))."
|
||||
)
|
||||
case .unacceptableStatusCode(let code):
|
||||
return "Response status code was unacceptable: \(code)."
|
||||
}
|
||||
}
|
||||
}
|
||||
465
Pods/Alamofire/Source/Alamofire.swift
generated
Normal file
465
Pods/Alamofire/Source/Alamofire.swift
generated
Normal file
@@ -0,0 +1,465 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// Types adopting the `URLConvertible` protocol can be used to construct URLs, which are then used to construct
|
||||
/// URL requests.
|
||||
public protocol URLConvertible {
|
||||
/// Returns a URL that conforms to RFC 2396 or throws an `Error`.
|
||||
///
|
||||
/// - throws: An `Error` if the type cannot be converted to a `URL`.
|
||||
///
|
||||
/// - returns: A URL or throws an `Error`.
|
||||
func asURL() throws -> URL
|
||||
}
|
||||
|
||||
extension String: URLConvertible {
|
||||
/// Returns a URL if `self` represents a valid URL string that conforms to RFC 2396 or throws an `AFError`.
|
||||
///
|
||||
/// - throws: An `AFError.invalidURL` if `self` is not a valid URL string.
|
||||
///
|
||||
/// - returns: A URL or throws an `AFError`.
|
||||
public func asURL() throws -> URL {
|
||||
guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) }
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
extension URL: URLConvertible {
|
||||
/// Returns self.
|
||||
public func asURL() throws -> URL { return self }
|
||||
}
|
||||
|
||||
extension URLComponents: URLConvertible {
|
||||
/// Returns a URL if `url` is not nil, otherwise throws an `Error`.
|
||||
///
|
||||
/// - throws: An `AFError.invalidURL` if `url` is `nil`.
|
||||
///
|
||||
/// - returns: A URL or throws an `AFError`.
|
||||
public func asURL() throws -> URL {
|
||||
guard let url = url else { throw AFError.invalidURL(url: self) }
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests.
|
||||
public protocol URLRequestConvertible {
|
||||
/// Returns a URL request or throws if an `Error` was encountered.
|
||||
///
|
||||
/// - throws: An `Error` if the underlying `URLRequest` is `nil`.
|
||||
///
|
||||
/// - returns: A URL request.
|
||||
func asURLRequest() throws -> URLRequest
|
||||
}
|
||||
|
||||
extension URLRequestConvertible {
|
||||
/// The URL request.
|
||||
public var urlRequest: URLRequest? { return try? asURLRequest() }
|
||||
}
|
||||
|
||||
extension URLRequest: URLRequestConvertible {
|
||||
/// Returns a URL request or throws if an `Error` was encountered.
|
||||
public func asURLRequest() throws -> URLRequest { return self }
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension URLRequest {
|
||||
/// Creates an instance with the specified `method`, `urlString` and `headers`.
|
||||
///
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
///
|
||||
/// - returns: The new `URLRequest` instance.
|
||||
public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil) throws {
|
||||
let url = try url.asURL()
|
||||
|
||||
self.init(url: url)
|
||||
|
||||
httpMethod = method.rawValue
|
||||
|
||||
if let headers = headers {
|
||||
for (headerField, headerValue) in headers {
|
||||
setValue(headerValue, forHTTPHeaderField: headerField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func adapt(using adapter: RequestAdapter?) throws -> URLRequest {
|
||||
guard let adapter = adapter else { return self }
|
||||
return try adapter.adapt(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Data Request
|
||||
|
||||
/// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of the specified `url`,
|
||||
/// `method`, `parameters`, `encoding` and `headers`.
|
||||
///
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.get` by default.
|
||||
/// - parameter parameters: The parameters. `nil` by default.
|
||||
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `DataRequest`.
|
||||
@discardableResult
|
||||
public func request(
|
||||
_ url: URLConvertible,
|
||||
method: HTTPMethod = .get,
|
||||
parameters: Parameters? = nil,
|
||||
encoding: ParameterEncoding = URLEncoding.default,
|
||||
headers: HTTPHeaders? = nil)
|
||||
-> DataRequest
|
||||
{
|
||||
return SessionManager.default.request(
|
||||
url,
|
||||
method: method,
|
||||
parameters: parameters,
|
||||
encoding: encoding,
|
||||
headers: headers
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
|
||||
/// specified `urlRequest`.
|
||||
///
|
||||
/// - parameter urlRequest: The URL request
|
||||
///
|
||||
/// - returns: The created `DataRequest`.
|
||||
@discardableResult
|
||||
public func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
|
||||
return SessionManager.default.request(urlRequest)
|
||||
}
|
||||
|
||||
// MARK: - Download Request
|
||||
|
||||
// MARK: URL Request
|
||||
|
||||
/// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of the specified `url`,
|
||||
/// `method`, `parameters`, `encoding`, `headers` and save them to the `destination`.
|
||||
///
|
||||
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||
/// underlying URL session.
|
||||
///
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.get` by default.
|
||||
/// - parameter parameters: The parameters. `nil` by default.
|
||||
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `DownloadRequest`.
|
||||
@discardableResult
|
||||
public func download(
|
||||
_ url: URLConvertible,
|
||||
method: HTTPMethod = .get,
|
||||
parameters: Parameters? = nil,
|
||||
encoding: ParameterEncoding = URLEncoding.default,
|
||||
headers: HTTPHeaders? = nil,
|
||||
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||
-> DownloadRequest
|
||||
{
|
||||
return SessionManager.default.download(
|
||||
url,
|
||||
method: method,
|
||||
parameters: parameters,
|
||||
encoding: encoding,
|
||||
headers: headers,
|
||||
to: destination
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
|
||||
/// specified `urlRequest` and save them to the `destination`.
|
||||
///
|
||||
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||
/// underlying URL session.
|
||||
///
|
||||
/// - parameter urlRequest: The URL request.
|
||||
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `DownloadRequest`.
|
||||
@discardableResult
|
||||
public func download(
|
||||
_ urlRequest: URLRequestConvertible,
|
||||
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||
-> DownloadRequest
|
||||
{
|
||||
return SessionManager.default.download(urlRequest, to: destination)
|
||||
}
|
||||
|
||||
// MARK: Resume Data
|
||||
|
||||
/// Creates a `DownloadRequest` using the default `SessionManager` from the `resumeData` produced from a
|
||||
/// previous request cancellation to retrieve the contents of the original request and save them to the `destination`.
|
||||
///
|
||||
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||
/// underlying URL session.
|
||||
///
|
||||
/// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken
|
||||
/// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the
|
||||
/// data is written incorrectly and will always fail to resume the download. For more information about the bug and
|
||||
/// possible workarounds, please refer to the following Stack Overflow post:
|
||||
///
|
||||
/// - http://stackoverflow.com/a/39347461/1342462
|
||||
///
|
||||
/// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask`
|
||||
/// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for additional
|
||||
/// information.
|
||||
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `DownloadRequest`.
|
||||
@discardableResult
|
||||
public func download(
|
||||
resumingWith resumeData: Data,
|
||||
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||
-> DownloadRequest
|
||||
{
|
||||
return SessionManager.default.download(resumingWith: resumeData, to: destination)
|
||||
}
|
||||
|
||||
// MARK: - Upload Request
|
||||
|
||||
// MARK: File
|
||||
|
||||
/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers`
|
||||
/// for uploading the `file`.
|
||||
///
|
||||
/// - parameter file: The file to upload.
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.post` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
public func upload(
|
||||
_ fileURL: URL,
|
||||
to url: URLConvertible,
|
||||
method: HTTPMethod = .post,
|
||||
headers: HTTPHeaders? = nil)
|
||||
-> UploadRequest
|
||||
{
|
||||
return SessionManager.default.upload(fileURL, to: url, method: method, headers: headers)
|
||||
}
|
||||
|
||||
/// Creates a `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
|
||||
/// uploading the `file`.
|
||||
///
|
||||
/// - parameter file: The file to upload.
|
||||
/// - parameter urlRequest: The URL request.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
public func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||
return SessionManager.default.upload(fileURL, with: urlRequest)
|
||||
}
|
||||
|
||||
// MARK: Data
|
||||
|
||||
/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers`
|
||||
/// for uploading the `data`.
|
||||
///
|
||||
/// - parameter data: The data to upload.
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.post` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
public func upload(
|
||||
_ data: Data,
|
||||
to url: URLConvertible,
|
||||
method: HTTPMethod = .post,
|
||||
headers: HTTPHeaders? = nil)
|
||||
-> UploadRequest
|
||||
{
|
||||
return SessionManager.default.upload(data, to: url, method: method, headers: headers)
|
||||
}
|
||||
|
||||
/// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
|
||||
/// uploading the `data`.
|
||||
///
|
||||
/// - parameter data: The data to upload.
|
||||
/// - parameter urlRequest: The URL request.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
public func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||
return SessionManager.default.upload(data, with: urlRequest)
|
||||
}
|
||||
|
||||
// MARK: InputStream
|
||||
|
||||
/// Creates an `UploadRequest` using the default `SessionManager` from the specified `url`, `method` and `headers`
|
||||
/// for uploading the `stream`.
|
||||
///
|
||||
/// - parameter stream: The stream to upload.
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.post` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
public func upload(
|
||||
_ stream: InputStream,
|
||||
to url: URLConvertible,
|
||||
method: HTTPMethod = .post,
|
||||
headers: HTTPHeaders? = nil)
|
||||
-> UploadRequest
|
||||
{
|
||||
return SessionManager.default.upload(stream, to: url, method: method, headers: headers)
|
||||
}
|
||||
|
||||
/// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
|
||||
/// uploading the `stream`.
|
||||
///
|
||||
/// - parameter urlRequest: The URL request.
|
||||
/// - parameter stream: The stream to upload.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
public func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||
return SessionManager.default.upload(stream, with: urlRequest)
|
||||
}
|
||||
|
||||
// MARK: MultipartFormData
|
||||
|
||||
/// Encodes `multipartFormData` using `encodingMemoryThreshold` with the default `SessionManager` and calls
|
||||
/// `encodingCompletion` with new `UploadRequest` using the `url`, `method` and `headers`.
|
||||
///
|
||||
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
|
||||
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
|
||||
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
|
||||
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
|
||||
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
|
||||
/// used for larger payloads such as video content.
|
||||
///
|
||||
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
|
||||
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
|
||||
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
|
||||
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
|
||||
/// technique was used.
|
||||
///
|
||||
/// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
|
||||
/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
|
||||
/// `multipartFormDataEncodingMemoryThreshold` by default.
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.post` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
/// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
|
||||
public func upload(
|
||||
multipartFormData: @escaping (MultipartFormData) -> Void,
|
||||
usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
|
||||
to url: URLConvertible,
|
||||
method: HTTPMethod = .post,
|
||||
headers: HTTPHeaders? = nil,
|
||||
encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)
|
||||
{
|
||||
return SessionManager.default.upload(
|
||||
multipartFormData: multipartFormData,
|
||||
usingThreshold: encodingMemoryThreshold,
|
||||
to: url,
|
||||
method: method,
|
||||
headers: headers,
|
||||
encodingCompletion: encodingCompletion
|
||||
)
|
||||
}
|
||||
|
||||
/// Encodes `multipartFormData` using `encodingMemoryThreshold` and the default `SessionManager` and
|
||||
/// calls `encodingCompletion` with new `UploadRequest` using the `urlRequest`.
|
||||
///
|
||||
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
|
||||
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
|
||||
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
|
||||
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
|
||||
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
|
||||
/// used for larger payloads such as video content.
|
||||
///
|
||||
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
|
||||
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
|
||||
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
|
||||
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
|
||||
/// technique was used.
|
||||
///
|
||||
/// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
|
||||
/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
|
||||
/// `multipartFormDataEncodingMemoryThreshold` by default.
|
||||
/// - parameter urlRequest: The URL request.
|
||||
/// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
|
||||
public func upload(
|
||||
multipartFormData: @escaping (MultipartFormData) -> Void,
|
||||
usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
|
||||
with urlRequest: URLRequestConvertible,
|
||||
encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)
|
||||
{
|
||||
return SessionManager.default.upload(
|
||||
multipartFormData: multipartFormData,
|
||||
usingThreshold: encodingMemoryThreshold,
|
||||
with: urlRequest,
|
||||
encodingCompletion: encodingCompletion
|
||||
)
|
||||
}
|
||||
|
||||
#if !os(watchOS)
|
||||
|
||||
// MARK: - Stream Request
|
||||
|
||||
// MARK: Hostname and Port
|
||||
|
||||
/// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `hostname`
|
||||
/// and `port`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter hostName: The hostname of the server to connect to.
|
||||
/// - parameter port: The port of the server to connect to.
|
||||
///
|
||||
/// - returns: The created `StreamRequest`.
|
||||
@discardableResult
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
public func stream(withHostName hostName: String, port: Int) -> StreamRequest {
|
||||
return SessionManager.default.stream(withHostName: hostName, port: port)
|
||||
}
|
||||
|
||||
// MARK: NetService
|
||||
|
||||
/// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `netService`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter netService: The net service used to identify the endpoint.
|
||||
///
|
||||
/// - returns: The created `StreamRequest`.
|
||||
@discardableResult
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
public func stream(with netService: NetService) -> StreamRequest {
|
||||
return SessionManager.default.stream(with: netService)
|
||||
}
|
||||
|
||||
#endif
|
||||
37
Pods/Alamofire/Source/DispatchQueue+Alamofire.swift
generated
Normal file
37
Pods/Alamofire/Source/DispatchQueue+Alamofire.swift
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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 {
|
||||
static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) }
|
||||
static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) }
|
||||
static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) }
|
||||
static var background: DispatchQueue { return DispatchQueue.global(qos: .background) }
|
||||
|
||||
func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
|
||||
asyncAfter(deadline: .now() + delay, execute: closure)
|
||||
}
|
||||
}
|
||||
580
Pods/Alamofire/Source/MultipartFormData.swift
generated
Normal file
580
Pods/Alamofire/Source/MultipartFormData.swift
generated
Normal file
@@ -0,0 +1,580 @@
|
||||
//
|
||||
// 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 os(iOS) || os(watchOS) || os(tvOS)
|
||||
import MobileCoreServices
|
||||
#elseif os(macOS)
|
||||
import CoreServices
|
||||
#endif
|
||||
|
||||
/// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode
|
||||
/// multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead
|
||||
/// to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the
|
||||
/// data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for
|
||||
/// larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset.
|
||||
///
|
||||
/// For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well
|
||||
/// and the w3 form documentation.
|
||||
///
|
||||
/// - https://www.ietf.org/rfc/rfc2388.txt
|
||||
/// - https://www.ietf.org/rfc/rfc2045.txt
|
||||
/// - https://www.w3.org/TR/html401/interact/forms.html#h-17.13
|
||||
open class MultipartFormData {
|
||||
|
||||
// MARK: - Helper Types
|
||||
|
||||
struct EncodingCharacters {
|
||||
static let crlf = "\r\n"
|
||||
}
|
||||
|
||||
struct BoundaryGenerator {
|
||||
enum BoundaryType {
|
||||
case initial, encapsulated, final
|
||||
}
|
||||
|
||||
static func randomBoundary() -> String {
|
||||
return String(format: "alamofire.boundary.%08x%08x", arc4random(), arc4random())
|
||||
}
|
||||
|
||||
static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data {
|
||||
let boundaryText: String
|
||||
|
||||
switch boundaryType {
|
||||
case .initial:
|
||||
boundaryText = "--\(boundary)\(EncodingCharacters.crlf)"
|
||||
case .encapsulated:
|
||||
boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)"
|
||||
case .final:
|
||||
boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)"
|
||||
}
|
||||
|
||||
return boundaryText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
|
||||
}
|
||||
}
|
||||
|
||||
class BodyPart {
|
||||
let headers: HTTPHeaders
|
||||
let bodyStream: InputStream
|
||||
let bodyContentLength: UInt64
|
||||
var hasInitialBoundary = false
|
||||
var hasFinalBoundary = false
|
||||
|
||||
init(headers: HTTPHeaders, bodyStream: InputStream, bodyContentLength: UInt64) {
|
||||
self.headers = headers
|
||||
self.bodyStream = bodyStream
|
||||
self.bodyContentLength = bodyContentLength
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`.
|
||||
open 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 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } }
|
||||
|
||||
/// The boundary used to separate the body parts in the encoded form data.
|
||||
public let boundary: String
|
||||
|
||||
private var bodyParts: [BodyPart]
|
||||
private var bodyPartError: AFError?
|
||||
private let streamBufferSize: Int
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
/// Creates a multipart form data object.
|
||||
///
|
||||
/// - returns: The multipart form data object.
|
||||
public init() {
|
||||
self.boundary = BoundaryGenerator.randomBoundary()
|
||||
self.bodyParts = []
|
||||
|
||||
///
|
||||
/// The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more
|
||||
/// information, please refer to the following article:
|
||||
/// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html
|
||||
///
|
||||
|
||||
self.streamBufferSize = 1024
|
||||
}
|
||||
|
||||
// MARK: - Body Parts
|
||||
|
||||
/// Creates a body part from the data and appends it to the multipart form data object.
|
||||
///
|
||||
/// The body part data will be encoded using the following format:
|
||||
///
|
||||
/// - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
|
||||
/// - Encoded data
|
||||
/// - Multipart form boundary
|
||||
///
|
||||
/// - parameter data: The data to encode into the multipart form data.
|
||||
/// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header.
|
||||
public func append(_ data: Data, withName name: String) {
|
||||
let headers = contentHeaders(withName: name)
|
||||
let stream = InputStream(data: data)
|
||||
let length = UInt64(data.count)
|
||||
|
||||
append(stream, withLength: length, headers: headers)
|
||||
}
|
||||
|
||||
/// Creates a body part from the data and appends it to the multipart form data object.
|
||||
///
|
||||
/// The body part data will be encoded using the following format:
|
||||
///
|
||||
/// - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
|
||||
/// - `Content-Type: #{generated mimeType}` (HTTP Header)
|
||||
/// - Encoded data
|
||||
/// - Multipart form boundary
|
||||
///
|
||||
/// - parameter data: The data to encode into the multipart form data.
|
||||
/// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header.
|
||||
/// - parameter mimeType: The MIME type to associate with the data content type in the `Content-Type` HTTP header.
|
||||
public func append(_ data: Data, withName name: String, mimeType: String) {
|
||||
let headers = contentHeaders(withName: name, mimeType: mimeType)
|
||||
let stream = InputStream(data: data)
|
||||
let length = UInt64(data.count)
|
||||
|
||||
append(stream, withLength: length, headers: headers)
|
||||
}
|
||||
|
||||
/// Creates a body part from the data and appends it to the multipart form data object.
|
||||
///
|
||||
/// The body part data will be encoded using the following format:
|
||||
///
|
||||
/// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
|
||||
/// - `Content-Type: #{mimeType}` (HTTP Header)
|
||||
/// - Encoded file data
|
||||
/// - Multipart form boundary
|
||||
///
|
||||
/// - parameter data: The data to encode into the multipart form data.
|
||||
/// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header.
|
||||
/// - parameter fileName: The filename to associate with the data in the `Content-Disposition` HTTP header.
|
||||
/// - parameter mimeType: The MIME type to associate with the data in the `Content-Type` HTTP header.
|
||||
public func append(_ data: Data, withName name: String, fileName: String, mimeType: String) {
|
||||
let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
|
||||
let stream = InputStream(data: data)
|
||||
let length = UInt64(data.count)
|
||||
|
||||
append(stream, withLength: length, headers: headers)
|
||||
}
|
||||
|
||||
/// Creates a body part from the file and appends it to the multipart form data object.
|
||||
///
|
||||
/// The body part data will be encoded using the following format:
|
||||
///
|
||||
/// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header)
|
||||
/// - `Content-Type: #{generated mimeType}` (HTTP Header)
|
||||
/// - Encoded file data
|
||||
/// - Multipart form boundary
|
||||
///
|
||||
/// The filename in the `Content-Disposition` HTTP header is generated from the last path component of the
|
||||
/// `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the
|
||||
/// system associated MIME type.
|
||||
///
|
||||
/// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data.
|
||||
/// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header.
|
||||
public func append(_ fileURL: URL, withName name: String) {
|
||||
let fileName = fileURL.lastPathComponent
|
||||
let pathExtension = fileURL.pathExtension
|
||||
|
||||
if !fileName.isEmpty && !pathExtension.isEmpty {
|
||||
let mime = mimeType(forPathExtension: pathExtension)
|
||||
append(fileURL, withName: name, fileName: fileName, mimeType: mime)
|
||||
} else {
|
||||
setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a body part from the file and appends it to the multipart form data object.
|
||||
///
|
||||
/// The body part data will be encoded using the following format:
|
||||
///
|
||||
/// - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header)
|
||||
/// - Content-Type: #{mimeType} (HTTP Header)
|
||||
/// - Encoded file data
|
||||
/// - Multipart form boundary
|
||||
///
|
||||
/// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data.
|
||||
/// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header.
|
||||
/// - parameter fileName: The filename to associate with the file content in the `Content-Disposition` HTTP header.
|
||||
/// - parameter mimeType: The MIME type to associate with the file content in the `Content-Type` HTTP header.
|
||||
public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) {
|
||||
let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
|
||||
|
||||
//============================================================
|
||||
// Check 1 - is file URL?
|
||||
//============================================================
|
||||
|
||||
guard fileURL.isFileURL else {
|
||||
setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL))
|
||||
return
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// Check 2 - is file URL reachable?
|
||||
//============================================================
|
||||
|
||||
do {
|
||||
let isReachable = try fileURL.checkPromisedItemIsReachable()
|
||||
guard isReachable else {
|
||||
setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL))
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error))
|
||||
return
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// Check 3 - is file URL a directory?
|
||||
//============================================================
|
||||
|
||||
var isDirectory: ObjCBool = false
|
||||
let path = fileURL.path
|
||||
|
||||
guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else {
|
||||
setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL))
|
||||
return
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// Check 4 - can the file size be extracted?
|
||||
//============================================================
|
||||
|
||||
let bodyContentLength: UInt64
|
||||
|
||||
do {
|
||||
guard let fileSize = try FileManager.default.attributesOfItem(atPath: path)[.size] as? NSNumber else {
|
||||
setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL))
|
||||
return
|
||||
}
|
||||
|
||||
bodyContentLength = fileSize.uint64Value
|
||||
}
|
||||
catch {
|
||||
setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error))
|
||||
return
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// Check 5 - can a stream be created from file URL?
|
||||
//============================================================
|
||||
|
||||
guard let stream = InputStream(url: fileURL) else {
|
||||
setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL))
|
||||
return
|
||||
}
|
||||
|
||||
append(stream, withLength: bodyContentLength, headers: headers)
|
||||
}
|
||||
|
||||
/// Creates a body part from the stream and appends it to the multipart form data object.
|
||||
///
|
||||
/// The body part data will be encoded using the following format:
|
||||
///
|
||||
/// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
|
||||
/// - `Content-Type: #{mimeType}` (HTTP Header)
|
||||
/// - Encoded stream data
|
||||
/// - Multipart form boundary
|
||||
///
|
||||
/// - parameter stream: The input stream to encode in the multipart form data.
|
||||
/// - parameter length: The content length of the stream.
|
||||
/// - parameter name: The name to associate with the stream content in the `Content-Disposition` HTTP header.
|
||||
/// - parameter fileName: The filename to associate with the stream content in the `Content-Disposition` HTTP header.
|
||||
/// - parameter mimeType: The MIME type to associate with the stream content in the `Content-Type` HTTP header.
|
||||
public func append(
|
||||
_ stream: InputStream,
|
||||
withLength length: UInt64,
|
||||
name: String,
|
||||
fileName: String,
|
||||
mimeType: String)
|
||||
{
|
||||
let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
|
||||
append(stream, withLength: length, headers: headers)
|
||||
}
|
||||
|
||||
/// Creates a body part with the headers, stream and length and appends it to the multipart form data object.
|
||||
///
|
||||
/// The body part data will be encoded using the following format:
|
||||
///
|
||||
/// - HTTP headers
|
||||
/// - Encoded stream data
|
||||
/// - Multipart form boundary
|
||||
///
|
||||
/// - parameter stream: The input stream to encode in the multipart form data.
|
||||
/// - parameter length: The content length of the stream.
|
||||
/// - parameter headers: The HTTP headers for the body part.
|
||||
public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) {
|
||||
let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
|
||||
bodyParts.append(bodyPart)
|
||||
}
|
||||
|
||||
// MARK: - Data Encoding
|
||||
|
||||
/// Encodes all the appended body parts into a single `Data` value.
|
||||
///
|
||||
/// It is important to note that this method will load all the appended body parts into memory all at the same
|
||||
/// time. This method should only be used when the encoded data will have a small memory footprint. For large data
|
||||
/// cases, please use the `writeEncodedDataToDisk(fileURL:completionHandler:)` method.
|
||||
///
|
||||
/// - throws: An `AFError` if encoding encounters an error.
|
||||
///
|
||||
/// - returns: The encoded `Data` if encoding is successful.
|
||||
public func encode() throws -> Data {
|
||||
if let bodyPartError = bodyPartError {
|
||||
throw bodyPartError
|
||||
}
|
||||
|
||||
var encoded = Data()
|
||||
|
||||
bodyParts.first?.hasInitialBoundary = true
|
||||
bodyParts.last?.hasFinalBoundary = true
|
||||
|
||||
for bodyPart in bodyParts {
|
||||
let encodedData = try encode(bodyPart)
|
||||
encoded.append(encodedData)
|
||||
}
|
||||
|
||||
return encoded
|
||||
}
|
||||
|
||||
/// Writes the appended body parts into the given file URL.
|
||||
///
|
||||
/// This process is facilitated by reading and writing with input and output streams, respectively. Thus,
|
||||
/// this approach is very memory efficient and should be used for large body part data.
|
||||
///
|
||||
/// - parameter fileURL: The file URL to write the multipart form data into.
|
||||
///
|
||||
/// - throws: An `AFError` if encoding encounters an error.
|
||||
public func writeEncodedData(to fileURL: URL) throws {
|
||||
if let bodyPartError = bodyPartError {
|
||||
throw bodyPartError
|
||||
}
|
||||
|
||||
if FileManager.default.fileExists(atPath: fileURL.path) {
|
||||
throw AFError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL))
|
||||
} else if !fileURL.isFileURL {
|
||||
throw AFError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL))
|
||||
}
|
||||
|
||||
guard let outputStream = OutputStream(url: fileURL, append: false) else {
|
||||
throw AFError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL))
|
||||
}
|
||||
|
||||
outputStream.open()
|
||||
defer { outputStream.close() }
|
||||
|
||||
self.bodyParts.first?.hasInitialBoundary = true
|
||||
self.bodyParts.last?.hasFinalBoundary = true
|
||||
|
||||
for bodyPart in self.bodyParts {
|
||||
try write(bodyPart, to: outputStream)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private - Body Part Encoding
|
||||
|
||||
private func encode(_ bodyPart: BodyPart) throws -> Data {
|
||||
var encoded = Data()
|
||||
|
||||
let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
|
||||
encoded.append(initialData)
|
||||
|
||||
let headerData = encodeHeaders(for: bodyPart)
|
||||
encoded.append(headerData)
|
||||
|
||||
let bodyStreamData = try encodeBodyStream(for: bodyPart)
|
||||
encoded.append(bodyStreamData)
|
||||
|
||||
if bodyPart.hasFinalBoundary {
|
||||
encoded.append(finalBoundaryData())
|
||||
}
|
||||
|
||||
return encoded
|
||||
}
|
||||
|
||||
private func encodeHeaders(for bodyPart: BodyPart) -> Data {
|
||||
var headerText = ""
|
||||
|
||||
for (key, value) in bodyPart.headers {
|
||||
headerText += "\(key): \(value)\(EncodingCharacters.crlf)"
|
||||
}
|
||||
headerText += EncodingCharacters.crlf
|
||||
|
||||
return headerText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
|
||||
}
|
||||
|
||||
private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data {
|
||||
let inputStream = bodyPart.bodyStream
|
||||
inputStream.open()
|
||||
defer { inputStream.close() }
|
||||
|
||||
var encoded = Data()
|
||||
|
||||
while inputStream.hasBytesAvailable {
|
||||
var buffer = [UInt8](repeating: 0, count: streamBufferSize)
|
||||
let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
|
||||
|
||||
if let error = inputStream.streamError {
|
||||
throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error))
|
||||
}
|
||||
|
||||
if bytesRead > 0 {
|
||||
encoded.append(buffer, count: bytesRead)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return encoded
|
||||
}
|
||||
|
||||
// MARK: - Private - Writing Body Part to Output Stream
|
||||
|
||||
private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
||||
try writeInitialBoundaryData(for: bodyPart, to: outputStream)
|
||||
try writeHeaderData(for: bodyPart, to: outputStream)
|
||||
try writeBodyStream(for: bodyPart, to: outputStream)
|
||||
try writeFinalBoundaryData(for: bodyPart, to: outputStream)
|
||||
}
|
||||
|
||||
private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
||||
let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
|
||||
return try write(initialData, to: outputStream)
|
||||
}
|
||||
|
||||
private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
||||
let headerData = encodeHeaders(for: bodyPart)
|
||||
return try write(headerData, to: outputStream)
|
||||
}
|
||||
|
||||
private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
||||
let inputStream = bodyPart.bodyStream
|
||||
|
||||
inputStream.open()
|
||||
defer { inputStream.close() }
|
||||
|
||||
while inputStream.hasBytesAvailable {
|
||||
var buffer = [UInt8](repeating: 0, count: streamBufferSize)
|
||||
let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
|
||||
|
||||
if let streamError = inputStream.streamError {
|
||||
throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError))
|
||||
}
|
||||
|
||||
if bytesRead > 0 {
|
||||
if buffer.count != bytesRead {
|
||||
buffer = Array(buffer[0..<bytesRead])
|
||||
}
|
||||
|
||||
try write(&buffer, to: outputStream)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func writeFinalBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
|
||||
if bodyPart.hasFinalBoundary {
|
||||
return try write(finalBoundaryData(), to: outputStream)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private - Writing Buffered Data to Output Stream
|
||||
|
||||
private func write(_ data: Data, to outputStream: OutputStream) throws {
|
||||
var buffer = [UInt8](repeating: 0, count: data.count)
|
||||
data.copyBytes(to: &buffer, count: data.count)
|
||||
|
||||
return try write(&buffer, to: outputStream)
|
||||
}
|
||||
|
||||
private func write(_ buffer: inout [UInt8], to outputStream: OutputStream) throws {
|
||||
var bytesToWrite = buffer.count
|
||||
|
||||
while bytesToWrite > 0, outputStream.hasSpaceAvailable {
|
||||
let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite)
|
||||
|
||||
if let error = outputStream.streamError {
|
||||
throw AFError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error))
|
||||
}
|
||||
|
||||
bytesToWrite -= bytesWritten
|
||||
|
||||
if bytesToWrite > 0 {
|
||||
buffer = Array(buffer[bytesWritten..<buffer.count])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private - Mime Type
|
||||
|
||||
private func mimeType(forPathExtension pathExtension: String) -> String {
|
||||
if
|
||||
let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
|
||||
let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue()
|
||||
{
|
||||
return contentType as String
|
||||
}
|
||||
|
||||
return "application/octet-stream"
|
||||
}
|
||||
|
||||
// MARK: - Private - Content Headers
|
||||
|
||||
private func contentHeaders(withName name: String, fileName: String? = nil, mimeType: String? = nil) -> [String: String] {
|
||||
var disposition = "form-data; name=\"\(name)\""
|
||||
if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" }
|
||||
|
||||
var headers = ["Content-Disposition": disposition]
|
||||
if let mimeType = mimeType { headers["Content-Type"] = mimeType }
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
// MARK: - Private - Boundary Encoding
|
||||
|
||||
private func initialBoundaryData() -> Data {
|
||||
return BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary)
|
||||
}
|
||||
|
||||
private func encapsulatedBoundaryData() -> Data {
|
||||
return BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary)
|
||||
}
|
||||
|
||||
private func finalBoundaryData() -> Data {
|
||||
return BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary)
|
||||
}
|
||||
|
||||
// MARK: - Private - Errors
|
||||
|
||||
private func setBodyPartError(withReason reason: AFError.MultipartEncodingFailureReason) {
|
||||
guard bodyPartError == nil else { return }
|
||||
bodyPartError = AFError.multipartEncodingFailed(reason: reason)
|
||||
}
|
||||
}
|
||||
233
Pods/Alamofire/Source/NetworkReachabilityManager.swift
generated
Normal file
233
Pods/Alamofire/Source/NetworkReachabilityManager.swift
generated
Normal file
@@ -0,0 +1,233 @@
|
||||
//
|
||||
// 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 !os(watchOS)
|
||||
|
||||
import Foundation
|
||||
import SystemConfiguration
|
||||
|
||||
/// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both WWAN and
|
||||
/// WiFi network interfaces.
|
||||
///
|
||||
/// Reachability can be used to determine background information about why a network operation failed, or to retry
|
||||
/// network requests when a connection is established. It should not be used to prevent a user from initiating a network
|
||||
/// request, as it's possible that an initial request may be required to establish reachability.
|
||||
open class NetworkReachabilityManager {
|
||||
/// Defines the various states of network reachability.
|
||||
///
|
||||
/// - unknown: It is unknown whether the network is reachable.
|
||||
/// - notReachable: The network is not reachable.
|
||||
/// - reachable: The network is reachable.
|
||||
public enum NetworkReachabilityStatus {
|
||||
case unknown
|
||||
case notReachable
|
||||
case reachable(ConnectionType)
|
||||
}
|
||||
|
||||
/// Defines the various connection types detected by reachability flags.
|
||||
///
|
||||
/// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi.
|
||||
/// - wwan: The connection type is a WWAN connection.
|
||||
public enum ConnectionType {
|
||||
case ethernetOrWiFi
|
||||
case wwan
|
||||
}
|
||||
|
||||
/// A closure executed when the network reachability status changes. The closure takes a single argument: the
|
||||
/// network reachability status.
|
||||
public typealias Listener = (NetworkReachabilityStatus) -> Void
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// Whether the network is currently reachable.
|
||||
open var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi }
|
||||
|
||||
/// Whether the network is currently reachable over the WWAN interface.
|
||||
open var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) }
|
||||
|
||||
/// Whether the network is currently reachable over Ethernet or WiFi interface.
|
||||
open var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) }
|
||||
|
||||
/// The current network reachability status.
|
||||
open var networkReachabilityStatus: NetworkReachabilityStatus {
|
||||
guard let flags = self.flags else { return .unknown }
|
||||
return networkReachabilityStatusForFlags(flags)
|
||||
}
|
||||
|
||||
/// The dispatch queue to execute the `listener` closure on.
|
||||
open var listenerQueue: DispatchQueue = DispatchQueue.main
|
||||
|
||||
/// A closure executed when the network reachability status changes.
|
||||
open var listener: Listener?
|
||||
|
||||
open var flags: SCNetworkReachabilityFlags? {
|
||||
var flags = SCNetworkReachabilityFlags()
|
||||
|
||||
if SCNetworkReachabilityGetFlags(reachability, &flags) {
|
||||
return flags
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private let reachability: SCNetworkReachability
|
||||
open var previousFlags: SCNetworkReachabilityFlags
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Creates a `NetworkReachabilityManager` instance with the specified host.
|
||||
///
|
||||
/// - parameter host: The host used to evaluate network reachability.
|
||||
///
|
||||
/// - returns: The new `NetworkReachabilityManager` instance.
|
||||
public convenience init?(host: String) {
|
||||
guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil }
|
||||
self.init(reachability: reachability)
|
||||
}
|
||||
|
||||
/// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0.
|
||||
///
|
||||
/// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing
|
||||
/// status of the device, both IPv4 and IPv6.
|
||||
///
|
||||
/// - returns: The new `NetworkReachabilityManager` instance.
|
||||
public convenience init?() {
|
||||
var address = sockaddr_in()
|
||||
address.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
|
||||
address.sin_family = sa_family_t(AF_INET)
|
||||
|
||||
guard let reachability = withUnsafePointer(to: &address, { pointer in
|
||||
return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout<sockaddr>.size) {
|
||||
return SCNetworkReachabilityCreateWithAddress(nil, $0)
|
||||
}
|
||||
}) else { return nil }
|
||||
|
||||
self.init(reachability: reachability)
|
||||
}
|
||||
|
||||
private init(reachability: SCNetworkReachability) {
|
||||
self.reachability = reachability
|
||||
self.previousFlags = SCNetworkReachabilityFlags()
|
||||
}
|
||||
|
||||
deinit {
|
||||
stopListening()
|
||||
}
|
||||
|
||||
// MARK: - Listening
|
||||
|
||||
/// Starts listening for changes in network reachability status.
|
||||
///
|
||||
/// - returns: `true` if listening was started successfully, `false` otherwise.
|
||||
@discardableResult
|
||||
open func startListening() -> Bool {
|
||||
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
|
||||
context.info = Unmanaged.passUnretained(self).toOpaque()
|
||||
|
||||
let callbackEnabled = SCNetworkReachabilitySetCallback(
|
||||
reachability,
|
||||
{ (_, flags, info) in
|
||||
let reachability = Unmanaged<NetworkReachabilityManager>.fromOpaque(info!).takeUnretainedValue()
|
||||
reachability.notifyListener(flags)
|
||||
},
|
||||
&context
|
||||
)
|
||||
|
||||
let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue)
|
||||
|
||||
listenerQueue.async {
|
||||
self.previousFlags = SCNetworkReachabilityFlags()
|
||||
self.notifyListener(self.flags ?? SCNetworkReachabilityFlags())
|
||||
}
|
||||
|
||||
return callbackEnabled && queueEnabled
|
||||
}
|
||||
|
||||
/// Stops listening for changes in network reachability status.
|
||||
open func stopListening() {
|
||||
SCNetworkReachabilitySetCallback(reachability, nil, nil)
|
||||
SCNetworkReachabilitySetDispatchQueue(reachability, nil)
|
||||
}
|
||||
|
||||
// MARK: - Internal - Listener Notification
|
||||
|
||||
func notifyListener(_ flags: SCNetworkReachabilityFlags) {
|
||||
guard previousFlags != flags else { return }
|
||||
previousFlags = flags
|
||||
|
||||
listener?(networkReachabilityStatusForFlags(flags))
|
||||
}
|
||||
|
||||
// MARK: - Internal - Network Reachability Status
|
||||
|
||||
func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus {
|
||||
guard isNetworkReachable(with: flags) else { return .notReachable }
|
||||
|
||||
var networkStatus: NetworkReachabilityStatus = .reachable(.ethernetOrWiFi)
|
||||
|
||||
#if os(iOS)
|
||||
if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) }
|
||||
#endif
|
||||
|
||||
return networkStatus
|
||||
}
|
||||
|
||||
func isNetworkReachable(with flags: SCNetworkReachabilityFlags) -> Bool {
|
||||
let isReachable = flags.contains(.reachable)
|
||||
let needsConnection = flags.contains(.connectionRequired)
|
||||
let canConnectAutomatically = flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic)
|
||||
let canConnectWithoutUserInteraction = canConnectAutomatically && !flags.contains(.interventionRequired)
|
||||
|
||||
return isReachable && (!needsConnection || canConnectWithoutUserInteraction)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {}
|
||||
|
||||
/// Returns whether the two network reachability status values are equal.
|
||||
///
|
||||
/// - parameter lhs: The left-hand side value to compare.
|
||||
/// - parameter rhs: The right-hand side value to compare.
|
||||
///
|
||||
/// - returns: `true` if the two values are equal, `false` otherwise.
|
||||
public func ==(
|
||||
lhs: NetworkReachabilityManager.NetworkReachabilityStatus,
|
||||
rhs: NetworkReachabilityManager.NetworkReachabilityStatus)
|
||||
-> Bool
|
||||
{
|
||||
switch (lhs, rhs) {
|
||||
case (.unknown, .unknown):
|
||||
return true
|
||||
case (.notReachable, .notReachable):
|
||||
return true
|
||||
case let (.reachable(lhsConnectionType), .reachable(rhsConnectionType)):
|
||||
return lhsConnectionType == rhsConnectionType
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
55
Pods/Alamofire/Source/Notifications.swift
generated
Normal file
55
Pods/Alamofire/Source/Notifications.swift
generated
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// 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 Notification.Name {
|
||||
/// Used as a namespace for all `URLSessionTask` related notifications.
|
||||
public struct Task {
|
||||
/// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`.
|
||||
public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume")
|
||||
|
||||
/// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`.
|
||||
public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend")
|
||||
|
||||
/// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`.
|
||||
public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel")
|
||||
|
||||
/// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`.
|
||||
public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension Notification {
|
||||
/// Used as a namespace for all `Notification` user info dictionary keys.
|
||||
public struct Key {
|
||||
/// User info dictionary key representing the `URLSessionTask` associated with the notification.
|
||||
public static let Task = "org.alamofire.notification.key.task"
|
||||
|
||||
/// User info dictionary key representing the responseData associated with the notification.
|
||||
public static let ResponseData = "org.alamofire.notification.key.responseData"
|
||||
}
|
||||
}
|
||||
483
Pods/Alamofire/Source/ParameterEncoding.swift
generated
Normal file
483
Pods/Alamofire/Source/ParameterEncoding.swift
generated
Normal file
@@ -0,0 +1,483 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// HTTP method definitions.
|
||||
///
|
||||
/// See https://tools.ietf.org/html/rfc7231#section-4.3
|
||||
public enum HTTPMethod: String {
|
||||
case options = "OPTIONS"
|
||||
case get = "GET"
|
||||
case head = "HEAD"
|
||||
case post = "POST"
|
||||
case put = "PUT"
|
||||
case patch = "PATCH"
|
||||
case delete = "DELETE"
|
||||
case trace = "TRACE"
|
||||
case connect = "CONNECT"
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// A dictionary of parameters to apply to a `URLRequest`.
|
||||
public typealias Parameters = [String: Any]
|
||||
|
||||
/// A type used to define how a set of parameters are applied to a `URLRequest`.
|
||||
public protocol ParameterEncoding {
|
||||
/// Creates a URL request by encoding parameters and applying them onto an existing request.
|
||||
///
|
||||
/// - parameter urlRequest: The request to have parameters applied.
|
||||
/// - parameter parameters: The parameters to apply.
|
||||
///
|
||||
/// - throws: An `AFError.parameterEncodingFailed` error if encoding fails.
|
||||
///
|
||||
/// - returns: The encoded request.
|
||||
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP
|
||||
/// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as
|
||||
/// the HTTP body depends on the destination of the encoding.
|
||||
///
|
||||
/// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to
|
||||
/// `application/x-www-form-urlencoded; charset=utf-8`.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// - methodDependent: Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE`
|
||||
/// requests and sets as the HTTP body for requests with any other HTTP method.
|
||||
/// - queryString: Sets or appends encoded query string result to existing query string.
|
||||
/// - httpBody: Sets encoded query string result as the HTTP body of the URL request.
|
||||
public enum Destination {
|
||||
case methodDependent, queryString, httpBody
|
||||
}
|
||||
|
||||
/// Configures how `Array` parameters are encoded.
|
||||
///
|
||||
/// - brackets: An empty set of square brackets is appended to the key for every value.
|
||||
/// This is the default behavior.
|
||||
/// - noBrackets: No brackets are appended. The key is encoded as is.
|
||||
public enum ArrayEncoding {
|
||||
case brackets, noBrackets
|
||||
|
||||
func encode(key: String) -> String {
|
||||
switch self {
|
||||
case .brackets:
|
||||
return "\(key)[]"
|
||||
case .noBrackets:
|
||||
return key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures how `Bool` parameters are encoded.
|
||||
///
|
||||
/// - numeric: Encode `true` as `1` and `false` as `0`. This is the default behavior.
|
||||
/// - literal: Encode `true` and `false` as string literals.
|
||||
public enum BoolEncoding {
|
||||
case numeric, 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.
|
||||
public static var `default`: URLEncoding { return URLEncoding() }
|
||||
|
||||
/// Returns a `URLEncoding` instance with a `.methodDependent` destination.
|
||||
public static var methodDependent: URLEncoding { return URLEncoding() }
|
||||
|
||||
/// Returns a `URLEncoding` instance with a `.queryString` destination.
|
||||
public static var queryString: URLEncoding { return URLEncoding(destination: .queryString) }
|
||||
|
||||
/// Returns a `URLEncoding` instance with an `.httpBody` destination.
|
||||
public static var httpBody: URLEncoding { return URLEncoding(destination: .httpBody) }
|
||||
|
||||
/// The destination defining where the encoded query string is to be applied to the URL request.
|
||||
public let destination: Destination
|
||||
|
||||
/// 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 a `URLEncoding` instance using the specified destination.
|
||||
///
|
||||
/// - parameter destination: The destination defining where the encoded query string is to be applied.
|
||||
/// - parameter arrayEncoding: The encoding to use for `Array` parameters.
|
||||
/// - parameter boolEncoding: The encoding to use for `Bool` parameters.
|
||||
///
|
||||
/// - returns: The new `URLEncoding` instance.
|
||||
public init(destination: Destination = .methodDependent, arrayEncoding: ArrayEncoding = .brackets, boolEncoding: BoolEncoding = .numeric) {
|
||||
self.destination = destination
|
||||
self.arrayEncoding = arrayEncoding
|
||||
self.boolEncoding = boolEncoding
|
||||
}
|
||||
|
||||
// MARK: Encoding
|
||||
|
||||
/// Creates a URL request by encoding parameters and applying them onto an existing request.
|
||||
///
|
||||
/// - parameter urlRequest: The request to have parameters applied.
|
||||
/// - parameter parameters: The parameters to apply.
|
||||
///
|
||||
/// - throws: An `Error` if the encoding process encounters an error.
|
||||
///
|
||||
/// - returns: The encoded request.
|
||||
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
|
||||
var urlRequest = try urlRequest.asURLRequest()
|
||||
|
||||
guard let parameters = parameters else { return urlRequest }
|
||||
|
||||
if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
|
||||
guard let url = urlRequest.url else {
|
||||
throw AFError.parameterEncodingFailed(reason: .missingURL)
|
||||
}
|
||||
|
||||
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
|
||||
let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
|
||||
urlComponents.percentEncodedQuery = percentEncodedQuery
|
||||
urlRequest.url = urlComponents.url
|
||||
}
|
||||
} else {
|
||||
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
|
||||
urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
}
|
||||
|
||||
urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
|
||||
}
|
||||
|
||||
return urlRequest
|
||||
}
|
||||
|
||||
/// Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion.
|
||||
///
|
||||
/// - parameter key: The key of the query component.
|
||||
/// - parameter value: The value of the query component.
|
||||
///
|
||||
/// - returns: The percent-escaped, URL encoded query string components.
|
||||
public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
|
||||
var components: [(String, String)] = []
|
||||
|
||||
if let dictionary = value as? [String: Any] {
|
||||
for (nestedKey, value) in dictionary {
|
||||
components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
|
||||
}
|
||||
} else if let array = value as? [Any] {
|
||||
for value in array {
|
||||
components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value)
|
||||
}
|
||||
} else if let value = value as? NSNumber {
|
||||
if value.isBool {
|
||||
components.append((escape(key), escape(boolEncoding.encode(value: value.boolValue))))
|
||||
} else {
|
||||
components.append((escape(key), escape("\(value)")))
|
||||
}
|
||||
} else if let bool = value as? Bool {
|
||||
components.append((escape(key), escape(boolEncoding.encode(value: bool))))
|
||||
} else {
|
||||
components.append((escape(key), escape("\(value)")))
|
||||
}
|
||||
|
||||
return components
|
||||
}
|
||||
|
||||
/// Returns a percent-escaped string following RFC 3986 for a query string key or value.
|
||||
///
|
||||
/// RFC 3986 states that the following characters are "reserved" characters.
|
||||
///
|
||||
/// - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
|
||||
/// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
|
||||
///
|
||||
/// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
|
||||
/// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
|
||||
/// should be percent-escaped in the query string.
|
||||
///
|
||||
/// - parameter string: The string to be percent-escaped.
|
||||
///
|
||||
/// - returns: The percent-escaped string.
|
||||
public func escape(_ string: String) -> String {
|
||||
let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
|
||||
let subDelimitersToEncode = "!$&'()*+,;="
|
||||
|
||||
var allowedCharacterSet = CharacterSet.urlQueryAllowed
|
||||
allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
|
||||
|
||||
var escaped = ""
|
||||
|
||||
//==========================================================================================================
|
||||
//
|
||||
// Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few
|
||||
// hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no
|
||||
// longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more
|
||||
// info, please refer to:
|
||||
//
|
||||
// - https://github.com/Alamofire/Alamofire/issues/206
|
||||
//
|
||||
//==========================================================================================================
|
||||
|
||||
if #available(iOS 8.3, *) {
|
||||
escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
|
||||
} else {
|
||||
let batchSize = 50
|
||||
var index = string.startIndex
|
||||
|
||||
while index != string.endIndex {
|
||||
let startIndex = index
|
||||
let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex
|
||||
let range = startIndex..<endIndex
|
||||
|
||||
let substring = string[range]
|
||||
|
||||
escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? String(substring)
|
||||
|
||||
index = endIndex
|
||||
}
|
||||
}
|
||||
|
||||
return escaped
|
||||
}
|
||||
|
||||
private func query(_ parameters: [String: Any]) -> String {
|
||||
var components: [(String, String)] = []
|
||||
|
||||
for key in parameters.keys.sorted(by: <) {
|
||||
let value = parameters[key]!
|
||||
components += queryComponents(fromKey: key, value: value)
|
||||
}
|
||||
return components.map { "\($0)=\($1)" }.joined(separator: "&")
|
||||
}
|
||||
|
||||
private func encodesParametersInURL(with method: HTTPMethod) -> Bool {
|
||||
switch destination {
|
||||
case .queryString:
|
||||
return true
|
||||
case .httpBody:
|
||||
return false
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
switch method {
|
||||
case .get, .head, .delete:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the
|
||||
/// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`.
|
||||
public struct JSONEncoding: ParameterEncoding {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// Returns a `JSONEncoding` instance with default writing options.
|
||||
public static var `default`: JSONEncoding { return JSONEncoding() }
|
||||
|
||||
/// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options.
|
||||
public static var prettyPrinted: JSONEncoding { return JSONEncoding(options: .prettyPrinted) }
|
||||
|
||||
/// The options for writing the parameters as JSON data.
|
||||
public let options: JSONSerialization.WritingOptions
|
||||
|
||||
// MARK: Initialization
|
||||
|
||||
/// Creates a `JSONEncoding` instance using the specified options.
|
||||
///
|
||||
/// - parameter options: The options for writing the parameters as JSON data.
|
||||
///
|
||||
/// - returns: The new `JSONEncoding` instance.
|
||||
public init(options: JSONSerialization.WritingOptions = []) {
|
||||
self.options = options
|
||||
}
|
||||
|
||||
// MARK: Encoding
|
||||
|
||||
/// Creates a URL request by encoding parameters and applying them onto an existing request.
|
||||
///
|
||||
/// - parameter urlRequest: The request to have parameters applied.
|
||||
/// - parameter parameters: The parameters to apply.
|
||||
///
|
||||
/// - throws: An `Error` if the encoding process encounters an error.
|
||||
///
|
||||
/// - returns: The encoded request.
|
||||
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
|
||||
var urlRequest = try urlRequest.asURLRequest()
|
||||
|
||||
guard let parameters = parameters else { return urlRequest }
|
||||
|
||||
do {
|
||||
let data = try JSONSerialization.data(withJSONObject: parameters, options: options)
|
||||
|
||||
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
|
||||
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
}
|
||||
|
||||
urlRequest.httpBody = data
|
||||
} catch {
|
||||
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
|
||||
}
|
||||
|
||||
return urlRequest
|
||||
}
|
||||
|
||||
/// Creates a URL request by encoding the JSON object and setting the resulting data on the HTTP body.
|
||||
///
|
||||
/// - parameter urlRequest: The request to apply the JSON object to.
|
||||
/// - parameter jsonObject: The JSON object to apply to the request.
|
||||
///
|
||||
/// - throws: An `Error` if the encoding process encounters an error.
|
||||
///
|
||||
/// - returns: The encoded request.
|
||||
public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest {
|
||||
var urlRequest = try urlRequest.asURLRequest()
|
||||
|
||||
guard let jsonObject = jsonObject else { return urlRequest }
|
||||
|
||||
do {
|
||||
let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options)
|
||||
|
||||
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
|
||||
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
}
|
||||
|
||||
urlRequest.httpBody = data
|
||||
} catch {
|
||||
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
|
||||
}
|
||||
|
||||
return urlRequest
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Uses `PropertyListSerialization` to create a plist representation of the parameters object, according to the
|
||||
/// associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header
|
||||
/// field of an encoded request is set to `application/x-plist`.
|
||||
public struct PropertyListEncoding: ParameterEncoding {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// Returns a default `PropertyListEncoding` instance.
|
||||
public static var `default`: PropertyListEncoding { return PropertyListEncoding() }
|
||||
|
||||
/// Returns a `PropertyListEncoding` instance with xml formatting and default writing options.
|
||||
public static var xml: PropertyListEncoding { return PropertyListEncoding(format: .xml) }
|
||||
|
||||
/// Returns a `PropertyListEncoding` instance with binary formatting and default writing options.
|
||||
public static var binary: PropertyListEncoding { return PropertyListEncoding(format: .binary) }
|
||||
|
||||
/// The property list serialization format.
|
||||
public let format: PropertyListSerialization.PropertyListFormat
|
||||
|
||||
/// The options for writing the parameters as plist data.
|
||||
public let options: PropertyListSerialization.WriteOptions
|
||||
|
||||
// MARK: Initialization
|
||||
|
||||
/// Creates a `PropertyListEncoding` instance using the specified format and options.
|
||||
///
|
||||
/// - parameter format: The property list serialization format.
|
||||
/// - parameter options: The options for writing the parameters as plist data.
|
||||
///
|
||||
/// - returns: The new `PropertyListEncoding` instance.
|
||||
public init(
|
||||
format: PropertyListSerialization.PropertyListFormat = .xml,
|
||||
options: PropertyListSerialization.WriteOptions = 0)
|
||||
{
|
||||
self.format = format
|
||||
self.options = options
|
||||
}
|
||||
|
||||
// MARK: Encoding
|
||||
|
||||
/// Creates a URL request by encoding parameters and applying them onto an existing request.
|
||||
///
|
||||
/// - parameter urlRequest: The request to have parameters applied.
|
||||
/// - parameter parameters: The parameters to apply.
|
||||
///
|
||||
/// - throws: An `Error` if the encoding process encounters an error.
|
||||
///
|
||||
/// - returns: The encoded request.
|
||||
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
|
||||
var urlRequest = try urlRequest.asURLRequest()
|
||||
|
||||
guard let parameters = parameters else { return urlRequest }
|
||||
|
||||
do {
|
||||
let data = try PropertyListSerialization.data(
|
||||
fromPropertyList: parameters,
|
||||
format: format,
|
||||
options: options
|
||||
)
|
||||
|
||||
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
|
||||
urlRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type")
|
||||
}
|
||||
|
||||
urlRequest.httpBody = data
|
||||
} catch {
|
||||
throw AFError.parameterEncodingFailed(reason: .propertyListEncodingFailed(error: error))
|
||||
}
|
||||
|
||||
return urlRequest
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension NSNumber {
|
||||
fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) }
|
||||
}
|
||||
654
Pods/Alamofire/Source/Request.swift
generated
Normal file
654
Pods/Alamofire/Source/Request.swift
generated
Normal file
@@ -0,0 +1,654 @@
|
||||
//
|
||||
// Request.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 inspect and optionally adapt a `URLRequest` in some manner if necessary.
|
||||
public protocol RequestAdapter {
|
||||
/// Inspects and adapts the specified `URLRequest` in some manner if necessary and returns the result.
|
||||
///
|
||||
/// - parameter urlRequest: The URL request to adapt.
|
||||
///
|
||||
/// - throws: An `Error` if the adaptation encounters an error.
|
||||
///
|
||||
/// - returns: The adapted `URLRequest`.
|
||||
func adapt(_ urlRequest: URLRequest) throws -> URLRequest
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// A closure executed when the `RequestRetrier` determines whether a `Request` should be retried or not.
|
||||
public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void
|
||||
|
||||
/// A type that determines whether a request should be retried after being executed by the specified session manager
|
||||
/// and encountering an error.
|
||||
public protocol RequestRetrier {
|
||||
/// Determines whether the `Request` should be retried by calling the `completion` closure.
|
||||
///
|
||||
/// This operation is fully 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.
|
||||
///
|
||||
/// - parameter manager: The session manager the request was executed on.
|
||||
/// - parameter request: The request that failed due to the encountered error.
|
||||
/// - parameter error: The error encountered when executing the request.
|
||||
/// - parameter completion: The completion closure to be executed when retry decision has been determined.
|
||||
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
protocol TaskConvertible {
|
||||
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask
|
||||
}
|
||||
|
||||
/// A dictionary of headers to apply to a `URLRequest`.
|
||||
public typealias HTTPHeaders = [String: String]
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Responsible for sending a request and receiving the response and associated data from the server, as well as
|
||||
/// managing its underlying `URLSessionTask`.
|
||||
open class Request {
|
||||
|
||||
// MARK: Helper Types
|
||||
|
||||
/// A closure executed when monitoring upload or download progress of a request.
|
||||
public typealias ProgressHandler = (Progress) -> Void
|
||||
|
||||
enum RequestTask {
|
||||
case data(TaskConvertible?, URLSessionTask?)
|
||||
case download(TaskConvertible?, URLSessionTask?)
|
||||
case upload(TaskConvertible?, URLSessionTask?)
|
||||
case stream(TaskConvertible?, URLSessionTask?)
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// The delegate for the underlying task.
|
||||
open internal(set) var delegate: TaskDelegate {
|
||||
get {
|
||||
taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
|
||||
return taskDelegate
|
||||
}
|
||||
set {
|
||||
taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
|
||||
taskDelegate = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// The underlying task.
|
||||
open var task: URLSessionTask? { return delegate.task }
|
||||
|
||||
/// The session belonging to the underlying task.
|
||||
public let session: URLSession
|
||||
|
||||
/// The request sent or to be sent to the server.
|
||||
open var request: URLRequest? { return task?.originalRequest }
|
||||
|
||||
/// The response received from the server, if any.
|
||||
open var response: HTTPURLResponse? { return task?.response as? HTTPURLResponse }
|
||||
|
||||
/// The number of times the request has been retried.
|
||||
open internal(set) var retryCount: UInt = 0
|
||||
|
||||
let originalTask: TaskConvertible?
|
||||
|
||||
var startTime: CFAbsoluteTime?
|
||||
var endTime: CFAbsoluteTime?
|
||||
|
||||
var validations: [() -> Void] = []
|
||||
|
||||
private var taskDelegate: TaskDelegate
|
||||
private var taskDelegateLock = NSLock()
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
|
||||
self.session = session
|
||||
|
||||
switch requestTask {
|
||||
case .data(let originalTask, let task):
|
||||
taskDelegate = DataTaskDelegate(task: task)
|
||||
self.originalTask = originalTask
|
||||
case .download(let originalTask, let task):
|
||||
taskDelegate = DownloadTaskDelegate(task: task)
|
||||
self.originalTask = originalTask
|
||||
case .upload(let originalTask, let task):
|
||||
taskDelegate = UploadTaskDelegate(task: task)
|
||||
self.originalTask = originalTask
|
||||
case .stream(let originalTask, let task):
|
||||
taskDelegate = TaskDelegate(task: task)
|
||||
self.originalTask = originalTask
|
||||
}
|
||||
|
||||
delegate.error = error
|
||||
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
|
||||
}
|
||||
|
||||
// MARK: Authentication
|
||||
|
||||
/// Associates an HTTP Basic credential with the request.
|
||||
///
|
||||
/// - parameter user: The user.
|
||||
/// - parameter password: The password.
|
||||
/// - parameter persistence: The URL credential persistence. `.ForSession` by default.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
open func authenticate(
|
||||
user: String,
|
||||
password: String,
|
||||
persistence: URLCredential.Persistence = .forSession)
|
||||
-> Self
|
||||
{
|
||||
let credential = URLCredential(user: user, password: password, persistence: persistence)
|
||||
return authenticate(usingCredential: credential)
|
||||
}
|
||||
|
||||
/// Associates a specified credential with the request.
|
||||
///
|
||||
/// - parameter credential: The credential.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
open func authenticate(usingCredential credential: URLCredential) -> Self {
|
||||
delegate.credential = credential
|
||||
return self
|
||||
}
|
||||
|
||||
/// Returns a base64 encoded basic authentication credential as an authorization header tuple.
|
||||
///
|
||||
/// - parameter user: The user.
|
||||
/// - parameter password: The password.
|
||||
///
|
||||
/// - returns: A tuple with Authorization header and credential value if encoding succeeds, `nil` otherwise.
|
||||
open class func authorizationHeader(user: String, password: String) -> (key: String, value: String)? {
|
||||
guard let data = "\(user):\(password)".data(using: .utf8) else { return nil }
|
||||
|
||||
let credential = data.base64EncodedString(options: [])
|
||||
|
||||
return (key: "Authorization", value: "Basic \(credential)")
|
||||
}
|
||||
|
||||
// MARK: State
|
||||
|
||||
/// Resumes the request.
|
||||
open func resume() {
|
||||
guard let task = task else { delegate.queue.isSuspended = false ; return }
|
||||
|
||||
if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }
|
||||
|
||||
task.resume()
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: Notification.Name.Task.DidResume,
|
||||
object: self,
|
||||
userInfo: [Notification.Key.Task: task]
|
||||
)
|
||||
}
|
||||
|
||||
/// Suspends the request.
|
||||
open func suspend() {
|
||||
guard let task = task else { return }
|
||||
|
||||
task.suspend()
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: Notification.Name.Task.DidSuspend,
|
||||
object: self,
|
||||
userInfo: [Notification.Key.Task: task]
|
||||
)
|
||||
}
|
||||
|
||||
/// Cancels the request.
|
||||
open func cancel() {
|
||||
guard let task = task else { return }
|
||||
|
||||
task.cancel()
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: Notification.Name.Task.DidCancel,
|
||||
object: self,
|
||||
userInfo: [Notification.Key.Task: task]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CustomStringConvertible
|
||||
|
||||
extension Request: CustomStringConvertible {
|
||||
/// The textual representation used when written to an output stream, which includes the HTTP method and URL, as
|
||||
/// well as the response status code if a response has been received.
|
||||
open var description: String {
|
||||
var components: [String] = []
|
||||
|
||||
if let HTTPMethod = request?.httpMethod {
|
||||
components.append(HTTPMethod)
|
||||
}
|
||||
|
||||
if let urlString = request?.url?.absoluteString {
|
||||
components.append(urlString)
|
||||
}
|
||||
|
||||
if let response = response {
|
||||
components.append("(\(response.statusCode))")
|
||||
}
|
||||
|
||||
return components.joined(separator: " ")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CustomDebugStringConvertible
|
||||
|
||||
extension Request: CustomDebugStringConvertible {
|
||||
/// The textual representation used when written to an output stream, in the form of a cURL command.
|
||||
open var debugDescription: String {
|
||||
return cURLRepresentation()
|
||||
}
|
||||
|
||||
func cURLRepresentation() -> String {
|
||||
var components = ["$ curl -v"]
|
||||
|
||||
guard let request = self.request,
|
||||
let url = request.url,
|
||||
let host = url.host
|
||||
else {
|
||||
return "$ curl command could not be created"
|
||||
}
|
||||
|
||||
if let httpMethod = request.httpMethod, httpMethod != "GET" {
|
||||
components.append("-X \(httpMethod)")
|
||||
}
|
||||
|
||||
if let credentialStorage = self.session.configuration.urlCredentialStorage {
|
||||
let protectionSpace = URLProtectionSpace(
|
||||
host: host,
|
||||
port: url.port ?? 0,
|
||||
protocol: url.scheme,
|
||||
realm: host,
|
||||
authenticationMethod: NSURLAuthenticationMethodHTTPBasic
|
||||
)
|
||||
|
||||
if let credentials = credentialStorage.credentials(for: protectionSpace)?.values {
|
||||
for credential in credentials {
|
||||
guard let user = credential.user, let password = credential.password else { continue }
|
||||
components.append("-u \(user):\(password)")
|
||||
}
|
||||
} else {
|
||||
if let credential = delegate.credential, let user = credential.user, let password = credential.password {
|
||||
components.append("-u \(user):\(password)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if session.configuration.httpShouldSetCookies {
|
||||
if
|
||||
let cookieStorage = session.configuration.httpCookieStorage,
|
||||
let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty
|
||||
{
|
||||
let string = cookies.reduce("") { $0 + "\($1.name)=\($1.value);" }
|
||||
|
||||
#if swift(>=3.2)
|
||||
components.append("-b \"\(string[..<string.index(before: string.endIndex)])\"")
|
||||
#else
|
||||
components.append("-b \"\(string.substring(to: string.characters.index(before: string.endIndex)))\"")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
var headers: [AnyHashable: Any] = [:]
|
||||
|
||||
if let additionalHeaders = session.configuration.httpAdditionalHeaders {
|
||||
for (field, value) in additionalHeaders where field != AnyHashable("Cookie") {
|
||||
headers[field] = value
|
||||
}
|
||||
}
|
||||
|
||||
if let headerFields = request.allHTTPHeaderFields {
|
||||
for (field, value) in headerFields where field != "Cookie" {
|
||||
headers[field] = value
|
||||
}
|
||||
}
|
||||
|
||||
for (field, value) in headers {
|
||||
let escapedValue = String(describing: value).replacingOccurrences(of: "\"", with: "\\\"")
|
||||
components.append("-H \"\(field): \(escapedValue)\"")
|
||||
}
|
||||
|
||||
if let httpBodyData = request.httpBody, let httpBody = String(data: httpBodyData, encoding: .utf8) {
|
||||
var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"")
|
||||
escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"")
|
||||
|
||||
components.append("-d \"\(escapedBody)\"")
|
||||
}
|
||||
|
||||
components.append("\"\(url.absoluteString)\"")
|
||||
|
||||
return components.joined(separator: " \\\n\t")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Specific type of `Request` that manages an underlying `URLSessionDataTask`.
|
||||
open class DataRequest: Request {
|
||||
|
||||
// MARK: Helper Types
|
||||
|
||||
struct Requestable: TaskConvertible {
|
||||
let urlRequest: URLRequest
|
||||
|
||||
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||
do {
|
||||
let urlRequest = try self.urlRequest.adapt(using: adapter)
|
||||
return queue.sync { session.dataTask(with: urlRequest) }
|
||||
} catch {
|
||||
throw AdaptError(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// The request sent or to be sent to the server.
|
||||
open override var request: URLRequest? {
|
||||
if let request = super.request { return request }
|
||||
if let requestable = originalTask as? Requestable { return requestable.urlRequest }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/// The progress of fetching the response data from the server for the request.
|
||||
open var progress: Progress { return dataDelegate.progress }
|
||||
|
||||
var dataDelegate: DataTaskDelegate { return delegate as! DataTaskDelegate }
|
||||
|
||||
// MARK: Stream
|
||||
|
||||
/// Sets a closure to be called periodically during the lifecycle of the request as data is read from the server.
|
||||
///
|
||||
/// This closure returns the bytes most recently received from the server, not including data from previous calls.
|
||||
/// If this closure is set, data will only be available within this closure, and will not be saved elsewhere. It is
|
||||
/// also important to note that the server data in any `Response` object will be `nil`.
|
||||
///
|
||||
/// - parameter closure: The code to be executed periodically during the lifecycle of the request.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
open func stream(closure: ((Data) -> Void)? = nil) -> Self {
|
||||
dataDelegate.dataStream = closure
|
||||
return self
|
||||
}
|
||||
|
||||
// MARK: Progress
|
||||
|
||||
/// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
|
||||
///
|
||||
/// - parameter queue: The dispatch queue to execute the closure on.
|
||||
/// - parameter closure: The code to be executed periodically as data is read from the server.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
|
||||
dataDelegate.progressHandler = (closure, queue)
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Specific type of `Request` that manages an underlying `URLSessionDownloadTask`.
|
||||
open class DownloadRequest: Request {
|
||||
|
||||
// MARK: Helper Types
|
||||
|
||||
/// A collection of options to be executed prior to moving a downloaded file from the temporary URL to the
|
||||
/// destination URL.
|
||||
public struct DownloadOptions: OptionSet {
|
||||
/// Returns the raw bitmask value of the option and satisfies the `RawRepresentable` protocol.
|
||||
public let rawValue: UInt
|
||||
|
||||
/// A `DownloadOptions` flag that creates intermediate directories for the destination URL if specified.
|
||||
public static let createIntermediateDirectories = DownloadOptions(rawValue: 1 << 0)
|
||||
|
||||
/// A `DownloadOptions` flag that removes a previous file from the destination URL if specified.
|
||||
public static let removePreviousFile = DownloadOptions(rawValue: 1 << 1)
|
||||
|
||||
/// Creates a `DownloadFileDestinationOptions` instance with the specified raw value.
|
||||
///
|
||||
/// - parameter rawValue: The raw bitmask value for the option.
|
||||
///
|
||||
/// - returns: A new log level instance.
|
||||
public init(rawValue: UInt) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
}
|
||||
|
||||
/// A closure executed once a download request has successfully completed in order to determine where to move the
|
||||
/// temporary file written to during the download process. The closure takes two arguments: the temporary file URL
|
||||
/// and the URL response, and returns a two arguments: the file URL where the temporary file should be moved and
|
||||
/// the options defining how the file should be moved.
|
||||
public typealias DownloadFileDestination = (
|
||||
_ temporaryURL: URL,
|
||||
_ response: HTTPURLResponse)
|
||||
-> (destinationURL: URL, options: DownloadOptions)
|
||||
|
||||
enum Downloadable: TaskConvertible {
|
||||
case request(URLRequest)
|
||||
case resumeData(Data)
|
||||
|
||||
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||
do {
|
||||
let task: URLSessionTask
|
||||
|
||||
switch self {
|
||||
case let .request(urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.sync { session.downloadTask(with: urlRequest) }
|
||||
case let .resumeData(resumeData):
|
||||
task = queue.sync { session.downloadTask(withResumeData: resumeData) }
|
||||
}
|
||||
|
||||
return task
|
||||
} catch {
|
||||
throw AdaptError(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// The request sent or to be sent to the server.
|
||||
open override var request: URLRequest? {
|
||||
if let request = super.request { return request }
|
||||
|
||||
if let downloadable = originalTask as? Downloadable, case let .request(urlRequest) = downloadable {
|
||||
return urlRequest
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/// The resume data of the underlying download task if available after a failure.
|
||||
open var resumeData: Data? { return downloadDelegate.resumeData }
|
||||
|
||||
/// The progress of downloading the response data from the server for the request.
|
||||
open var progress: Progress { return downloadDelegate.progress }
|
||||
|
||||
var downloadDelegate: DownloadTaskDelegate { return delegate as! DownloadTaskDelegate }
|
||||
|
||||
// MARK: State
|
||||
|
||||
/// Cancels the request.
|
||||
open override func cancel() {
|
||||
downloadDelegate.downloadTask.cancel { self.downloadDelegate.resumeData = $0 }
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: Notification.Name.Task.DidCancel,
|
||||
object: self,
|
||||
userInfo: [Notification.Key.Task: task as Any]
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: Progress
|
||||
|
||||
/// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
|
||||
///
|
||||
/// - parameter queue: The dispatch queue to execute the closure on.
|
||||
/// - parameter closure: The code to be executed periodically as data is read from the server.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
|
||||
downloadDelegate.progressHandler = (closure, queue)
|
||||
return self
|
||||
}
|
||||
|
||||
// MARK: Destination
|
||||
|
||||
/// Creates a download file destination closure which uses the default file manager to move the temporary file to a
|
||||
/// file URL in the first available directory with the specified search path directory and search path domain mask.
|
||||
///
|
||||
/// - parameter directory: The search path directory. `.DocumentDirectory` by default.
|
||||
/// - parameter domain: The search path domain mask. `.UserDomainMask` by default.
|
||||
///
|
||||
/// - returns: A download file destination closure.
|
||||
open class func suggestedDownloadDestination(
|
||||
for directory: FileManager.SearchPathDirectory = .documentDirectory,
|
||||
in domain: FileManager.SearchPathDomainMask = .userDomainMask)
|
||||
-> DownloadFileDestination
|
||||
{
|
||||
return { temporaryURL, response in
|
||||
let directoryURLs = FileManager.default.urls(for: directory, in: domain)
|
||||
|
||||
if !directoryURLs.isEmpty {
|
||||
return (directoryURLs[0].appendingPathComponent(response.suggestedFilename!), [])
|
||||
}
|
||||
|
||||
return (temporaryURL, [])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Specific type of `Request` that manages an underlying `URLSessionUploadTask`.
|
||||
open class UploadRequest: DataRequest {
|
||||
|
||||
// MARK: Helper Types
|
||||
|
||||
enum Uploadable: TaskConvertible {
|
||||
case data(Data, URLRequest)
|
||||
case file(URL, URLRequest)
|
||||
case stream(InputStream, URLRequest)
|
||||
|
||||
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||
do {
|
||||
let task: URLSessionTask
|
||||
|
||||
switch self {
|
||||
case let .data(data, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.sync { session.uploadTask(with: urlRequest, from: data) }
|
||||
case let .file(url, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.sync { session.uploadTask(with: urlRequest, fromFile: url) }
|
||||
case let .stream(_, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.sync { session.uploadTask(withStreamedRequest: urlRequest) }
|
||||
}
|
||||
|
||||
return task
|
||||
} catch {
|
||||
throw AdaptError(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// The request sent or to be sent to the server.
|
||||
open override var request: URLRequest? {
|
||||
if let request = super.request { return request }
|
||||
|
||||
guard let uploadable = originalTask as? Uploadable else { return nil }
|
||||
|
||||
switch uploadable {
|
||||
case .data(_, let urlRequest), .file(_, let urlRequest), .stream(_, let urlRequest):
|
||||
return urlRequest
|
||||
}
|
||||
}
|
||||
|
||||
/// The progress of uploading the payload to the server for the upload request.
|
||||
open var uploadProgress: Progress { return uploadDelegate.uploadProgress }
|
||||
|
||||
var uploadDelegate: UploadTaskDelegate { return delegate as! UploadTaskDelegate }
|
||||
|
||||
// MARK: Upload Progress
|
||||
|
||||
/// Sets a closure to be called periodically during the lifecycle of the `UploadRequest` as data is sent to
|
||||
/// the server.
|
||||
///
|
||||
/// After the data is sent to the server, the `progress(queue:closure:)` APIs can be used to monitor the progress
|
||||
/// of data being read from the server.
|
||||
///
|
||||
/// - parameter queue: The dispatch queue to execute the closure on.
|
||||
/// - parameter closure: The code to be executed periodically as data is sent to the server.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
open func uploadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
|
||||
uploadDelegate.uploadProgressHandler = (closure, queue)
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
#if !os(watchOS)
|
||||
|
||||
/// Specific type of `Request` that manages an underlying `URLSessionStreamTask`.
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
open class StreamRequest: Request {
|
||||
enum Streamable: TaskConvertible {
|
||||
case stream(hostName: String, port: Int)
|
||||
case netService(NetService)
|
||||
|
||||
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||
let task: URLSessionTask
|
||||
|
||||
switch self {
|
||||
case let .stream(hostName, port):
|
||||
task = queue.sync { session.streamTask(withHostName: hostName, port: port) }
|
||||
case let .netService(netService):
|
||||
task = queue.sync { session.streamTask(with: netService) }
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
567
Pods/Alamofire/Source/Response.swift
generated
Normal file
567
Pods/Alamofire/Source/Response.swift
generated
Normal file
@@ -0,0 +1,567 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// Used to store all data associated with an non-serialized response of a data or upload request.
|
||||
public struct DefaultDataResponse {
|
||||
/// The URL request sent to the server.
|
||||
public let request: URLRequest?
|
||||
|
||||
/// The server's response to the URL request.
|
||||
public let response: HTTPURLResponse?
|
||||
|
||||
/// The data returned by the server.
|
||||
public let data: Data?
|
||||
|
||||
/// The error encountered while executing or validating the request.
|
||||
public let error: Error?
|
||||
|
||||
/// The timeline of the complete lifecycle of the request.
|
||||
public let timeline: Timeline
|
||||
|
||||
var _metrics: AnyObject?
|
||||
|
||||
/// Creates a `DefaultDataResponse` instance from the specified parameters.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - request: The URL request sent to the server.
|
||||
/// - response: The server's response to the URL request.
|
||||
/// - data: The data returned by the server.
|
||||
/// - error: The error encountered while executing or validating the request.
|
||||
/// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
|
||||
/// - metrics: The task metrics containing the request / response statistics. `nil` by default.
|
||||
public init(
|
||||
request: URLRequest?,
|
||||
response: HTTPURLResponse?,
|
||||
data: Data?,
|
||||
error: Error?,
|
||||
timeline: Timeline = Timeline(),
|
||||
metrics: AnyObject? = nil)
|
||||
{
|
||||
self.request = request
|
||||
self.response = response
|
||||
self.data = data
|
||||
self.error = error
|
||||
self.timeline = timeline
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Used to store all data associated with a serialized response of a data or upload request.
|
||||
public struct DataResponse<Value> {
|
||||
/// The URL request sent to the server.
|
||||
public let request: URLRequest?
|
||||
|
||||
/// The server's response to the URL request.
|
||||
public let response: HTTPURLResponse?
|
||||
|
||||
/// The data returned by the server.
|
||||
public let data: Data?
|
||||
|
||||
/// The result of response serialization.
|
||||
public let result: Result<Value>
|
||||
|
||||
/// The timeline of the complete lifecycle of the request.
|
||||
public let timeline: Timeline
|
||||
|
||||
/// Returns the associated value of the result if it is a success, `nil` otherwise.
|
||||
public var value: Value? { return result.value }
|
||||
|
||||
/// Returns the associated error value if the result if it is a failure, `nil` otherwise.
|
||||
public var error: Error? { return result.error }
|
||||
|
||||
var _metrics: AnyObject?
|
||||
|
||||
/// Creates a `DataResponse` instance with the specified parameters derived from response serialization.
|
||||
///
|
||||
/// - parameter request: The URL request sent to the server.
|
||||
/// - parameter response: The server's response to the URL request.
|
||||
/// - parameter data: The data returned by the server.
|
||||
/// - parameter result: The result of response serialization.
|
||||
/// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
|
||||
///
|
||||
/// - returns: The new `DataResponse` instance.
|
||||
public init(
|
||||
request: URLRequest?,
|
||||
response: HTTPURLResponse?,
|
||||
data: Data?,
|
||||
result: Result<Value>,
|
||||
timeline: Timeline = Timeline())
|
||||
{
|
||||
self.request = request
|
||||
self.response = response
|
||||
self.data = data
|
||||
self.result = result
|
||||
self.timeline = timeline
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible {
|
||||
/// The textual representation used when written to an output stream, which includes whether the result was a
|
||||
/// success or failure.
|
||||
public var description: String {
|
||||
return result.debugDescription
|
||||
}
|
||||
|
||||
/// The debug textual representation used when written to an output stream, which includes the URL request, the URL
|
||||
/// response, the server data, the response serialization result and the timeline.
|
||||
public var debugDescription: String {
|
||||
var output: [String] = []
|
||||
|
||||
output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil")
|
||||
output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")
|
||||
output.append("[Data]: \(data?.count ?? 0) bytes")
|
||||
output.append("[Result]: \(result.debugDescription)")
|
||||
output.append("[Timeline]: \(timeline.debugDescription)")
|
||||
|
||||
return output.joined(separator: "\n")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
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<T>(_ transform: (Value) -> T) -> DataResponse<T> {
|
||||
var response = DataResponse<T>(
|
||||
request: request,
|
||||
response: self.response,
|
||||
data: data,
|
||||
result: result.map(transform),
|
||||
timeline: timeline
|
||||
)
|
||||
|
||||
response._metrics = _metrics
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result
|
||||
/// value as a parameter.
|
||||
///
|
||||
/// Use the `flatMap` method with a closure that may throw an error. For example:
|
||||
///
|
||||
/// let possibleData: DataResponse<Data> = ...
|
||||
/// let possibleObject = possibleData.flatMap {
|
||||
/// 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 flatMap<T>(_ transform: (Value) throws -> T) -> DataResponse<T> {
|
||||
var response = DataResponse<T>(
|
||||
request: request,
|
||||
response: self.response,
|
||||
data: data,
|
||||
result: result.flatMap(transform),
|
||||
timeline: timeline
|
||||
)
|
||||
|
||||
response._metrics = _metrics
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/// 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<E: Error>(_ transform: (Error) -> E) -> DataResponse {
|
||||
var response = DataResponse(
|
||||
request: request,
|
||||
response: self.response,
|
||||
data: data,
|
||||
result: result.mapError(transform),
|
||||
timeline: timeline
|
||||
)
|
||||
|
||||
response._metrics = _metrics
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/// Evaluates the specified closure when the `DataResponse` is a failure, passing the unwrapped error as a parameter.
|
||||
///
|
||||
/// Use the `flatMapError` function with a closure that may throw an error. For example:
|
||||
///
|
||||
/// let possibleData: DataResponse<Data> = ...
|
||||
/// let possibleObject = possibleData.flatMapError {
|
||||
/// 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 flatMapError<E: Error>(_ transform: (Error) throws -> E) -> DataResponse {
|
||||
var response = DataResponse(
|
||||
request: request,
|
||||
response: self.response,
|
||||
data: data,
|
||||
result: result.flatMapError(transform),
|
||||
timeline: timeline
|
||||
)
|
||||
|
||||
response._metrics = _metrics
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Used to store all data associated with an non-serialized response of a download request.
|
||||
public struct DefaultDownloadResponse {
|
||||
/// The URL request sent to the server.
|
||||
public let request: URLRequest?
|
||||
|
||||
/// The server's response to the URL request.
|
||||
public let response: HTTPURLResponse?
|
||||
|
||||
/// The temporary destination URL of the data returned from the server.
|
||||
public let temporaryURL: URL?
|
||||
|
||||
/// The final destination URL of the data returned from the server if it was moved.
|
||||
public let destinationURL: URL?
|
||||
|
||||
/// The resume data generated if the request was cancelled.
|
||||
public let resumeData: Data?
|
||||
|
||||
/// The error encountered while executing or validating the request.
|
||||
public let error: Error?
|
||||
|
||||
/// The timeline of the complete lifecycle of the request.
|
||||
public let timeline: Timeline
|
||||
|
||||
var _metrics: AnyObject?
|
||||
|
||||
/// Creates a `DefaultDownloadResponse` instance from the specified parameters.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - request: The URL request sent to the server.
|
||||
/// - response: The server's response to the URL request.
|
||||
/// - temporaryURL: The temporary destination URL of the data returned from the server.
|
||||
/// - destinationURL: The final destination URL of the data returned from the server if it was moved.
|
||||
/// - resumeData: The resume data generated if the request was cancelled.
|
||||
/// - error: The error encountered while executing or validating the request.
|
||||
/// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
|
||||
/// - metrics: The task metrics containing the request / response statistics. `nil` by default.
|
||||
public init(
|
||||
request: URLRequest?,
|
||||
response: HTTPURLResponse?,
|
||||
temporaryURL: URL?,
|
||||
destinationURL: URL?,
|
||||
resumeData: Data?,
|
||||
error: Error?,
|
||||
timeline: Timeline = Timeline(),
|
||||
metrics: AnyObject? = nil)
|
||||
{
|
||||
self.request = request
|
||||
self.response = response
|
||||
self.temporaryURL = temporaryURL
|
||||
self.destinationURL = destinationURL
|
||||
self.resumeData = resumeData
|
||||
self.error = error
|
||||
self.timeline = timeline
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// Used to store all data associated with a serialized response of a download request.
|
||||
public struct DownloadResponse<Value> {
|
||||
/// The URL request sent to the server.
|
||||
public let request: URLRequest?
|
||||
|
||||
/// The server's response to the URL request.
|
||||
public let response: HTTPURLResponse?
|
||||
|
||||
/// The temporary destination URL of the data returned from the server.
|
||||
public let temporaryURL: URL?
|
||||
|
||||
/// The final destination URL of the data returned from the server if it was moved.
|
||||
public let destinationURL: URL?
|
||||
|
||||
/// The resume data generated if the request was cancelled.
|
||||
public let resumeData: Data?
|
||||
|
||||
/// The result of response serialization.
|
||||
public let result: Result<Value>
|
||||
|
||||
/// The timeline of the complete lifecycle of the request.
|
||||
public let timeline: Timeline
|
||||
|
||||
/// Returns the associated value of the result if it is a success, `nil` otherwise.
|
||||
public var value: Value? { return result.value }
|
||||
|
||||
/// Returns the associated error value if the result if it is a failure, `nil` otherwise.
|
||||
public var error: Error? { return result.error }
|
||||
|
||||
var _metrics: AnyObject?
|
||||
|
||||
/// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization.
|
||||
///
|
||||
/// - parameter request: The URL request sent to the server.
|
||||
/// - parameter response: The server's response to the URL request.
|
||||
/// - parameter temporaryURL: The temporary destination URL of the data returned from the server.
|
||||
/// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved.
|
||||
/// - parameter resumeData: The resume data generated if the request was cancelled.
|
||||
/// - parameter result: The result of response serialization.
|
||||
/// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
|
||||
///
|
||||
/// - returns: The new `DownloadResponse` instance.
|
||||
public init(
|
||||
request: URLRequest?,
|
||||
response: HTTPURLResponse?,
|
||||
temporaryURL: URL?,
|
||||
destinationURL: URL?,
|
||||
resumeData: Data?,
|
||||
result: Result<Value>,
|
||||
timeline: Timeline = Timeline())
|
||||
{
|
||||
self.request = request
|
||||
self.response = response
|
||||
self.temporaryURL = temporaryURL
|
||||
self.destinationURL = destinationURL
|
||||
self.resumeData = resumeData
|
||||
self.result = result
|
||||
self.timeline = timeline
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible {
|
||||
/// The textual representation used when written to an output stream, which includes whether the result was a
|
||||
/// success or failure.
|
||||
public var description: String {
|
||||
return result.debugDescription
|
||||
}
|
||||
|
||||
/// The debug textual representation used when written to an output stream, which includes the URL request, the URL
|
||||
/// response, the temporary and destination URLs, the resume data, the response serialization result and the
|
||||
/// timeline.
|
||||
public var debugDescription: String {
|
||||
var output: [String] = []
|
||||
|
||||
output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil")
|
||||
output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")
|
||||
output.append("[TemporaryURL]: \(temporaryURL?.path ?? "nil")")
|
||||
output.append("[DestinationURL]: \(destinationURL?.path ?? "nil")")
|
||||
output.append("[ResumeData]: \(resumeData?.count ?? 0) bytes")
|
||||
output.append("[Result]: \(result.debugDescription)")
|
||||
output.append("[Timeline]: \(timeline.debugDescription)")
|
||||
|
||||
return output.joined(separator: "\n")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
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<T>(_ transform: (Value) -> T) -> DownloadResponse<T> {
|
||||
var response = DownloadResponse<T>(
|
||||
request: request,
|
||||
response: self.response,
|
||||
temporaryURL: temporaryURL,
|
||||
destinationURL: destinationURL,
|
||||
resumeData: resumeData,
|
||||
result: result.map(transform),
|
||||
timeline: timeline
|
||||
)
|
||||
|
||||
response._metrics = _metrics
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/// Evaluates the given closure when the result of this `DownloadResponse` is a success, passing the unwrapped
|
||||
/// result value as a parameter.
|
||||
///
|
||||
/// Use the `flatMap` method with a closure that may throw an error. For example:
|
||||
///
|
||||
/// let possibleData: DownloadResponse<Data> = ...
|
||||
/// let possibleObject = possibleData.flatMap {
|
||||
/// 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 flatMap<T>(_ transform: (Value) throws -> T) -> DownloadResponse<T> {
|
||||
var response = DownloadResponse<T>(
|
||||
request: request,
|
||||
response: self.response,
|
||||
temporaryURL: temporaryURL,
|
||||
destinationURL: destinationURL,
|
||||
resumeData: resumeData,
|
||||
result: result.flatMap(transform),
|
||||
timeline: timeline
|
||||
)
|
||||
|
||||
response._metrics = _metrics
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/// 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<E: Error>(_ transform: (Error) -> E) -> DownloadResponse {
|
||||
var response = DownloadResponse(
|
||||
request: request,
|
||||
response: self.response,
|
||||
temporaryURL: temporaryURL,
|
||||
destinationURL: destinationURL,
|
||||
resumeData: resumeData,
|
||||
result: result.mapError(transform),
|
||||
timeline: timeline
|
||||
)
|
||||
|
||||
response._metrics = _metrics
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
/// Evaluates the specified closure when the `DownloadResponse` is a failure, passing the unwrapped error as a parameter.
|
||||
///
|
||||
/// Use the `flatMapError` function with a closure that may throw an error. For example:
|
||||
///
|
||||
/// let possibleData: DownloadResponse<Data> = ...
|
||||
/// let possibleObject = possibleData.flatMapError {
|
||||
/// 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 flatMapError<E: Error>(_ transform: (Error) throws -> E) -> DownloadResponse {
|
||||
var response = DownloadResponse(
|
||||
request: request,
|
||||
response: self.response,
|
||||
temporaryURL: temporaryURL,
|
||||
destinationURL: destinationURL,
|
||||
resumeData: resumeData,
|
||||
result: result.flatMapError(transform),
|
||||
timeline: timeline
|
||||
)
|
||||
|
||||
response._metrics = _metrics
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
protocol Response {
|
||||
/// The task metrics containing the request / response statistics.
|
||||
var _metrics: AnyObject? { get set }
|
||||
mutating func add(_ metrics: AnyObject?)
|
||||
}
|
||||
|
||||
extension Response {
|
||||
mutating func add(_ metrics: AnyObject?) {
|
||||
#if !os(watchOS)
|
||||
guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return }
|
||||
guard let metrics = metrics as? URLSessionTaskMetrics else { return }
|
||||
|
||||
_metrics = metrics
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
|
||||
extension DefaultDataResponse: Response {
|
||||
#if !os(watchOS)
|
||||
/// The task metrics containing the request / response statistics.
|
||||
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
|
||||
#endif
|
||||
}
|
||||
|
||||
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
|
||||
extension DataResponse: Response {
|
||||
#if !os(watchOS)
|
||||
/// The task metrics containing the request / response statistics.
|
||||
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
|
||||
#endif
|
||||
}
|
||||
|
||||
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
|
||||
extension DefaultDownloadResponse: Response {
|
||||
#if !os(watchOS)
|
||||
/// The task metrics containing the request / response statistics.
|
||||
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
|
||||
#endif
|
||||
}
|
||||
|
||||
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
|
||||
extension DownloadResponse: Response {
|
||||
#if !os(watchOS)
|
||||
/// The task metrics containing the request / response statistics.
|
||||
public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
|
||||
#endif
|
||||
}
|
||||
715
Pods/Alamofire/Source/ResponseSerialization.swift
generated
Normal file
715
Pods/Alamofire/Source/ResponseSerialization.swift
generated
Normal file
@@ -0,0 +1,715 @@
|
||||
//
|
||||
// ResponseSerialization.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
|
||||
|
||||
/// The type in which all data response serializers must conform to in order to serialize a response.
|
||||
public protocol DataResponseSerializerProtocol {
|
||||
/// The type of serialized object to be created by this `DataResponseSerializerType`.
|
||||
associatedtype SerializedObject
|
||||
|
||||
/// A closure used by response handlers that takes a request, response, data and error and returns a result.
|
||||
var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<SerializedObject> { get }
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// A generic `DataResponseSerializerType` used to serialize a request, response, and data into a serialized object.
|
||||
public struct DataResponseSerializer<Value>: DataResponseSerializerProtocol {
|
||||
/// The type of serialized object to be created by this `DataResponseSerializer`.
|
||||
public typealias SerializedObject = Value
|
||||
|
||||
/// A closure used by response handlers that takes a request, response, data and error and returns a result.
|
||||
public var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>
|
||||
|
||||
/// Initializes the `ResponseSerializer` instance with the given serialize response closure.
|
||||
///
|
||||
/// - parameter serializeResponse: The closure used to serialize the response.
|
||||
///
|
||||
/// - returns: The new generic response serializer instance.
|
||||
public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>) {
|
||||
self.serializeResponse = serializeResponse
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// The type in which all download response serializers must conform to in order to serialize a response.
|
||||
public protocol DownloadResponseSerializerProtocol {
|
||||
/// The type of serialized object to be created by this `DownloadResponseSerializerType`.
|
||||
associatedtype SerializedObject
|
||||
|
||||
/// A closure used by response handlers that takes a request, response, url and error and returns a result.
|
||||
var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<SerializedObject> { get }
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// A generic `DownloadResponseSerializerType` used to serialize a request, response, and data into a serialized object.
|
||||
public struct DownloadResponseSerializer<Value>: DownloadResponseSerializerProtocol {
|
||||
/// The type of serialized object to be created by this `DownloadResponseSerializer`.
|
||||
public typealias SerializedObject = Value
|
||||
|
||||
/// A closure used by response handlers that takes a request, response, url and error and returns a result.
|
||||
public var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>
|
||||
|
||||
/// Initializes the `ResponseSerializer` instance with the given serialize response closure.
|
||||
///
|
||||
/// - parameter serializeResponse: The closure used to serialize the response.
|
||||
///
|
||||
/// - returns: The new generic response serializer instance.
|
||||
public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>) {
|
||||
self.serializeResponse = serializeResponse
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Timeline
|
||||
|
||||
extension Request {
|
||||
var timeline: Timeline {
|
||||
let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent()
|
||||
let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
|
||||
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
|
||||
|
||||
return Timeline(
|
||||
requestStartTime: requestStartTime,
|
||||
initialResponseTime: initialResponseTime,
|
||||
requestCompletedTime: requestCompletedTime,
|
||||
serializationCompletedTime: CFAbsoluteTimeGetCurrent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Default
|
||||
|
||||
extension DataRequest {
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter queue: The queue on which the completion handler is dispatched.
|
||||
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self {
|
||||
delegate.queue.addOperation {
|
||||
(queue ?? DispatchQueue.main).async {
|
||||
var dataResponse = DefaultDataResponse(
|
||||
request: self.request,
|
||||
response: self.response,
|
||||
data: self.delegate.data,
|
||||
error: self.delegate.error,
|
||||
timeline: self.timeline
|
||||
)
|
||||
|
||||
dataResponse.add(self.delegate.metrics)
|
||||
|
||||
completionHandler(dataResponse)
|
||||
}
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter queue: The queue on which the completion handler is dispatched.
|
||||
/// - parameter responseSerializer: The response serializer responsible for serializing the request, response,
|
||||
/// and data.
|
||||
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func response<T: DataResponseSerializerProtocol>(
|
||||
queue: DispatchQueue? = nil,
|
||||
responseSerializer: T,
|
||||
completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
|
||||
-> Self
|
||||
{
|
||||
delegate.queue.addOperation {
|
||||
let result = responseSerializer.serializeResponse(
|
||||
self.request,
|
||||
self.response,
|
||||
self.delegate.data,
|
||||
self.delegate.error
|
||||
)
|
||||
|
||||
var dataResponse = DataResponse<T.SerializedObject>(
|
||||
request: self.request,
|
||||
response: self.response,
|
||||
data: self.delegate.data,
|
||||
result: result,
|
||||
timeline: self.timeline
|
||||
)
|
||||
|
||||
dataResponse.add(self.delegate.metrics)
|
||||
|
||||
(queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadRequest {
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter queue: The queue on which the completion handler is dispatched.
|
||||
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func response(
|
||||
queue: DispatchQueue? = nil,
|
||||
completionHandler: @escaping (DefaultDownloadResponse) -> Void)
|
||||
-> Self
|
||||
{
|
||||
delegate.queue.addOperation {
|
||||
(queue ?? DispatchQueue.main).async {
|
||||
var downloadResponse = DefaultDownloadResponse(
|
||||
request: self.request,
|
||||
response: self.response,
|
||||
temporaryURL: self.downloadDelegate.temporaryURL,
|
||||
destinationURL: self.downloadDelegate.destinationURL,
|
||||
resumeData: self.downloadDelegate.resumeData,
|
||||
error: self.downloadDelegate.error,
|
||||
timeline: self.timeline
|
||||
)
|
||||
|
||||
downloadResponse.add(self.delegate.metrics)
|
||||
|
||||
completionHandler(downloadResponse)
|
||||
}
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter queue: The queue on which the completion handler is dispatched.
|
||||
/// - parameter responseSerializer: The response serializer responsible for serializing the request, response,
|
||||
/// and data contained in the destination url.
|
||||
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func response<T: DownloadResponseSerializerProtocol>(
|
||||
queue: DispatchQueue? = nil,
|
||||
responseSerializer: T,
|
||||
completionHandler: @escaping (DownloadResponse<T.SerializedObject>) -> Void)
|
||||
-> Self
|
||||
{
|
||||
delegate.queue.addOperation {
|
||||
let result = responseSerializer.serializeResponse(
|
||||
self.request,
|
||||
self.response,
|
||||
self.downloadDelegate.fileURL,
|
||||
self.downloadDelegate.error
|
||||
)
|
||||
|
||||
var downloadResponse = DownloadResponse<T.SerializedObject>(
|
||||
request: self.request,
|
||||
response: self.response,
|
||||
temporaryURL: self.downloadDelegate.temporaryURL,
|
||||
destinationURL: self.downloadDelegate.destinationURL,
|
||||
resumeData: self.downloadDelegate.resumeData,
|
||||
result: result,
|
||||
timeline: self.timeline
|
||||
)
|
||||
|
||||
downloadResponse.add(self.delegate.metrics)
|
||||
|
||||
(queue ?? DispatchQueue.main).async { completionHandler(downloadResponse) }
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Data
|
||||
|
||||
extension Request {
|
||||
/// Returns a result data type that contains the response data as-is.
|
||||
///
|
||||
/// - parameter response: The response from the server.
|
||||
/// - parameter data: The data returned from the server.
|
||||
/// - parameter error: The error already encountered if it exists.
|
||||
///
|
||||
/// - returns: The result data type.
|
||||
public static func serializeResponseData(response: HTTPURLResponse?, data: Data?, error: Error?) -> Result<Data> {
|
||||
guard error == nil else { return .failure(error!) }
|
||||
|
||||
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(Data()) }
|
||||
|
||||
guard let validData = data else {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
|
||||
}
|
||||
|
||||
return .success(validData)
|
||||
}
|
||||
}
|
||||
|
||||
extension DataRequest {
|
||||
/// Creates a response serializer that returns the associated data as-is.
|
||||
///
|
||||
/// - returns: A data response serializer.
|
||||
public static func dataResponseSerializer() -> DataResponseSerializer<Data> {
|
||||
return DataResponseSerializer { _, response, data, error in
|
||||
return Request.serializeResponseData(response: response, data: data, error: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func responseData(
|
||||
queue: DispatchQueue? = nil,
|
||||
completionHandler: @escaping (DataResponse<Data>) -> Void)
|
||||
-> Self
|
||||
{
|
||||
return response(
|
||||
queue: queue,
|
||||
responseSerializer: DataRequest.dataResponseSerializer(),
|
||||
completionHandler: completionHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadRequest {
|
||||
/// Creates a response serializer that returns the associated data as-is.
|
||||
///
|
||||
/// - returns: A data response serializer.
|
||||
public static func dataResponseSerializer() -> DownloadResponseSerializer<Data> {
|
||||
return DownloadResponseSerializer { _, response, fileURL, error in
|
||||
guard error == nil else { return .failure(error!) }
|
||||
|
||||
guard let fileURL = fileURL else {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
|
||||
}
|
||||
|
||||
do {
|
||||
let data = try Data(contentsOf: fileURL)
|
||||
return Request.serializeResponseData(response: response, data: data, error: error)
|
||||
} catch {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter completionHandler: The code to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func responseData(
|
||||
queue: DispatchQueue? = nil,
|
||||
completionHandler: @escaping (DownloadResponse<Data>) -> Void)
|
||||
-> Self
|
||||
{
|
||||
return response(
|
||||
queue: queue,
|
||||
responseSerializer: DownloadRequest.dataResponseSerializer(),
|
||||
completionHandler: completionHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - String
|
||||
|
||||
extension Request {
|
||||
/// Returns a result string type initialized from the response data with the specified string encoding.
|
||||
///
|
||||
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
|
||||
/// response, falling back to the default HTTP default character set, ISO-8859-1.
|
||||
/// - parameter response: The response from the server.
|
||||
/// - parameter data: The data returned from the server.
|
||||
/// - parameter error: The error already encountered if it exists.
|
||||
///
|
||||
/// - returns: The result data type.
|
||||
public static func serializeResponseString(
|
||||
encoding: String.Encoding?,
|
||||
response: HTTPURLResponse?,
|
||||
data: Data?,
|
||||
error: Error?)
|
||||
-> Result<String>
|
||||
{
|
||||
guard error == nil else { return .failure(error!) }
|
||||
|
||||
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success("") }
|
||||
|
||||
guard let validData = data else {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
|
||||
}
|
||||
|
||||
var convertedEncoding = encoding
|
||||
|
||||
if let encodingName = response?.textEncodingName as CFString?, convertedEncoding == nil {
|
||||
convertedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(
|
||||
CFStringConvertIANACharSetNameToEncoding(encodingName))
|
||||
)
|
||||
}
|
||||
|
||||
let actualEncoding = convertedEncoding ?? .isoLatin1
|
||||
|
||||
if let string = String(data: validData, encoding: actualEncoding) {
|
||||
return .success(string)
|
||||
} else {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DataRequest {
|
||||
/// Creates a response serializer that returns a result string type initialized from the response data with
|
||||
/// the specified string encoding.
|
||||
///
|
||||
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
|
||||
/// response, falling back to the default HTTP default character set, ISO-8859-1.
|
||||
///
|
||||
/// - returns: A string response serializer.
|
||||
public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DataResponseSerializer<String> {
|
||||
return DataResponseSerializer { _, response, data, error in
|
||||
return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the
|
||||
/// server response, falling back to the default HTTP default character set,
|
||||
/// ISO-8859-1.
|
||||
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func responseString(
|
||||
queue: DispatchQueue? = nil,
|
||||
encoding: String.Encoding? = nil,
|
||||
completionHandler: @escaping (DataResponse<String>) -> Void)
|
||||
-> Self
|
||||
{
|
||||
return response(
|
||||
queue: queue,
|
||||
responseSerializer: DataRequest.stringResponseSerializer(encoding: encoding),
|
||||
completionHandler: completionHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadRequest {
|
||||
/// Creates a response serializer that returns a result string type initialized from the response data with
|
||||
/// the specified string encoding.
|
||||
///
|
||||
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
|
||||
/// response, falling back to the default HTTP default character set, ISO-8859-1.
|
||||
///
|
||||
/// - returns: A string response serializer.
|
||||
public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DownloadResponseSerializer<String> {
|
||||
return DownloadResponseSerializer { _, response, fileURL, error in
|
||||
guard error == nil else { return .failure(error!) }
|
||||
|
||||
guard let fileURL = fileURL else {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
|
||||
}
|
||||
|
||||
do {
|
||||
let data = try Data(contentsOf: fileURL)
|
||||
return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error)
|
||||
} catch {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the
|
||||
/// server response, falling back to the default HTTP default character set,
|
||||
/// ISO-8859-1.
|
||||
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func responseString(
|
||||
queue: DispatchQueue? = nil,
|
||||
encoding: String.Encoding? = nil,
|
||||
completionHandler: @escaping (DownloadResponse<String>) -> Void)
|
||||
-> Self
|
||||
{
|
||||
return response(
|
||||
queue: queue,
|
||||
responseSerializer: DownloadRequest.stringResponseSerializer(encoding: encoding),
|
||||
completionHandler: completionHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - JSON
|
||||
|
||||
extension Request {
|
||||
/// Returns a JSON object contained in a result type constructed from the response data using `JSONSerialization`
|
||||
/// with the specified reading options.
|
||||
///
|
||||
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
|
||||
/// - parameter response: The response from the server.
|
||||
/// - parameter data: The data returned from the server.
|
||||
/// - parameter error: The error already encountered if it exists.
|
||||
///
|
||||
/// - returns: The result data type.
|
||||
public static func serializeResponseJSON(
|
||||
options: JSONSerialization.ReadingOptions,
|
||||
response: HTTPURLResponse?,
|
||||
data: Data?,
|
||||
error: Error?)
|
||||
-> Result<Any>
|
||||
{
|
||||
guard error == nil else { return .failure(error!) }
|
||||
|
||||
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
|
||||
|
||||
guard let validData = data, validData.count > 0 else {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
|
||||
}
|
||||
|
||||
do {
|
||||
let json = try JSONSerialization.jsonObject(with: validData, options: options)
|
||||
return .success(json)
|
||||
} catch {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DataRequest {
|
||||
/// Creates a response serializer that returns a JSON object result type constructed from the response data using
|
||||
/// `JSONSerialization` with the specified reading options.
|
||||
///
|
||||
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
|
||||
///
|
||||
/// - returns: A JSON object response serializer.
|
||||
public static func jsonResponseSerializer(
|
||||
options: JSONSerialization.ReadingOptions = .allowFragments)
|
||||
-> DataResponseSerializer<Any>
|
||||
{
|
||||
return DataResponseSerializer { _, response, data, error in
|
||||
return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
|
||||
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func responseJSON(
|
||||
queue: DispatchQueue? = nil,
|
||||
options: JSONSerialization.ReadingOptions = .allowFragments,
|
||||
completionHandler: @escaping (DataResponse<Any>) -> Void)
|
||||
-> Self
|
||||
{
|
||||
return response(
|
||||
queue: queue,
|
||||
responseSerializer: DataRequest.jsonResponseSerializer(options: options),
|
||||
completionHandler: completionHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadRequest {
|
||||
/// Creates a response serializer that returns a JSON object result type constructed from the response data using
|
||||
/// `JSONSerialization` with the specified reading options.
|
||||
///
|
||||
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
|
||||
///
|
||||
/// - returns: A JSON object response serializer.
|
||||
public static func jsonResponseSerializer(
|
||||
options: JSONSerialization.ReadingOptions = .allowFragments)
|
||||
-> DownloadResponseSerializer<Any>
|
||||
{
|
||||
return DownloadResponseSerializer { _, response, fileURL, error in
|
||||
guard error == nil else { return .failure(error!) }
|
||||
|
||||
guard let fileURL = fileURL else {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
|
||||
}
|
||||
|
||||
do {
|
||||
let data = try Data(contentsOf: fileURL)
|
||||
return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
|
||||
} catch {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
|
||||
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func responseJSON(
|
||||
queue: DispatchQueue? = nil,
|
||||
options: JSONSerialization.ReadingOptions = .allowFragments,
|
||||
completionHandler: @escaping (DownloadResponse<Any>) -> Void)
|
||||
-> Self
|
||||
{
|
||||
return response(
|
||||
queue: queue,
|
||||
responseSerializer: DownloadRequest.jsonResponseSerializer(options: options),
|
||||
completionHandler: completionHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Property List
|
||||
|
||||
extension Request {
|
||||
/// Returns a plist object contained in a result type constructed from the response data using
|
||||
/// `PropertyListSerialization` with the specified reading options.
|
||||
///
|
||||
/// - parameter options: The property list reading options. Defaults to `[]`.
|
||||
/// - parameter response: The response from the server.
|
||||
/// - parameter data: The data returned from the server.
|
||||
/// - parameter error: The error already encountered if it exists.
|
||||
///
|
||||
/// - returns: The result data type.
|
||||
public static func serializeResponsePropertyList(
|
||||
options: PropertyListSerialization.ReadOptions,
|
||||
response: HTTPURLResponse?,
|
||||
data: Data?,
|
||||
error: Error?)
|
||||
-> Result<Any>
|
||||
{
|
||||
guard error == nil else { return .failure(error!) }
|
||||
|
||||
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
|
||||
|
||||
guard let validData = data, validData.count > 0 else {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
|
||||
}
|
||||
|
||||
do {
|
||||
let plist = try PropertyListSerialization.propertyList(from: validData, options: options, format: nil)
|
||||
return .success(plist)
|
||||
} catch {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .propertyListSerializationFailed(error: error)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DataRequest {
|
||||
/// Creates a response serializer that returns an object constructed from the response data using
|
||||
/// `PropertyListSerialization` with the specified reading options.
|
||||
///
|
||||
/// - parameter options: The property list reading options. Defaults to `[]`.
|
||||
///
|
||||
/// - returns: A property list object response serializer.
|
||||
public static func propertyListResponseSerializer(
|
||||
options: PropertyListSerialization.ReadOptions = [])
|
||||
-> DataResponseSerializer<Any>
|
||||
{
|
||||
return DataResponseSerializer { _, response, data, error in
|
||||
return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter options: The property list reading options. Defaults to `[]`.
|
||||
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func responsePropertyList(
|
||||
queue: DispatchQueue? = nil,
|
||||
options: PropertyListSerialization.ReadOptions = [],
|
||||
completionHandler: @escaping (DataResponse<Any>) -> Void)
|
||||
-> Self
|
||||
{
|
||||
return response(
|
||||
queue: queue,
|
||||
responseSerializer: DataRequest.propertyListResponseSerializer(options: options),
|
||||
completionHandler: completionHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension DownloadRequest {
|
||||
/// Creates a response serializer that returns an object constructed from the response data using
|
||||
/// `PropertyListSerialization` with the specified reading options.
|
||||
///
|
||||
/// - parameter options: The property list reading options. Defaults to `[]`.
|
||||
///
|
||||
/// - returns: A property list object response serializer.
|
||||
public static func propertyListResponseSerializer(
|
||||
options: PropertyListSerialization.ReadOptions = [])
|
||||
-> DownloadResponseSerializer<Any>
|
||||
{
|
||||
return DownloadResponseSerializer { _, response, fileURL, error in
|
||||
guard error == nil else { return .failure(error!) }
|
||||
|
||||
guard let fileURL = fileURL else {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputFileNil))
|
||||
}
|
||||
|
||||
do {
|
||||
let data = try Data(contentsOf: fileURL)
|
||||
return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error)
|
||||
} catch {
|
||||
return .failure(AFError.responseSerializationFailed(reason: .inputFileReadFailed(at: fileURL)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a handler to be called once the request has finished.
|
||||
///
|
||||
/// - parameter options: The property list reading options. Defaults to `[]`.
|
||||
/// - parameter completionHandler: A closure to be executed once the request has finished.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func responsePropertyList(
|
||||
queue: DispatchQueue? = nil,
|
||||
options: PropertyListSerialization.ReadOptions = [],
|
||||
completionHandler: @escaping (DownloadResponse<Any>) -> Void)
|
||||
-> Self
|
||||
{
|
||||
return response(
|
||||
queue: queue,
|
||||
responseSerializer: DownloadRequest.propertyListResponseSerializer(options: options),
|
||||
completionHandler: completionHandler
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of HTTP response status code that do not contain response data.
|
||||
private let emptyDataStatusCodes: Set<Int> = [204, 205]
|
||||
300
Pods/Alamofire/Source/Result.swift
generated
Normal file
300
Pods/Alamofire/Source/Result.swift
generated
Normal file
@@ -0,0 +1,300 @@
|
||||
//
|
||||
// Result.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
|
||||
|
||||
/// Used to represent whether a request was successful or encountered an error.
|
||||
///
|
||||
/// - success: The request and all post processing operations were successful resulting in the serialization of the
|
||||
/// provided associated value.
|
||||
///
|
||||
/// - failure: The request encountered an error resulting in a failure. The associated values are the original data
|
||||
/// provided by the server as well as the error that caused the failure.
|
||||
public enum Result<Value> {
|
||||
case success(Value)
|
||||
case failure(Error)
|
||||
|
||||
/// Returns `true` if the result is a success, `false` otherwise.
|
||||
public var isSuccess: Bool {
|
||||
switch self {
|
||||
case .success:
|
||||
return true
|
||||
case .failure:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the result is a failure, `false` otherwise.
|
||||
public var isFailure: Bool {
|
||||
return !isSuccess
|
||||
}
|
||||
|
||||
/// Returns the associated value if the result is a success, `nil` otherwise.
|
||||
public var value: Value? {
|
||||
switch self {
|
||||
case .success(let value):
|
||||
return value
|
||||
case .failure:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the associated error value if the result is a failure, `nil` otherwise.
|
||||
public var error: Error? {
|
||||
switch self {
|
||||
case .success:
|
||||
return nil
|
||||
case .failure(let error):
|
||||
return error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CustomStringConvertible
|
||||
|
||||
extension Result: CustomStringConvertible {
|
||||
/// The textual representation used when written to an output stream, which includes whether the result was a
|
||||
/// success or failure.
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .success:
|
||||
return "SUCCESS"
|
||||
case .failure:
|
||||
return "FAILURE"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CustomDebugStringConvertible
|
||||
|
||||
extension Result: CustomDebugStringConvertible {
|
||||
/// The debug textual representation used when written to an output stream, which includes whether the result was a
|
||||
/// success or failure in addition to the value or error.
|
||||
public var debugDescription: String {
|
||||
switch self {
|
||||
case .success(let value):
|
||||
return "SUCCESS: \(value)"
|
||||
case .failure(let error):
|
||||
return "FAILURE: \(error)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Functional APIs
|
||||
|
||||
extension Result {
|
||||
/// Creates a `Result` instance from the result of a closure.
|
||||
///
|
||||
/// A failure result is created when the closure throws, and a success result is created when the closure
|
||||
/// succeeds without throwing an error.
|
||||
///
|
||||
/// func someString() throws -> String { ... }
|
||||
///
|
||||
/// let result = Result(value: {
|
||||
/// return try someString()
|
||||
/// })
|
||||
///
|
||||
/// // The type of result is Result<String>
|
||||
///
|
||||
/// The trailing closure syntax is also supported:
|
||||
///
|
||||
/// let result = Result { try someString() }
|
||||
///
|
||||
/// - parameter value: The closure to execute and create the result for.
|
||||
public init(value: () throws -> Value) {
|
||||
do {
|
||||
self = try .success(value())
|
||||
} catch {
|
||||
self = .failure(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the success value, or throws the failure error.
|
||||
///
|
||||
/// let possibleString: Result<String> = .success("success")
|
||||
/// try print(possibleString.unwrap())
|
||||
/// // Prints "success"
|
||||
///
|
||||
/// let noString: Result<String> = .failure(error)
|
||||
/// try print(noString.unwrap())
|
||||
/// // Throws error
|
||||
public func unwrap() throws -> Value {
|
||||
switch self {
|
||||
case .success(let value):
|
||||
return value
|
||||
case .failure(let error):
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
|
||||
///
|
||||
/// Use the `map` method with a closure that does not throw. For example:
|
||||
///
|
||||
/// let possibleData: Result<Data> = .success(Data())
|
||||
/// let possibleInt = possibleData.map { $0.count }
|
||||
/// try print(possibleInt.unwrap())
|
||||
/// // Prints "0"
|
||||
///
|
||||
/// let noData: Result<Data> = .failure(error)
|
||||
/// let noInt = noData.map { $0.count }
|
||||
/// try print(noInt.unwrap())
|
||||
/// // Throws error
|
||||
///
|
||||
/// - parameter transform: A closure that takes the success value of the `Result` instance.
|
||||
///
|
||||
/// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the
|
||||
/// same failure.
|
||||
public func map<T>(_ transform: (Value) -> T) -> Result<T> {
|
||||
switch self {
|
||||
case .success(let value):
|
||||
return .success(transform(value))
|
||||
case .failure(let error):
|
||||
return .failure(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
|
||||
///
|
||||
/// Use the `flatMap` method with a closure that may throw an error. For example:
|
||||
///
|
||||
/// let possibleData: Result<Data> = .success(Data(...))
|
||||
/// let possibleObject = possibleData.flatMap {
|
||||
/// 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.
|
||||
public func flatMap<T>(_ transform: (Value) throws -> T) -> Result<T> {
|
||||
switch self {
|
||||
case .success(let value):
|
||||
do {
|
||||
return try .success(transform(value))
|
||||
} catch {
|
||||
return .failure(error)
|
||||
}
|
||||
case .failure(let error):
|
||||
return .failure(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates the specified closure when the `Result` 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: Result<Data> = .failure(someError)
|
||||
/// let withMyError: Result<Data> = possibleData.mapError { MyError.error($0) }
|
||||
///
|
||||
/// - Parameter transform: A 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 instance.
|
||||
public func mapError<T: Error>(_ transform: (Error) -> T) -> Result {
|
||||
switch self {
|
||||
case .failure(let error):
|
||||
return .failure(transform(error))
|
||||
case .success:
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter.
|
||||
///
|
||||
/// Use the `flatMapError` function with a closure that may throw an error. For example:
|
||||
///
|
||||
/// let possibleData: Result<Data> = .success(Data(...))
|
||||
/// let possibleObject = possibleData.flatMapError {
|
||||
/// 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 instance.
|
||||
public func flatMapError<T: Error>(_ transform: (Error) throws -> T) -> Result {
|
||||
switch self {
|
||||
case .failure(let error):
|
||||
do {
|
||||
return try .failure(transform(error))
|
||||
} catch {
|
||||
return .failure(error)
|
||||
}
|
||||
case .success:
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter.
|
||||
///
|
||||
/// Use the `withValue` function to evaluate the passed closure without modifying the `Result` instance.
|
||||
///
|
||||
/// - Parameter closure: A closure that takes the success value of this instance.
|
||||
/// - Returns: This `Result` instance, unmodified.
|
||||
@discardableResult
|
||||
public func withValue(_ closure: (Value) -> Void) -> Result {
|
||||
if case let .success(value) = self { closure(value) }
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
/// Evaluates the specified closure when the `Result` is a failure, passing the unwrapped error as a parameter.
|
||||
///
|
||||
/// Use the `withError` function to evaluate the passed closure without modifying the `Result` instance.
|
||||
///
|
||||
/// - Parameter closure: A closure that takes the success value of this instance.
|
||||
/// - Returns: This `Result` instance, unmodified.
|
||||
@discardableResult
|
||||
public func withError(_ closure: (Error) -> Void) -> Result {
|
||||
if case let .failure(error) = self { closure(error) }
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
/// Evaluates the specified closure when the `Result` is a success.
|
||||
///
|
||||
/// Use the `ifSuccess` function to evaluate the passed closure without modifying the `Result` instance.
|
||||
///
|
||||
/// - Parameter closure: A `Void` closure.
|
||||
/// - Returns: This `Result` instance, unmodified.
|
||||
@discardableResult
|
||||
public func ifSuccess(_ closure: () -> Void) -> Result {
|
||||
if isSuccess { closure() }
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
/// Evaluates the specified closure when the `Result` is a failure.
|
||||
///
|
||||
/// Use the `ifFailure` function to evaluate the passed closure without modifying the `Result` instance.
|
||||
///
|
||||
/// - Parameter closure: A `Void` closure.
|
||||
/// - Returns: This `Result` instance, unmodified.
|
||||
@discardableResult
|
||||
public func ifFailure(_ closure: () -> Void) -> Result {
|
||||
if isFailure { closure() }
|
||||
|
||||
return self
|
||||
}
|
||||
}
|
||||
307
Pods/Alamofire/Source/ServerTrustPolicy.swift
generated
Normal file
307
Pods/Alamofire/Source/ServerTrustPolicy.swift
generated
Normal file
@@ -0,0 +1,307 @@
|
||||
//
|
||||
// ServerTrustPolicy.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
|
||||
|
||||
/// Responsible for managing the mapping of `ServerTrustPolicy` objects to a given host.
|
||||
open class ServerTrustPolicyManager {
|
||||
/// The dictionary of policies mapped to a particular host.
|
||||
public let policies: [String: ServerTrustPolicy]
|
||||
|
||||
/// Initializes the `ServerTrustPolicyManager` instance with the given policies.
|
||||
///
|
||||
/// Since different servers and web services can have different leaf certificates, intermediate and even root
|
||||
/// certficates, it is important to have the flexibility to specify evaluation policies on a per host basis. This
|
||||
/// allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key
|
||||
/// pinning for host3 and disabling evaluation for host4.
|
||||
///
|
||||
/// - parameter policies: A dictionary of all policies mapped to a particular host.
|
||||
///
|
||||
/// - returns: The new `ServerTrustPolicyManager` instance.
|
||||
public init(policies: [String: ServerTrustPolicy]) {
|
||||
self.policies = policies
|
||||
}
|
||||
|
||||
/// Returns the `ServerTrustPolicy` for the given host if applicable.
|
||||
///
|
||||
/// By default, this method will return the policy that perfectly matches the given host. Subclasses could override
|
||||
/// this method and implement more complex mapping implementations such as wildcards.
|
||||
///
|
||||
/// - parameter host: The host to use when searching for a matching policy.
|
||||
///
|
||||
/// - returns: The server trust policy for the given host if found.
|
||||
open func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
|
||||
return policies[host]
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension URLSession {
|
||||
private struct AssociatedKeys {
|
||||
static var managerKey = "URLSession.ServerTrustPolicyManager"
|
||||
}
|
||||
|
||||
var serverTrustPolicyManager: ServerTrustPolicyManager? {
|
||||
get {
|
||||
return objc_getAssociatedObject(self, &AssociatedKeys.managerKey) as? ServerTrustPolicyManager
|
||||
}
|
||||
set (manager) {
|
||||
objc_setAssociatedObject(self, &AssociatedKeys.managerKey, manager, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ServerTrustPolicy
|
||||
|
||||
/// The `ServerTrustPolicy` evaluates the server trust generally provided by an `NSURLAuthenticationChallenge` when
|
||||
/// connecting to a server over a secure HTTPS connection. The policy configuration then evaluates the server trust
|
||||
/// with a given set of criteria to determine whether the server trust is valid and the connection should be made.
|
||||
///
|
||||
/// Using pinned certificates or public keys for evaluation helps prevent man-in-the-middle (MITM) attacks and other
|
||||
/// vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged
|
||||
/// to route all communication over an HTTPS connection with pinning enabled.
|
||||
///
|
||||
/// - performDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to
|
||||
/// validate the host provided by the challenge. Applications are encouraged to always
|
||||
/// validate the host in production environments to guarantee the validity of the server's
|
||||
/// certificate chain.
|
||||
///
|
||||
/// - performRevokedEvaluation: 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.
|
||||
///
|
||||
/// - pinCertificates: Uses the pinned certificates to validate the server trust. The server trust is
|
||||
/// considered valid if one of the pinned certificates match one of the server certificates.
|
||||
/// By validating both the certificate chain and host, certificate pinning provides a very
|
||||
/// secure form of server trust validation mitigating most, if not all, MITM attacks.
|
||||
/// Applications are encouraged to always validate the host and require a valid certificate
|
||||
/// chain in production environments.
|
||||
///
|
||||
/// - pinPublicKeys: Uses the pinned public keys to validate the server trust. The server trust is considered
|
||||
/// valid if one of the pinned public keys match one of the server certificate public keys.
|
||||
/// By validating both the certificate chain and host, public key pinning provides a very
|
||||
/// secure form of server trust validation mitigating most, if not all, MITM attacks.
|
||||
/// Applications are encouraged to always validate the host and require a valid certificate
|
||||
/// chain in production environments.
|
||||
///
|
||||
/// - disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid.
|
||||
///
|
||||
/// - customEvaluation: Uses the associated closure to evaluate the validity of the server trust.
|
||||
public enum ServerTrustPolicy {
|
||||
case performDefaultEvaluation(validateHost: Bool)
|
||||
case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags)
|
||||
case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool)
|
||||
case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool)
|
||||
case disableEvaluation
|
||||
case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool)
|
||||
|
||||
// MARK: - Bundle Location
|
||||
|
||||
/// Returns all certificates within the given bundle with a `.cer` file extension.
|
||||
///
|
||||
/// - parameter bundle: The bundle to search for all `.cer` files.
|
||||
///
|
||||
/// - returns: All certificates within the given bundle.
|
||||
public static func certificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] {
|
||||
var certificates: [SecCertificate] = []
|
||||
|
||||
let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in
|
||||
bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil)
|
||||
}.joined())
|
||||
|
||||
for path in paths {
|
||||
if
|
||||
let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData,
|
||||
let certificate = SecCertificateCreateWithData(nil, certificateData)
|
||||
{
|
||||
certificates.append(certificate)
|
||||
}
|
||||
}
|
||||
|
||||
return certificates
|
||||
}
|
||||
|
||||
/// Returns all public keys within the given bundle with a `.cer` file extension.
|
||||
///
|
||||
/// - parameter bundle: The bundle to search for all `*.cer` files.
|
||||
///
|
||||
/// - returns: All public keys within the given bundle.
|
||||
public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] {
|
||||
var publicKeys: [SecKey] = []
|
||||
|
||||
for certificate in certificates(in: bundle) {
|
||||
if let publicKey = publicKey(for: certificate) {
|
||||
publicKeys.append(publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
return publicKeys
|
||||
}
|
||||
|
||||
// MARK: - Evaluation
|
||||
|
||||
/// Evaluates whether the server trust is valid for the given host.
|
||||
///
|
||||
/// - parameter serverTrust: The server trust to evaluate.
|
||||
/// - parameter host: The host of the challenge protection space.
|
||||
///
|
||||
/// - returns: Whether the server trust is valid.
|
||||
public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool {
|
||||
var serverTrustIsValid = false
|
||||
|
||||
switch self {
|
||||
case let .performDefaultEvaluation(validateHost):
|
||||
let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
|
||||
SecTrustSetPolicies(serverTrust, policy)
|
||||
|
||||
serverTrustIsValid = trustIsValid(serverTrust)
|
||||
case let .performRevokedEvaluation(validateHost, revocationFlags):
|
||||
let defaultPolicy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
|
||||
let revokedPolicy = SecPolicyCreateRevocation(revocationFlags)
|
||||
SecTrustSetPolicies(serverTrust, [defaultPolicy, revokedPolicy] as CFTypeRef)
|
||||
|
||||
serverTrustIsValid = trustIsValid(serverTrust)
|
||||
case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost):
|
||||
if validateCertificateChain {
|
||||
let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
|
||||
SecTrustSetPolicies(serverTrust, policy)
|
||||
|
||||
SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray)
|
||||
SecTrustSetAnchorCertificatesOnly(serverTrust, true)
|
||||
|
||||
serverTrustIsValid = trustIsValid(serverTrust)
|
||||
} else {
|
||||
let serverCertificatesDataArray = certificateData(for: serverTrust)
|
||||
let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates)
|
||||
|
||||
outerLoop: for serverCertificateData in serverCertificatesDataArray {
|
||||
for pinnedCertificateData in pinnedCertificatesDataArray {
|
||||
if serverCertificateData == pinnedCertificateData {
|
||||
serverTrustIsValid = true
|
||||
break outerLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost):
|
||||
var certificateChainEvaluationPassed = true
|
||||
|
||||
if validateCertificateChain {
|
||||
let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil)
|
||||
SecTrustSetPolicies(serverTrust, policy)
|
||||
|
||||
certificateChainEvaluationPassed = trustIsValid(serverTrust)
|
||||
}
|
||||
|
||||
if certificateChainEvaluationPassed {
|
||||
outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeys(for: serverTrust) as [AnyObject] {
|
||||
for pinnedPublicKey in pinnedPublicKeys as [AnyObject] {
|
||||
if serverPublicKey.isEqual(pinnedPublicKey) {
|
||||
serverTrustIsValid = true
|
||||
break outerLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case .disableEvaluation:
|
||||
serverTrustIsValid = true
|
||||
case let .customEvaluation(closure):
|
||||
serverTrustIsValid = closure(serverTrust, host)
|
||||
}
|
||||
|
||||
return serverTrustIsValid
|
||||
}
|
||||
|
||||
// MARK: - Private - Trust Validation
|
||||
|
||||
private func trustIsValid(_ trust: SecTrust) -> Bool {
|
||||
var isValid = false
|
||||
|
||||
var result = SecTrustResultType.invalid
|
||||
let status = SecTrustEvaluate(trust, &result)
|
||||
|
||||
if status == errSecSuccess {
|
||||
let unspecified = SecTrustResultType.unspecified
|
||||
let proceed = SecTrustResultType.proceed
|
||||
|
||||
|
||||
isValid = result == unspecified || result == proceed
|
||||
}
|
||||
|
||||
return isValid
|
||||
}
|
||||
|
||||
// MARK: - Private - Certificate Data
|
||||
|
||||
private func certificateData(for trust: SecTrust) -> [Data] {
|
||||
var certificates: [SecCertificate] = []
|
||||
|
||||
for index in 0..<SecTrustGetCertificateCount(trust) {
|
||||
if let certificate = SecTrustGetCertificateAtIndex(trust, index) {
|
||||
certificates.append(certificate)
|
||||
}
|
||||
}
|
||||
|
||||
return certificateData(for: certificates)
|
||||
}
|
||||
|
||||
private func certificateData(for certificates: [SecCertificate]) -> [Data] {
|
||||
return certificates.map { SecCertificateCopyData($0) as Data }
|
||||
}
|
||||
|
||||
// MARK: - Private - Public Key Extraction
|
||||
|
||||
private static func publicKeys(for trust: SecTrust) -> [SecKey] {
|
||||
var publicKeys: [SecKey] = []
|
||||
|
||||
for index in 0..<SecTrustGetCertificateCount(trust) {
|
||||
if
|
||||
let certificate = SecTrustGetCertificateAtIndex(trust, index),
|
||||
let publicKey = publicKey(for: certificate)
|
||||
{
|
||||
publicKeys.append(publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
return publicKeys
|
||||
}
|
||||
|
||||
private static func publicKey(for certificate: SecCertificate) -> SecKey? {
|
||||
var publicKey: SecKey?
|
||||
|
||||
let policy = SecPolicyCreateBasicX509()
|
||||
var trust: SecTrust?
|
||||
let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust)
|
||||
|
||||
if let trust = trust, trustCreationStatus == errSecSuccess {
|
||||
publicKey = SecTrustCopyPublicKey(trust)
|
||||
}
|
||||
|
||||
return publicKey
|
||||
}
|
||||
}
|
||||
725
Pods/Alamofire/Source/SessionDelegate.swift
generated
Normal file
725
Pods/Alamofire/Source/SessionDelegate.swift
generated
Normal file
@@ -0,0 +1,725 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// Responsible for handling all delegate callbacks for the underlying session.
|
||||
open class SessionDelegate: NSObject {
|
||||
|
||||
// MARK: URLSessionDelegate Overrides
|
||||
|
||||
/// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`.
|
||||
open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
|
||||
open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
|
||||
|
||||
/// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`.
|
||||
open var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`.
|
||||
open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?
|
||||
|
||||
// MARK: URLSessionTaskDelegate Overrides
|
||||
|
||||
/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`.
|
||||
open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
|
||||
|
||||
/// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and
|
||||
/// requires the caller to call the `completionHandler`.
|
||||
open var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`.
|
||||
open var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
|
||||
|
||||
/// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and
|
||||
/// requires the caller to call the `completionHandler`.
|
||||
open var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`.
|
||||
open var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
|
||||
|
||||
/// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and
|
||||
/// requires the caller to call the `completionHandler`.
|
||||
open var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`.
|
||||
open var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didCompleteWithError:)`.
|
||||
open var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?
|
||||
|
||||
// MARK: URLSessionDataDelegate Overrides
|
||||
|
||||
/// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)`.
|
||||
open var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
|
||||
|
||||
/// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and
|
||||
/// requires caller to call the `completionHandler`.
|
||||
open var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`.
|
||||
open var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:)`.
|
||||
open var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`.
|
||||
open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
|
||||
|
||||
/// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and
|
||||
/// requires caller to call the `completionHandler`.
|
||||
open var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)?
|
||||
|
||||
// MARK: URLSessionDownloadDelegate Overrides
|
||||
|
||||
/// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didFinishDownloadingTo:)`.
|
||||
open var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`.
|
||||
open var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
|
||||
|
||||
/// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)`.
|
||||
open var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
|
||||
|
||||
// MARK: URLSessionStreamDelegate Overrides
|
||||
|
||||
#if !os(watchOS)
|
||||
|
||||
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:readClosedFor:)`.
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
|
||||
get {
|
||||
return _streamTaskReadClosed as? (URLSession, URLSessionStreamTask) -> Void
|
||||
}
|
||||
set {
|
||||
_streamTaskReadClosed = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:writeClosedFor:)`.
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)? {
|
||||
get {
|
||||
return _streamTaskWriteClosed as? (URLSession, URLSessionStreamTask) -> Void
|
||||
}
|
||||
set {
|
||||
_streamTaskWriteClosed = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:betterRouteDiscoveredFor:)`.
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)? {
|
||||
get {
|
||||
return _streamTaskBetterRouteDiscovered as? (URLSession, URLSessionStreamTask) -> Void
|
||||
}
|
||||
set {
|
||||
_streamTaskBetterRouteDiscovered = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:streamTask:didBecome:outputStream:)`.
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)? {
|
||||
get {
|
||||
return _streamTaskDidBecomeInputStream as? (URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void
|
||||
}
|
||||
set {
|
||||
_streamTaskDidBecomeInputStream = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var _streamTaskReadClosed: Any?
|
||||
var _streamTaskWriteClosed: Any?
|
||||
var _streamTaskBetterRouteDiscovered: Any?
|
||||
var _streamTaskDidBecomeInputStream: Any?
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
var retrier: RequestRetrier?
|
||||
weak var sessionManager: SessionManager?
|
||||
|
||||
var requests: [Int: Request] = [:]
|
||||
private let lock = NSLock()
|
||||
|
||||
/// Access the task delegate for the specified task in a thread-safe manner.
|
||||
open subscript(task: URLSessionTask) -> Request? {
|
||||
get {
|
||||
lock.lock() ; defer { lock.unlock() }
|
||||
return requests[task.taskIdentifier]
|
||||
}
|
||||
set {
|
||||
lock.lock() ; defer { lock.unlock() }
|
||||
requests[task.taskIdentifier] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
/// Initializes the `SessionDelegate` instance.
|
||||
///
|
||||
/// - returns: The new `SessionDelegate` instance.
|
||||
public override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: NSObject Overrides
|
||||
|
||||
/// Returns a `Bool` indicating whether the `SessionDelegate` implements or inherits a method that can respond
|
||||
/// to a specified message.
|
||||
///
|
||||
/// - parameter selector: A selector that identifies a message.
|
||||
///
|
||||
/// - returns: `true` if the receiver implements or inherits a method that can respond to selector, otherwise `false`.
|
||||
open override func responds(to selector: Selector) -> Bool {
|
||||
#if !os(macOS)
|
||||
if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
|
||||
return sessionDidFinishEventsForBackgroundURLSession != nil
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !os(watchOS)
|
||||
if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) {
|
||||
switch selector {
|
||||
case #selector(URLSessionStreamDelegate.urlSession(_:readClosedFor:)):
|
||||
return streamTaskReadClosed != nil
|
||||
case #selector(URLSessionStreamDelegate.urlSession(_:writeClosedFor:)):
|
||||
return streamTaskWriteClosed != nil
|
||||
case #selector(URLSessionStreamDelegate.urlSession(_:betterRouteDiscoveredFor:)):
|
||||
return streamTaskBetterRouteDiscovered != nil
|
||||
case #selector(URLSessionStreamDelegate.urlSession(_:streamTask:didBecome:outputStream:)):
|
||||
return streamTaskDidBecomeInputAndOutputStreams != nil
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
switch selector {
|
||||
case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)):
|
||||
return sessionDidBecomeInvalidWithError != nil
|
||||
case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)):
|
||||
return (sessionDidReceiveChallenge != nil || sessionDidReceiveChallengeWithCompletion != nil)
|
||||
case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)):
|
||||
return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil)
|
||||
case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)):
|
||||
return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil)
|
||||
default:
|
||||
return type(of: self).instancesRespond(to: selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - URLSessionDelegate
|
||||
|
||||
extension SessionDelegate: URLSessionDelegate {
|
||||
/// Tells the delegate that the session has been invalidated.
|
||||
///
|
||||
/// - parameter session: The session object that was invalidated.
|
||||
/// - parameter error: The error that caused invalidation, or nil if the invalidation was explicit.
|
||||
open func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
|
||||
sessionDidBecomeInvalidWithError?(session, error)
|
||||
}
|
||||
|
||||
/// Requests credentials from the delegate in response to a session-level authentication request from the
|
||||
/// remote server.
|
||||
///
|
||||
/// - parameter session: The session containing the task that requested authentication.
|
||||
/// - parameter challenge: An object that contains the request for authentication.
|
||||
/// - parameter completionHandler: A handler that your delegate method must call providing the disposition
|
||||
/// and credential.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
didReceive challenge: URLAuthenticationChallenge,
|
||||
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
|
||||
{
|
||||
guard sessionDidReceiveChallengeWithCompletion == nil else {
|
||||
sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler)
|
||||
return
|
||||
}
|
||||
|
||||
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
|
||||
var credential: URLCredential?
|
||||
|
||||
if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
|
||||
(disposition, credential) = sessionDidReceiveChallenge(session, challenge)
|
||||
} else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
|
||||
let host = challenge.protectionSpace.host
|
||||
|
||||
if
|
||||
let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
|
||||
let serverTrust = challenge.protectionSpace.serverTrust
|
||||
{
|
||||
if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
|
||||
disposition = .useCredential
|
||||
credential = URLCredential(trust: serverTrust)
|
||||
} else {
|
||||
disposition = .cancelAuthenticationChallenge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler(disposition, credential)
|
||||
}
|
||||
|
||||
#if !os(macOS)
|
||||
|
||||
/// Tells the delegate that all messages enqueued for a session have been delivered.
|
||||
///
|
||||
/// - parameter session: The session that no longer has any outstanding requests.
|
||||
open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
|
||||
sessionDidFinishEventsForBackgroundURLSession?(session)
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: - URLSessionTaskDelegate
|
||||
|
||||
extension SessionDelegate: URLSessionTaskDelegate {
|
||||
/// Tells the delegate that the remote server requested an HTTP redirect.
|
||||
///
|
||||
/// - parameter session: The session containing the task whose request resulted in a redirect.
|
||||
/// - parameter task: The task whose request resulted in a redirect.
|
||||
/// - parameter response: An object containing the server’s response to the original request.
|
||||
/// - parameter request: A URL request object filled out with the new location.
|
||||
/// - parameter completionHandler: A closure that your handler should call with either the value of the request
|
||||
/// parameter, a modified URL request object, or NULL to refuse the redirect and
|
||||
/// return the body of the redirect response.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
task: URLSessionTask,
|
||||
willPerformHTTPRedirection response: HTTPURLResponse,
|
||||
newRequest request: URLRequest,
|
||||
completionHandler: @escaping (URLRequest?) -> Void)
|
||||
{
|
||||
guard taskWillPerformHTTPRedirectionWithCompletion == nil else {
|
||||
taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler)
|
||||
return
|
||||
}
|
||||
|
||||
var redirectRequest: URLRequest? = request
|
||||
|
||||
if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
|
||||
redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
|
||||
}
|
||||
|
||||
completionHandler(redirectRequest)
|
||||
}
|
||||
|
||||
/// Requests credentials from the delegate in response to an authentication request from the remote server.
|
||||
///
|
||||
/// - parameter session: The session containing the task whose request requires authentication.
|
||||
/// - parameter task: The task whose request requires authentication.
|
||||
/// - parameter challenge: An object that contains the request for authentication.
|
||||
/// - parameter completionHandler: A handler that your delegate method must call providing the disposition
|
||||
/// and credential.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
task: URLSessionTask,
|
||||
didReceive challenge: URLAuthenticationChallenge,
|
||||
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
|
||||
{
|
||||
guard taskDidReceiveChallengeWithCompletion == nil else {
|
||||
taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
|
||||
return
|
||||
}
|
||||
|
||||
if let taskDidReceiveChallenge = taskDidReceiveChallenge {
|
||||
let result = taskDidReceiveChallenge(session, task, challenge)
|
||||
completionHandler(result.0, result.1)
|
||||
} else if let delegate = self[task]?.delegate {
|
||||
delegate.urlSession(
|
||||
session,
|
||||
task: task,
|
||||
didReceive: challenge,
|
||||
completionHandler: completionHandler
|
||||
)
|
||||
} else {
|
||||
urlSession(session, didReceive: challenge, completionHandler: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tells the delegate when a task requires a new request body stream to send to the remote server.
|
||||
///
|
||||
/// - parameter session: The session containing the task that needs a new body stream.
|
||||
/// - parameter task: The task that needs a new body stream.
|
||||
/// - parameter completionHandler: A completion handler that your delegate method should call with the new body stream.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
task: URLSessionTask,
|
||||
needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
|
||||
{
|
||||
guard taskNeedNewBodyStreamWithCompletion == nil else {
|
||||
taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler)
|
||||
return
|
||||
}
|
||||
|
||||
if let taskNeedNewBodyStream = taskNeedNewBodyStream {
|
||||
completionHandler(taskNeedNewBodyStream(session, task))
|
||||
} else if let delegate = self[task]?.delegate {
|
||||
delegate.urlSession(session, task: task, needNewBodyStream: completionHandler)
|
||||
}
|
||||
}
|
||||
|
||||
/// Periodically informs the delegate of the progress of sending body content to the server.
|
||||
///
|
||||
/// - parameter session: The session containing the data task.
|
||||
/// - parameter task: The data task.
|
||||
/// - parameter bytesSent: The number of bytes sent since the last time this delegate method was called.
|
||||
/// - parameter totalBytesSent: The total number of bytes sent so far.
|
||||
/// - parameter totalBytesExpectedToSend: The expected length of the body data.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
task: URLSessionTask,
|
||||
didSendBodyData bytesSent: Int64,
|
||||
totalBytesSent: Int64,
|
||||
totalBytesExpectedToSend: Int64)
|
||||
{
|
||||
if let taskDidSendBodyData = taskDidSendBodyData {
|
||||
taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
|
||||
} else if let delegate = self[task]?.delegate as? UploadTaskDelegate {
|
||||
delegate.URLSession(
|
||||
session,
|
||||
task: task,
|
||||
didSendBodyData: bytesSent,
|
||||
totalBytesSent: totalBytesSent,
|
||||
totalBytesExpectedToSend: totalBytesExpectedToSend
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#if !os(watchOS)
|
||||
|
||||
/// Tells the delegate that the session finished collecting metrics for the task.
|
||||
///
|
||||
/// - parameter session: The session collecting the metrics.
|
||||
/// - parameter task: The task whose metrics have been collected.
|
||||
/// - parameter metrics: The collected metrics.
|
||||
@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
|
||||
@objc(URLSession:task:didFinishCollectingMetrics:)
|
||||
open func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
|
||||
self[task]?.delegate.metrics = metrics
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// Tells the delegate that the task finished transferring data.
|
||||
///
|
||||
/// - parameter session: The session containing the task whose request finished transferring data.
|
||||
/// - parameter task: The task whose request finished transferring data.
|
||||
/// - parameter error: If an error occurred, an error object indicating how the transfer failed, otherwise nil.
|
||||
open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
||||
/// Executed after it is determined that the request is not going to be retried
|
||||
let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
strongSelf.taskDidComplete?(session, task, error)
|
||||
|
||||
strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
|
||||
|
||||
var userInfo: [String: Any] = [Notification.Key.Task: task]
|
||||
|
||||
if let data = (strongSelf[task]?.delegate as? DataTaskDelegate)?.data {
|
||||
userInfo[Notification.Key.ResponseData] = data
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(
|
||||
name: Notification.Name.Task.DidComplete,
|
||||
object: strongSelf,
|
||||
userInfo: userInfo
|
||||
)
|
||||
|
||||
strongSelf[task] = nil
|
||||
}
|
||||
|
||||
guard let request = self[task], let sessionManager = sessionManager else {
|
||||
completeTask(session, task, error)
|
||||
return
|
||||
}
|
||||
|
||||
// Run all validations on the request before checking if an error occurred
|
||||
request.validations.forEach { $0() }
|
||||
|
||||
// Determine whether an error has occurred
|
||||
var error: Error? = error
|
||||
|
||||
if request.delegate.error != nil {
|
||||
error = request.delegate.error
|
||||
}
|
||||
|
||||
/// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
|
||||
/// should be retried. Otherwise, complete the task by notifying the task delegate.
|
||||
if let retrier = retrier, let error = error {
|
||||
retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
|
||||
guard shouldRetry else { completeTask(session, task, error) ; return }
|
||||
|
||||
DispatchQueue.utility.after(timeDelay) { [weak self] in
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false
|
||||
|
||||
if retrySucceeded, let task = request.task {
|
||||
strongSelf[task] = request
|
||||
return
|
||||
} else {
|
||||
completeTask(session, task, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completeTask(session, task, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - URLSessionDataDelegate
|
||||
|
||||
extension SessionDelegate: URLSessionDataDelegate {
|
||||
/// Tells the delegate that the data task received the initial reply (headers) from the server.
|
||||
///
|
||||
/// - parameter session: The session containing the data task that received an initial reply.
|
||||
/// - parameter dataTask: The data task that received an initial reply.
|
||||
/// - parameter response: A URL response object populated with headers.
|
||||
/// - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a
|
||||
/// constant to indicate whether the transfer should continue as a data task or
|
||||
/// should become a download task.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
dataTask: URLSessionDataTask,
|
||||
didReceive response: URLResponse,
|
||||
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
|
||||
{
|
||||
guard dataTaskDidReceiveResponseWithCompletion == nil else {
|
||||
dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler)
|
||||
return
|
||||
}
|
||||
|
||||
var disposition: URLSession.ResponseDisposition = .allow
|
||||
|
||||
if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
|
||||
disposition = dataTaskDidReceiveResponse(session, dataTask, response)
|
||||
}
|
||||
|
||||
completionHandler(disposition)
|
||||
}
|
||||
|
||||
/// Tells the delegate that the data task was changed to a download task.
|
||||
///
|
||||
/// - parameter session: The session containing the task that was replaced by a download task.
|
||||
/// - parameter dataTask: The data task that was replaced by a download task.
|
||||
/// - parameter downloadTask: The new download task that replaced the data task.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
dataTask: URLSessionDataTask,
|
||||
didBecome downloadTask: URLSessionDownloadTask)
|
||||
{
|
||||
if let dataTaskDidBecomeDownloadTask = dataTaskDidBecomeDownloadTask {
|
||||
dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask)
|
||||
} else {
|
||||
self[downloadTask]?.delegate = DownloadTaskDelegate(task: downloadTask)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tells the delegate that the data task has received some of the expected data.
|
||||
///
|
||||
/// - parameter session: The session containing the data task that provided data.
|
||||
/// - parameter dataTask: The data task that provided data.
|
||||
/// - parameter data: A data object containing the transferred data.
|
||||
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
||||
if let dataTaskDidReceiveData = dataTaskDidReceiveData {
|
||||
dataTaskDidReceiveData(session, dataTask, data)
|
||||
} else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
|
||||
delegate.urlSession(session, dataTask: dataTask, didReceive: data)
|
||||
}
|
||||
}
|
||||
|
||||
/// Asks the delegate whether the data (or upload) task should store the response in the cache.
|
||||
///
|
||||
/// - parameter session: The session containing the data (or upload) task.
|
||||
/// - parameter dataTask: The data (or upload) task.
|
||||
/// - parameter proposedResponse: The default caching behavior. This behavior is determined based on the current
|
||||
/// caching policy and the values of certain received headers, such as the Pragma
|
||||
/// and Cache-Control headers.
|
||||
/// - parameter completionHandler: A block that your handler must call, providing either the original proposed
|
||||
/// response, a modified version of that response, or NULL to prevent caching the
|
||||
/// response. If your delegate implements this method, it must call this completion
|
||||
/// handler; otherwise, your app leaks memory.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
dataTask: URLSessionDataTask,
|
||||
willCacheResponse proposedResponse: CachedURLResponse,
|
||||
completionHandler: @escaping (CachedURLResponse?) -> Void)
|
||||
{
|
||||
guard dataTaskWillCacheResponseWithCompletion == nil else {
|
||||
dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler)
|
||||
return
|
||||
}
|
||||
|
||||
if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
|
||||
completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
|
||||
} else if let delegate = self[dataTask]?.delegate as? DataTaskDelegate {
|
||||
delegate.urlSession(
|
||||
session,
|
||||
dataTask: dataTask,
|
||||
willCacheResponse: proposedResponse,
|
||||
completionHandler: completionHandler
|
||||
)
|
||||
} else {
|
||||
completionHandler(proposedResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - URLSessionDownloadDelegate
|
||||
|
||||
extension SessionDelegate: URLSessionDownloadDelegate {
|
||||
/// Tells the delegate that a download task has finished downloading.
|
||||
///
|
||||
/// - parameter session: The session containing the download task that finished.
|
||||
/// - parameter downloadTask: The download task that finished.
|
||||
/// - parameter location: A file URL for the temporary file. Because the file is temporary, you must either
|
||||
/// open the file for reading or move it to a permanent location in your app’s sandbox
|
||||
/// container directory before returning from this delegate method.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
downloadTask: URLSessionDownloadTask,
|
||||
didFinishDownloadingTo location: URL)
|
||||
{
|
||||
if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
|
||||
downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
|
||||
} else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
|
||||
delegate.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
|
||||
}
|
||||
}
|
||||
|
||||
/// Periodically informs the delegate about the download’s progress.
|
||||
///
|
||||
/// - parameter session: The session containing the download task.
|
||||
/// - parameter downloadTask: The download task.
|
||||
/// - parameter bytesWritten: The number of bytes transferred since the last time this delegate
|
||||
/// method was called.
|
||||
/// - parameter totalBytesWritten: The total number of bytes transferred so far.
|
||||
/// - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length
|
||||
/// header. If this header was not provided, the value is
|
||||
/// `NSURLSessionTransferSizeUnknown`.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
downloadTask: URLSessionDownloadTask,
|
||||
didWriteData bytesWritten: Int64,
|
||||
totalBytesWritten: Int64,
|
||||
totalBytesExpectedToWrite: Int64)
|
||||
{
|
||||
if let downloadTaskDidWriteData = downloadTaskDidWriteData {
|
||||
downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
|
||||
} else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
|
||||
delegate.urlSession(
|
||||
session,
|
||||
downloadTask: downloadTask,
|
||||
didWriteData: bytesWritten,
|
||||
totalBytesWritten: totalBytesWritten,
|
||||
totalBytesExpectedToWrite: totalBytesExpectedToWrite
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tells the delegate that the download task has resumed downloading.
|
||||
///
|
||||
/// - parameter session: The session containing the download task that finished.
|
||||
/// - parameter downloadTask: The download task that resumed. See explanation in the discussion.
|
||||
/// - parameter fileOffset: If the file's cache policy or last modified date prevents reuse of the
|
||||
/// existing content, then this value is zero. Otherwise, this value is an
|
||||
/// integer representing the number of bytes on disk that do not need to be
|
||||
/// retrieved again.
|
||||
/// - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header.
|
||||
/// If this header was not provided, the value is NSURLSessionTransferSizeUnknown.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
downloadTask: URLSessionDownloadTask,
|
||||
didResumeAtOffset fileOffset: Int64,
|
||||
expectedTotalBytes: Int64)
|
||||
{
|
||||
if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
|
||||
downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
|
||||
} else if let delegate = self[downloadTask]?.delegate as? DownloadTaskDelegate {
|
||||
delegate.urlSession(
|
||||
session,
|
||||
downloadTask: downloadTask,
|
||||
didResumeAtOffset: fileOffset,
|
||||
expectedTotalBytes: expectedTotalBytes
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - URLSessionStreamDelegate
|
||||
|
||||
#if !os(watchOS)
|
||||
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
extension SessionDelegate: URLSessionStreamDelegate {
|
||||
/// Tells the delegate that the read side of the connection has been closed.
|
||||
///
|
||||
/// - parameter session: The session.
|
||||
/// - parameter streamTask: The stream task.
|
||||
open func urlSession(_ session: URLSession, readClosedFor streamTask: URLSessionStreamTask) {
|
||||
streamTaskReadClosed?(session, streamTask)
|
||||
}
|
||||
|
||||
/// Tells the delegate that the write side of the connection has been closed.
|
||||
///
|
||||
/// - parameter session: The session.
|
||||
/// - parameter streamTask: The stream task.
|
||||
open func urlSession(_ session: URLSession, writeClosedFor streamTask: URLSessionStreamTask) {
|
||||
streamTaskWriteClosed?(session, streamTask)
|
||||
}
|
||||
|
||||
/// Tells the delegate that the system has determined that a better route to the host is available.
|
||||
///
|
||||
/// - parameter session: The session.
|
||||
/// - parameter streamTask: The stream task.
|
||||
open func urlSession(_ session: URLSession, betterRouteDiscoveredFor streamTask: URLSessionStreamTask) {
|
||||
streamTaskBetterRouteDiscovered?(session, streamTask)
|
||||
}
|
||||
|
||||
/// Tells the delegate that the stream task has been completed and provides the unopened stream objects.
|
||||
///
|
||||
/// - parameter session: The session.
|
||||
/// - parameter streamTask: The stream task.
|
||||
/// - parameter inputStream: The new input stream.
|
||||
/// - parameter outputStream: The new output stream.
|
||||
open func urlSession(
|
||||
_ session: URLSession,
|
||||
streamTask: URLSessionStreamTask,
|
||||
didBecome inputStream: InputStream,
|
||||
outputStream: OutputStream)
|
||||
{
|
||||
streamTaskDidBecomeInputAndOutputStreams?(session, streamTask, inputStream, outputStream)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
896
Pods/Alamofire/Source/SessionManager.swift
generated
Normal file
896
Pods/Alamofire/Source/SessionManager.swift
generated
Normal file
@@ -0,0 +1,896 @@
|
||||
//
|
||||
// SessionManager.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
|
||||
|
||||
/// Responsible for creating and managing `Request` objects, as well as their underlying `NSURLSession`.
|
||||
open class SessionManager {
|
||||
|
||||
// MARK: - Helper Types
|
||||
|
||||
/// Defines whether the `MultipartFormData` encoding was successful and contains result of the encoding as
|
||||
/// associated values.
|
||||
///
|
||||
/// - Success: Represents a successful `MultipartFormData` encoding and contains the new `UploadRequest` along with
|
||||
/// streaming information.
|
||||
/// - Failure: Used to represent a failure in the `MultipartFormData` encoding and also contains the encoding
|
||||
/// error.
|
||||
public enum MultipartFormDataEncodingResult {
|
||||
case success(request: UploadRequest, streamingFromDisk: Bool, streamFileURL: URL?)
|
||||
case failure(Error)
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/// A default instance of `SessionManager`, used by top-level Alamofire request methods, and suitable for use
|
||||
/// directly for any ad hoc requests.
|
||||
public static let `default`: SessionManager = {
|
||||
let configuration = URLSessionConfiguration.default
|
||||
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
|
||||
|
||||
return SessionManager(configuration: configuration)
|
||||
}()
|
||||
|
||||
/// Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers.
|
||||
public static let defaultHTTPHeaders: HTTPHeaders = {
|
||||
// Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3
|
||||
let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"
|
||||
|
||||
// Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5
|
||||
let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
|
||||
let quality = 1.0 - (Double(index) * 0.1)
|
||||
return "\(languageCode);q=\(quality)"
|
||||
}.joined(separator: ", ")
|
||||
|
||||
// User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3
|
||||
// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0`
|
||||
let userAgent: String = {
|
||||
if let info = Bundle.main.infoDictionary {
|
||||
let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
|
||||
let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
|
||||
let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown"
|
||||
let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown"
|
||||
|
||||
let osNameVersion: String = {
|
||||
let version = ProcessInfo.processInfo.operatingSystemVersion
|
||||
let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
|
||||
|
||||
let osName: String = {
|
||||
#if os(iOS)
|
||||
return "iOS"
|
||||
#elseif os(watchOS)
|
||||
return "watchOS"
|
||||
#elseif os(tvOS)
|
||||
return "tvOS"
|
||||
#elseif os(macOS)
|
||||
return "OS X"
|
||||
#elseif os(Linux)
|
||||
return "Linux"
|
||||
#else
|
||||
return "Unknown"
|
||||
#endif
|
||||
}()
|
||||
|
||||
return "\(osName) \(versionString)"
|
||||
}()
|
||||
|
||||
let alamofireVersion: String = {
|
||||
guard
|
||||
let afInfo = Bundle(for: SessionManager.self).infoDictionary,
|
||||
let build = afInfo["CFBundleShortVersionString"]
|
||||
else { return "Unknown" }
|
||||
|
||||
return "Alamofire/\(build)"
|
||||
}()
|
||||
|
||||
return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"
|
||||
}
|
||||
|
||||
return "Alamofire"
|
||||
}()
|
||||
|
||||
return [
|
||||
"Accept-Encoding": acceptEncoding,
|
||||
"Accept-Language": acceptLanguage,
|
||||
"User-Agent": userAgent
|
||||
]
|
||||
}()
|
||||
|
||||
/// Default memory threshold used when encoding `MultipartFormData` in bytes.
|
||||
public static let multipartFormDataEncodingMemoryThreshold: UInt64 = 10_000_000
|
||||
|
||||
/// The underlying session.
|
||||
public let session: URLSession
|
||||
|
||||
/// The session delegate handling all the task and session delegate callbacks.
|
||||
public let delegate: SessionDelegate
|
||||
|
||||
/// Whether to start requests immediately after being constructed. `true` by default.
|
||||
open var startRequestsImmediately: Bool = true
|
||||
|
||||
/// The request adapter called each time a new request is created.
|
||||
open var adapter: RequestAdapter?
|
||||
|
||||
/// The request retrier called each time a request encounters an error to determine whether to retry the request.
|
||||
open var retrier: RequestRetrier? {
|
||||
get { return delegate.retrier }
|
||||
set { delegate.retrier = newValue }
|
||||
}
|
||||
|
||||
/// The background completion handler closure provided by the UIApplicationDelegate
|
||||
/// `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background
|
||||
/// completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation
|
||||
/// will automatically call the handler.
|
||||
///
|
||||
/// If you need to handle your own events before the handler is called, then you need to override the
|
||||
/// SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished.
|
||||
///
|
||||
/// `nil` by default.
|
||||
open var backgroundCompletionHandler: (() -> Void)?
|
||||
|
||||
let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
/// Creates an instance with the specified `configuration`, `delegate` and `serverTrustPolicyManager`.
|
||||
///
|
||||
/// - parameter configuration: The configuration used to construct the managed session.
|
||||
/// `URLSessionConfiguration.default` by default.
|
||||
/// - parameter delegate: The delegate used when initializing the session. `SessionDelegate()` by
|
||||
/// default.
|
||||
/// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
|
||||
/// challenges. `nil` by default.
|
||||
///
|
||||
/// - returns: The new `SessionManager` instance.
|
||||
public init(
|
||||
configuration: URLSessionConfiguration = URLSessionConfiguration.default,
|
||||
delegate: SessionDelegate = SessionDelegate(),
|
||||
serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
|
||||
{
|
||||
self.delegate = delegate
|
||||
self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
|
||||
|
||||
commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
|
||||
}
|
||||
|
||||
/// Creates an instance with the specified `session`, `delegate` and `serverTrustPolicyManager`.
|
||||
///
|
||||
/// - parameter session: The URL session.
|
||||
/// - parameter delegate: The delegate of the URL session. Must equal the URL session's delegate.
|
||||
/// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
|
||||
/// challenges. `nil` by default.
|
||||
///
|
||||
/// - returns: The new `SessionManager` instance if the URL session's delegate matches; `nil` otherwise.
|
||||
public init?(
|
||||
session: URLSession,
|
||||
delegate: SessionDelegate,
|
||||
serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
|
||||
{
|
||||
guard delegate === session.delegate else { return nil }
|
||||
|
||||
self.delegate = delegate
|
||||
self.session = session
|
||||
|
||||
commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
|
||||
}
|
||||
|
||||
private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
|
||||
session.serverTrustPolicyManager = serverTrustPolicyManager
|
||||
|
||||
delegate.sessionManager = self
|
||||
|
||||
delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
|
||||
guard let strongSelf = self else { return }
|
||||
DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
session.invalidateAndCancel()
|
||||
}
|
||||
|
||||
// MARK: - Data Request
|
||||
|
||||
/// Creates a `DataRequest` to retrieve the contents of the specified `url`, `method`, `parameters`, `encoding`
|
||||
/// and `headers`.
|
||||
///
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.get` by default.
|
||||
/// - parameter parameters: The parameters. `nil` by default.
|
||||
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `DataRequest`.
|
||||
@discardableResult
|
||||
open func request(
|
||||
_ url: URLConvertible,
|
||||
method: HTTPMethod = .get,
|
||||
parameters: Parameters? = nil,
|
||||
encoding: ParameterEncoding = URLEncoding.default,
|
||||
headers: HTTPHeaders? = nil)
|
||||
-> DataRequest
|
||||
{
|
||||
var originalRequest: URLRequest?
|
||||
|
||||
do {
|
||||
originalRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
|
||||
return request(encodedURLRequest)
|
||||
} catch {
|
||||
return request(originalRequest, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `DataRequest` to retrieve the contents of a URL based on the specified `urlRequest`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter urlRequest: The URL request.
|
||||
///
|
||||
/// - returns: The created `DataRequest`.
|
||||
@discardableResult
|
||||
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
|
||||
var originalRequest: URLRequest?
|
||||
|
||||
do {
|
||||
originalRequest = try urlRequest.asURLRequest()
|
||||
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
|
||||
|
||||
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
|
||||
let request = DataRequest(session: session, requestTask: .data(originalTask, task))
|
||||
|
||||
delegate[task] = request
|
||||
|
||||
if startRequestsImmediately { request.resume() }
|
||||
|
||||
return request
|
||||
} catch {
|
||||
return request(originalRequest, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private - Request Implementation
|
||||
|
||||
private func request(_ urlRequest: URLRequest?, failedWith error: Error) -> DataRequest {
|
||||
var requestTask: Request.RequestTask = .data(nil, nil)
|
||||
|
||||
if let urlRequest = urlRequest {
|
||||
let originalTask = DataRequest.Requestable(urlRequest: urlRequest)
|
||||
requestTask = .data(originalTask, nil)
|
||||
}
|
||||
|
||||
let underlyingError = error.underlyingAdaptError ?? error
|
||||
let request = DataRequest(session: session, requestTask: requestTask, error: underlyingError)
|
||||
|
||||
if let retrier = retrier, error is AdaptError {
|
||||
allowRetrier(retrier, toRetry: request, with: underlyingError)
|
||||
} else {
|
||||
if startRequestsImmediately { request.resume() }
|
||||
}
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
// MARK: - Download Request
|
||||
|
||||
// MARK: URL Request
|
||||
|
||||
/// Creates a `DownloadRequest` to retrieve the contents the specified `url`, `method`, `parameters`, `encoding`,
|
||||
/// `headers` and save them to the `destination`.
|
||||
///
|
||||
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||
/// underlying URL session.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.get` by default.
|
||||
/// - parameter parameters: The parameters. `nil` by default.
|
||||
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `DownloadRequest`.
|
||||
@discardableResult
|
||||
open func download(
|
||||
_ url: URLConvertible,
|
||||
method: HTTPMethod = .get,
|
||||
parameters: Parameters? = nil,
|
||||
encoding: ParameterEncoding = URLEncoding.default,
|
||||
headers: HTTPHeaders? = nil,
|
||||
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||
-> DownloadRequest
|
||||
{
|
||||
do {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
|
||||
return download(encodedURLRequest, to: destination)
|
||||
} catch {
|
||||
return download(nil, to: destination, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `DownloadRequest` to retrieve the contents of a URL based on the specified `urlRequest` and save
|
||||
/// them to the `destination`.
|
||||
///
|
||||
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||
/// underlying URL session.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter urlRequest: The URL request
|
||||
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `DownloadRequest`.
|
||||
@discardableResult
|
||||
open func download(
|
||||
_ urlRequest: URLRequestConvertible,
|
||||
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||
-> DownloadRequest
|
||||
{
|
||||
do {
|
||||
let urlRequest = try urlRequest.asURLRequest()
|
||||
return download(.request(urlRequest), to: destination)
|
||||
} catch {
|
||||
return download(nil, to: destination, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Resume Data
|
||||
|
||||
/// Creates a `DownloadRequest` from the `resumeData` produced from a previous request cancellation to retrieve
|
||||
/// the contents of the original request and save them to the `destination`.
|
||||
///
|
||||
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
|
||||
/// underlying URL session.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// On the latest release of all the Apple platforms (iOS 10, macOS 10.12, tvOS 10, watchOS 3), `resumeData` is broken
|
||||
/// on background URL session configurations. There's an underlying bug in the `resumeData` generation logic where the
|
||||
/// data is written incorrectly and will always fail to resume the download. For more information about the bug and
|
||||
/// possible workarounds, please refer to the following Stack Overflow post:
|
||||
///
|
||||
/// - http://stackoverflow.com/a/39347461/1342462
|
||||
///
|
||||
/// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask`
|
||||
/// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for
|
||||
/// additional information.
|
||||
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `DownloadRequest`.
|
||||
@discardableResult
|
||||
open func download(
|
||||
resumingWith resumeData: Data,
|
||||
to destination: DownloadRequest.DownloadFileDestination? = nil)
|
||||
-> DownloadRequest
|
||||
{
|
||||
return download(.resumeData(resumeData), to: destination)
|
||||
}
|
||||
|
||||
// MARK: Private - Download Implementation
|
||||
|
||||
private func download(
|
||||
_ downloadable: DownloadRequest.Downloadable,
|
||||
to destination: DownloadRequest.DownloadFileDestination?)
|
||||
-> DownloadRequest
|
||||
{
|
||||
do {
|
||||
let task = try downloadable.task(session: session, adapter: adapter, queue: queue)
|
||||
let download = DownloadRequest(session: session, requestTask: .download(downloadable, task))
|
||||
|
||||
download.downloadDelegate.destination = destination
|
||||
|
||||
delegate[task] = download
|
||||
|
||||
if startRequestsImmediately { download.resume() }
|
||||
|
||||
return download
|
||||
} catch {
|
||||
return download(downloadable, to: destination, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func download(
|
||||
_ downloadable: DownloadRequest.Downloadable?,
|
||||
to destination: DownloadRequest.DownloadFileDestination?,
|
||||
failedWith error: Error)
|
||||
-> DownloadRequest
|
||||
{
|
||||
var downloadTask: Request.RequestTask = .download(nil, nil)
|
||||
|
||||
if let downloadable = downloadable {
|
||||
downloadTask = .download(downloadable, nil)
|
||||
}
|
||||
|
||||
let underlyingError = error.underlyingAdaptError ?? error
|
||||
|
||||
let download = DownloadRequest(session: session, requestTask: downloadTask, error: underlyingError)
|
||||
download.downloadDelegate.destination = destination
|
||||
|
||||
if let retrier = retrier, error is AdaptError {
|
||||
allowRetrier(retrier, toRetry: download, with: underlyingError)
|
||||
} else {
|
||||
if startRequestsImmediately { download.resume() }
|
||||
}
|
||||
|
||||
return download
|
||||
}
|
||||
|
||||
// MARK: - Upload Request
|
||||
|
||||
// MARK: File
|
||||
|
||||
/// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `file`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter file: The file to upload.
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.post` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
open func upload(
|
||||
_ fileURL: URL,
|
||||
to url: URLConvertible,
|
||||
method: HTTPMethod = .post,
|
||||
headers: HTTPHeaders? = nil)
|
||||
-> UploadRequest
|
||||
{
|
||||
do {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
return upload(fileURL, with: urlRequest)
|
||||
} catch {
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `UploadRequest` from the specified `urlRequest` for uploading the `file`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter file: The file to upload.
|
||||
/// - parameter urlRequest: The URL request.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
open func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||
do {
|
||||
let urlRequest = try urlRequest.asURLRequest()
|
||||
return upload(.file(fileURL, urlRequest))
|
||||
} catch {
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Data
|
||||
|
||||
/// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `data`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter data: The data to upload.
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.post` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
open func upload(
|
||||
_ data: Data,
|
||||
to url: URLConvertible,
|
||||
method: HTTPMethod = .post,
|
||||
headers: HTTPHeaders? = nil)
|
||||
-> UploadRequest
|
||||
{
|
||||
do {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
return upload(data, with: urlRequest)
|
||||
} catch {
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an `UploadRequest` from the specified `urlRequest` for uploading the `data`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter data: The data to upload.
|
||||
/// - parameter urlRequest: The URL request.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
open func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||
do {
|
||||
let urlRequest = try urlRequest.asURLRequest()
|
||||
return upload(.data(data, urlRequest))
|
||||
} catch {
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: InputStream
|
||||
|
||||
/// Creates an `UploadRequest` from the specified `url`, `method` and `headers` for uploading the `stream`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter stream: The stream to upload.
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.post` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
open func upload(
|
||||
_ stream: InputStream,
|
||||
to url: URLConvertible,
|
||||
method: HTTPMethod = .post,
|
||||
headers: HTTPHeaders? = nil)
|
||||
-> UploadRequest
|
||||
{
|
||||
do {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
return upload(stream, with: urlRequest)
|
||||
} catch {
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an `UploadRequest` from the specified `urlRequest` for uploading the `stream`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter stream: The stream to upload.
|
||||
/// - parameter urlRequest: The URL request.
|
||||
///
|
||||
/// - returns: The created `UploadRequest`.
|
||||
@discardableResult
|
||||
open func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest {
|
||||
do {
|
||||
let urlRequest = try urlRequest.asURLRequest()
|
||||
return upload(.stream(stream, urlRequest))
|
||||
} catch {
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: MultipartFormData
|
||||
|
||||
/// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new
|
||||
/// `UploadRequest` using the `url`, `method` and `headers`.
|
||||
///
|
||||
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
|
||||
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
|
||||
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
|
||||
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
|
||||
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
|
||||
/// used for larger payloads such as video content.
|
||||
///
|
||||
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
|
||||
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
|
||||
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
|
||||
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
|
||||
/// technique was used.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
|
||||
/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
|
||||
/// `multipartFormDataEncodingMemoryThreshold` by default.
|
||||
/// - parameter url: The URL.
|
||||
/// - parameter method: The HTTP method. `.post` by default.
|
||||
/// - parameter headers: The HTTP headers. `nil` by default.
|
||||
/// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
|
||||
open func upload(
|
||||
multipartFormData: @escaping (MultipartFormData) -> Void,
|
||||
usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
|
||||
to url: URLConvertible,
|
||||
method: HTTPMethod = .post,
|
||||
headers: HTTPHeaders? = nil,
|
||||
encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
|
||||
{
|
||||
do {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
|
||||
return upload(
|
||||
multipartFormData: multipartFormData,
|
||||
usingThreshold: encodingMemoryThreshold,
|
||||
with: urlRequest,
|
||||
encodingCompletion: encodingCompletion
|
||||
)
|
||||
} catch {
|
||||
DispatchQueue.main.async { encodingCompletion?(.failure(error)) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new
|
||||
/// `UploadRequest` using the `urlRequest`.
|
||||
///
|
||||
/// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
|
||||
/// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
|
||||
/// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
|
||||
/// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
|
||||
/// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
|
||||
/// used for larger payloads such as video content.
|
||||
///
|
||||
/// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
|
||||
/// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
|
||||
/// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
|
||||
/// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
|
||||
/// technique was used.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
|
||||
/// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
|
||||
/// `multipartFormDataEncodingMemoryThreshold` by default.
|
||||
/// - parameter urlRequest: The URL request.
|
||||
/// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
|
||||
open func upload(
|
||||
multipartFormData: @escaping (MultipartFormData) -> Void,
|
||||
usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
|
||||
with urlRequest: URLRequestConvertible,
|
||||
encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
|
||||
{
|
||||
DispatchQueue.global(qos: .utility).async {
|
||||
let formData = MultipartFormData()
|
||||
multipartFormData(formData)
|
||||
|
||||
var tempFileURL: URL?
|
||||
|
||||
do {
|
||||
var urlRequestWithContentType = try urlRequest.asURLRequest()
|
||||
urlRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type")
|
||||
|
||||
let isBackgroundSession = self.session.configuration.identifier != nil
|
||||
|
||||
if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession {
|
||||
let data = try formData.encode()
|
||||
|
||||
let encodingResult = MultipartFormDataEncodingResult.success(
|
||||
request: self.upload(data, with: urlRequestWithContentType),
|
||||
streamingFromDisk: false,
|
||||
streamFileURL: nil
|
||||
)
|
||||
|
||||
DispatchQueue.main.async { encodingCompletion?(encodingResult) }
|
||||
} else {
|
||||
let fileManager = FileManager.default
|
||||
let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory())
|
||||
let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data")
|
||||
let fileName = UUID().uuidString
|
||||
let fileURL = directoryURL.appendingPathComponent(fileName)
|
||||
|
||||
tempFileURL = fileURL
|
||||
|
||||
var directoryError: Error?
|
||||
|
||||
// Create directory inside serial queue to ensure two threads don't do this in parallel
|
||||
self.queue.sync {
|
||||
do {
|
||||
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
|
||||
} catch {
|
||||
directoryError = error
|
||||
}
|
||||
}
|
||||
|
||||
if let directoryError = directoryError { throw directoryError }
|
||||
|
||||
try formData.writeEncodedData(to: fileURL)
|
||||
|
||||
let upload = self.upload(fileURL, with: urlRequestWithContentType)
|
||||
|
||||
// Cleanup the temp file once the upload is complete
|
||||
upload.delegate.queue.addOperation {
|
||||
do {
|
||||
try FileManager.default.removeItem(at: fileURL)
|
||||
} catch {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let encodingResult = MultipartFormDataEncodingResult.success(
|
||||
request: upload,
|
||||
streamingFromDisk: true,
|
||||
streamFileURL: fileURL
|
||||
)
|
||||
|
||||
encodingCompletion?(encodingResult)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Cleanup the temp file in the event that the multipart form data encoding failed
|
||||
if let tempFileURL = tempFileURL {
|
||||
do {
|
||||
try FileManager.default.removeItem(at: tempFileURL)
|
||||
} catch {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.async { encodingCompletion?(.failure(error)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private - Upload Implementation
|
||||
|
||||
private func upload(_ uploadable: UploadRequest.Uploadable) -> UploadRequest {
|
||||
do {
|
||||
let task = try uploadable.task(session: session, adapter: adapter, queue: queue)
|
||||
let upload = UploadRequest(session: session, requestTask: .upload(uploadable, task))
|
||||
|
||||
if case let .stream(inputStream, _) = uploadable {
|
||||
upload.delegate.taskNeedNewBodyStream = { _, _ in inputStream }
|
||||
}
|
||||
|
||||
delegate[task] = upload
|
||||
|
||||
if startRequestsImmediately { upload.resume() }
|
||||
|
||||
return upload
|
||||
} catch {
|
||||
return upload(uploadable, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func upload(_ uploadable: UploadRequest.Uploadable?, failedWith error: Error) -> UploadRequest {
|
||||
var uploadTask: Request.RequestTask = .upload(nil, nil)
|
||||
|
||||
if let uploadable = uploadable {
|
||||
uploadTask = .upload(uploadable, nil)
|
||||
}
|
||||
|
||||
let underlyingError = error.underlyingAdaptError ?? error
|
||||
let upload = UploadRequest(session: session, requestTask: uploadTask, error: underlyingError)
|
||||
|
||||
if let retrier = retrier, error is AdaptError {
|
||||
allowRetrier(retrier, toRetry: upload, with: underlyingError)
|
||||
} else {
|
||||
if startRequestsImmediately { upload.resume() }
|
||||
}
|
||||
|
||||
return upload
|
||||
}
|
||||
|
||||
#if !os(watchOS)
|
||||
|
||||
// MARK: - Stream Request
|
||||
|
||||
// MARK: Hostname and Port
|
||||
|
||||
/// Creates a `StreamRequest` for bidirectional streaming using the `hostname` and `port`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter hostName: The hostname of the server to connect to.
|
||||
/// - parameter port: The port of the server to connect to.
|
||||
///
|
||||
/// - returns: The created `StreamRequest`.
|
||||
@discardableResult
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
open func stream(withHostName hostName: String, port: Int) -> StreamRequest {
|
||||
return stream(.stream(hostName: hostName, port: port))
|
||||
}
|
||||
|
||||
// MARK: NetService
|
||||
|
||||
/// Creates a `StreamRequest` for bidirectional streaming using the `netService`.
|
||||
///
|
||||
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
|
||||
///
|
||||
/// - parameter netService: The net service used to identify the endpoint.
|
||||
///
|
||||
/// - returns: The created `StreamRequest`.
|
||||
@discardableResult
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
open func stream(with netService: NetService) -> StreamRequest {
|
||||
return stream(.netService(netService))
|
||||
}
|
||||
|
||||
// MARK: Private - Stream Implementation
|
||||
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
private func stream(_ streamable: StreamRequest.Streamable) -> StreamRequest {
|
||||
do {
|
||||
let task = try streamable.task(session: session, adapter: adapter, queue: queue)
|
||||
let request = StreamRequest(session: session, requestTask: .stream(streamable, task))
|
||||
|
||||
delegate[task] = request
|
||||
|
||||
if startRequestsImmediately { request.resume() }
|
||||
|
||||
return request
|
||||
} catch {
|
||||
return stream(failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
|
||||
private func stream(failedWith error: Error) -> StreamRequest {
|
||||
let stream = StreamRequest(session: session, requestTask: .stream(nil, nil), error: error)
|
||||
if startRequestsImmediately { stream.resume() }
|
||||
return stream
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// MARK: - Internal - Retry Request
|
||||
|
||||
func retry(_ request: Request) -> Bool {
|
||||
guard let originalTask = request.originalTask else { return false }
|
||||
|
||||
do {
|
||||
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
|
||||
|
||||
if let originalTask = request.task {
|
||||
delegate[originalTask] = nil // removes the old request to avoid endless growth
|
||||
}
|
||||
|
||||
request.delegate.task = task // resets all task delegate data
|
||||
|
||||
request.retryCount += 1
|
||||
request.startTime = CFAbsoluteTimeGetCurrent()
|
||||
request.endTime = nil
|
||||
|
||||
task.resume()
|
||||
|
||||
return true
|
||||
} catch {
|
||||
request.delegate.error = error.underlyingAdaptError ?? error
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private func allowRetrier(_ retrier: RequestRetrier, toRetry request: Request, with error: Error) {
|
||||
DispatchQueue.utility.async { [weak self] in
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
retrier.should(strongSelf, retry: request, with: error) { shouldRetry, timeDelay in
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
guard shouldRetry else {
|
||||
if strongSelf.startRequestsImmediately { request.resume() }
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.utility.after(timeDelay) {
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
let retrySucceeded = strongSelf.retry(request)
|
||||
|
||||
if retrySucceeded, let task = request.task {
|
||||
strongSelf.delegate[task] = request
|
||||
} else {
|
||||
if strongSelf.startRequestsImmediately { request.resume() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
466
Pods/Alamofire/Source/TaskDelegate.swift
generated
Normal file
466
Pods/Alamofire/Source/TaskDelegate.swift
generated
Normal file
@@ -0,0 +1,466 @@
|
||||
//
|
||||
// TaskDelegate.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
|
||||
|
||||
/// The task delegate is responsible for handling all delegate callbacks for the underlying task as well as
|
||||
/// executing all operations attached to the serial operation queue upon task completion.
|
||||
open class TaskDelegate: NSObject {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// The serial operation queue used to execute all operations after the task completes.
|
||||
public let queue: OperationQueue
|
||||
|
||||
/// The data returned by the server.
|
||||
public var data: Data? { return nil }
|
||||
|
||||
/// The error generated throughout the lifecyle of the task.
|
||||
public var error: Error?
|
||||
|
||||
var task: URLSessionTask? {
|
||||
set {
|
||||
taskLock.lock(); defer { taskLock.unlock() }
|
||||
_task = newValue
|
||||
}
|
||||
get {
|
||||
taskLock.lock(); defer { taskLock.unlock() }
|
||||
return _task
|
||||
}
|
||||
}
|
||||
|
||||
var initialResponseTime: CFAbsoluteTime?
|
||||
var credential: URLCredential?
|
||||
var metrics: AnyObject? // URLSessionTaskMetrics
|
||||
|
||||
private var _task: URLSessionTask? {
|
||||
didSet { reset() }
|
||||
}
|
||||
|
||||
private let taskLock = NSLock()
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
init(task: URLSessionTask?) {
|
||||
_task = task
|
||||
|
||||
self.queue = {
|
||||
let operationQueue = OperationQueue()
|
||||
|
||||
operationQueue.maxConcurrentOperationCount = 1
|
||||
operationQueue.isSuspended = true
|
||||
operationQueue.qualityOfService = .utility
|
||||
|
||||
return operationQueue
|
||||
}()
|
||||
}
|
||||
|
||||
func reset() {
|
||||
error = nil
|
||||
initialResponseTime = nil
|
||||
}
|
||||
|
||||
// MARK: URLSessionTaskDelegate
|
||||
|
||||
var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
|
||||
var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
|
||||
var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
|
||||
var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)?
|
||||
|
||||
@objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)
|
||||
func urlSession(
|
||||
_ session: URLSession,
|
||||
task: URLSessionTask,
|
||||
willPerformHTTPRedirection response: HTTPURLResponse,
|
||||
newRequest request: URLRequest,
|
||||
completionHandler: @escaping (URLRequest?) -> Void)
|
||||
{
|
||||
var redirectRequest: URLRequest? = request
|
||||
|
||||
if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
|
||||
redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
|
||||
}
|
||||
|
||||
completionHandler(redirectRequest)
|
||||
}
|
||||
|
||||
@objc(URLSession:task:didReceiveChallenge:completionHandler:)
|
||||
func urlSession(
|
||||
_ session: URLSession,
|
||||
task: URLSessionTask,
|
||||
didReceive challenge: URLAuthenticationChallenge,
|
||||
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
|
||||
{
|
||||
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
|
||||
var credential: URLCredential?
|
||||
|
||||
if let taskDidReceiveChallenge = taskDidReceiveChallenge {
|
||||
(disposition, credential) = taskDidReceiveChallenge(session, task, challenge)
|
||||
} else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
|
||||
let host = challenge.protectionSpace.host
|
||||
|
||||
if
|
||||
let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
|
||||
let serverTrust = challenge.protectionSpace.serverTrust
|
||||
{
|
||||
if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
|
||||
disposition = .useCredential
|
||||
credential = URLCredential(trust: serverTrust)
|
||||
} else {
|
||||
disposition = .cancelAuthenticationChallenge
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if challenge.previousFailureCount > 0 {
|
||||
disposition = .rejectProtectionSpace
|
||||
} else {
|
||||
credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
|
||||
|
||||
if credential != nil {
|
||||
disposition = .useCredential
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler(disposition, credential)
|
||||
}
|
||||
|
||||
@objc(URLSession:task:needNewBodyStream:)
|
||||
func urlSession(
|
||||
_ session: URLSession,
|
||||
task: URLSessionTask,
|
||||
needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
|
||||
{
|
||||
var bodyStream: InputStream?
|
||||
|
||||
if let taskNeedNewBodyStream = taskNeedNewBodyStream {
|
||||
bodyStream = taskNeedNewBodyStream(session, task)
|
||||
}
|
||||
|
||||
completionHandler(bodyStream)
|
||||
}
|
||||
|
||||
@objc(URLSession:task:didCompleteWithError:)
|
||||
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
||||
if let taskDidCompleteWithError = taskDidCompleteWithError {
|
||||
taskDidCompleteWithError(session, task, error)
|
||||
} else {
|
||||
if let error = error {
|
||||
if self.error == nil { self.error = error }
|
||||
|
||||
if
|
||||
let downloadDelegate = self as? DownloadTaskDelegate,
|
||||
let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
|
||||
{
|
||||
downloadDelegate.resumeData = resumeData
|
||||
}
|
||||
}
|
||||
|
||||
queue.isSuspended = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
var dataTask: URLSessionDataTask { return task as! URLSessionDataTask }
|
||||
|
||||
override var data: Data? {
|
||||
if dataStream != nil {
|
||||
return nil
|
||||
} else {
|
||||
return mutableData
|
||||
}
|
||||
}
|
||||
|
||||
var progress: Progress
|
||||
var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
|
||||
|
||||
var dataStream: ((_ data: Data) -> Void)?
|
||||
|
||||
private var totalBytesReceived: Int64 = 0
|
||||
private var mutableData: Data
|
||||
|
||||
private var expectedContentLength: Int64?
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
override init(task: URLSessionTask?) {
|
||||
mutableData = Data()
|
||||
progress = Progress(totalUnitCount: 0)
|
||||
|
||||
super.init(task: task)
|
||||
}
|
||||
|
||||
override func reset() {
|
||||
super.reset()
|
||||
|
||||
progress = Progress(totalUnitCount: 0)
|
||||
totalBytesReceived = 0
|
||||
mutableData = Data()
|
||||
expectedContentLength = nil
|
||||
}
|
||||
|
||||
// MARK: URLSessionDataDelegate
|
||||
|
||||
var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
|
||||
var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
|
||||
var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
|
||||
var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
|
||||
|
||||
func urlSession(
|
||||
_ session: URLSession,
|
||||
dataTask: URLSessionDataTask,
|
||||
didReceive response: URLResponse,
|
||||
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
|
||||
{
|
||||
var disposition: URLSession.ResponseDisposition = .allow
|
||||
|
||||
expectedContentLength = response.expectedContentLength
|
||||
|
||||
if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
|
||||
disposition = dataTaskDidReceiveResponse(session, dataTask, response)
|
||||
}
|
||||
|
||||
completionHandler(disposition)
|
||||
}
|
||||
|
||||
func urlSession(
|
||||
_ session: URLSession,
|
||||
dataTask: URLSessionDataTask,
|
||||
didBecome downloadTask: URLSessionDownloadTask)
|
||||
{
|
||||
dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask)
|
||||
}
|
||||
|
||||
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
|
||||
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
|
||||
|
||||
if let dataTaskDidReceiveData = dataTaskDidReceiveData {
|
||||
dataTaskDidReceiveData(session, dataTask, data)
|
||||
} else {
|
||||
if let dataStream = dataStream {
|
||||
dataStream(data)
|
||||
} else {
|
||||
mutableData.append(data)
|
||||
}
|
||||
|
||||
let bytesReceived = Int64(data.count)
|
||||
totalBytesReceived += bytesReceived
|
||||
let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
|
||||
|
||||
progress.totalUnitCount = totalBytesExpected
|
||||
progress.completedUnitCount = totalBytesReceived
|
||||
|
||||
if let progressHandler = progressHandler {
|
||||
progressHandler.queue.async { progressHandler.closure(self.progress) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func urlSession(
|
||||
_ session: URLSession,
|
||||
dataTask: URLSessionDataTask,
|
||||
willCacheResponse proposedResponse: CachedURLResponse,
|
||||
completionHandler: @escaping (CachedURLResponse?) -> Void)
|
||||
{
|
||||
var cachedResponse: CachedURLResponse? = proposedResponse
|
||||
|
||||
if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
|
||||
cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse)
|
||||
}
|
||||
|
||||
completionHandler(cachedResponse)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask }
|
||||
|
||||
var progress: Progress
|
||||
var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
|
||||
|
||||
var resumeData: Data?
|
||||
override var data: Data? { return resumeData }
|
||||
|
||||
var destination: DownloadRequest.DownloadFileDestination?
|
||||
|
||||
var temporaryURL: URL?
|
||||
var destinationURL: URL?
|
||||
|
||||
var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL }
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
override init(task: URLSessionTask?) {
|
||||
progress = Progress(totalUnitCount: 0)
|
||||
super.init(task: task)
|
||||
}
|
||||
|
||||
override func reset() {
|
||||
super.reset()
|
||||
|
||||
progress = Progress(totalUnitCount: 0)
|
||||
resumeData = nil
|
||||
}
|
||||
|
||||
// MARK: URLSessionDownloadDelegate
|
||||
|
||||
var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)?
|
||||
var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
|
||||
var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?
|
||||
|
||||
func urlSession(
|
||||
_ session: URLSession,
|
||||
downloadTask: URLSessionDownloadTask,
|
||||
didFinishDownloadingTo location: URL)
|
||||
{
|
||||
temporaryURL = location
|
||||
|
||||
guard
|
||||
let destination = destination,
|
||||
let response = downloadTask.response as? HTTPURLResponse
|
||||
else { return }
|
||||
|
||||
let result = destination(location, response)
|
||||
let destinationURL = result.destinationURL
|
||||
let options = result.options
|
||||
|
||||
self.destinationURL = destinationURL
|
||||
|
||||
do {
|
||||
if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) {
|
||||
try FileManager.default.removeItem(at: destinationURL)
|
||||
}
|
||||
|
||||
if options.contains(.createIntermediateDirectories) {
|
||||
let directory = destinationURL.deletingLastPathComponent()
|
||||
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
||||
}
|
||||
|
||||
try FileManager.default.moveItem(at: location, to: destinationURL)
|
||||
} catch {
|
||||
self.error = error
|
||||
}
|
||||
}
|
||||
|
||||
func urlSession(
|
||||
_ session: URLSession,
|
||||
downloadTask: URLSessionDownloadTask,
|
||||
didWriteData bytesWritten: Int64,
|
||||
totalBytesWritten: Int64,
|
||||
totalBytesExpectedToWrite: Int64)
|
||||
{
|
||||
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
|
||||
|
||||
if let downloadTaskDidWriteData = downloadTaskDidWriteData {
|
||||
downloadTaskDidWriteData(
|
||||
session,
|
||||
downloadTask,
|
||||
bytesWritten,
|
||||
totalBytesWritten,
|
||||
totalBytesExpectedToWrite
|
||||
)
|
||||
} else {
|
||||
progress.totalUnitCount = totalBytesExpectedToWrite
|
||||
progress.completedUnitCount = totalBytesWritten
|
||||
|
||||
if let progressHandler = progressHandler {
|
||||
progressHandler.queue.async { progressHandler.closure(self.progress) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func urlSession(
|
||||
_ session: URLSession,
|
||||
downloadTask: URLSessionDownloadTask,
|
||||
didResumeAtOffset fileOffset: Int64,
|
||||
expectedTotalBytes: Int64)
|
||||
{
|
||||
if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
|
||||
downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
|
||||
} else {
|
||||
progress.totalUnitCount = expectedTotalBytes
|
||||
progress.completedUnitCount = fileOffset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
class UploadTaskDelegate: DataTaskDelegate {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask }
|
||||
|
||||
var uploadProgress: Progress
|
||||
var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
|
||||
|
||||
// MARK: Lifecycle
|
||||
|
||||
override init(task: URLSessionTask?) {
|
||||
uploadProgress = Progress(totalUnitCount: 0)
|
||||
super.init(task: task)
|
||||
}
|
||||
|
||||
override func reset() {
|
||||
super.reset()
|
||||
uploadProgress = Progress(totalUnitCount: 0)
|
||||
}
|
||||
|
||||
// MARK: URLSessionTaskDelegate
|
||||
|
||||
var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?
|
||||
|
||||
func URLSession(
|
||||
_ session: URLSession,
|
||||
task: URLSessionTask,
|
||||
didSendBodyData bytesSent: Int64,
|
||||
totalBytesSent: Int64,
|
||||
totalBytesExpectedToSend: Int64)
|
||||
{
|
||||
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
|
||||
|
||||
if let taskDidSendBodyData = taskDidSendBodyData {
|
||||
taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
|
||||
} else {
|
||||
uploadProgress.totalUnitCount = totalBytesExpectedToSend
|
||||
uploadProgress.completedUnitCount = totalBytesSent
|
||||
|
||||
if let uploadProgressHandler = uploadProgressHandler {
|
||||
uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
136
Pods/Alamofire/Source/Timeline.swift
generated
Normal file
136
Pods/Alamofire/Source/Timeline.swift
generated
Normal file
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// Timeline.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
|
||||
|
||||
/// Responsible for computing the timing metrics for the complete lifecycle of a `Request`.
|
||||
public struct Timeline {
|
||||
/// The time the request was initialized.
|
||||
public let requestStartTime: CFAbsoluteTime
|
||||
|
||||
/// The time the first bytes were received from or sent to the server.
|
||||
public let initialResponseTime: CFAbsoluteTime
|
||||
|
||||
/// The time when the request was completed.
|
||||
public let requestCompletedTime: CFAbsoluteTime
|
||||
|
||||
/// The time when the response serialization was completed.
|
||||
public let serializationCompletedTime: CFAbsoluteTime
|
||||
|
||||
/// The time interval in seconds from the time the request started to the initial response from the server.
|
||||
public let latency: TimeInterval
|
||||
|
||||
/// The time interval in seconds from the time the request started to the time the request completed.
|
||||
public let requestDuration: TimeInterval
|
||||
|
||||
/// The time interval in seconds from the time the request completed to the time response serialization completed.
|
||||
public let serializationDuration: TimeInterval
|
||||
|
||||
/// The time interval in seconds from the time the request started to the time response serialization completed.
|
||||
public let totalDuration: TimeInterval
|
||||
|
||||
/// Creates a new `Timeline` instance with the specified request times.
|
||||
///
|
||||
/// - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`.
|
||||
/// - parameter initialResponseTime: The time the first bytes were received from or sent to the server.
|
||||
/// Defaults to `0.0`.
|
||||
/// - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`.
|
||||
/// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults
|
||||
/// to `0.0`.
|
||||
///
|
||||
/// - returns: The new `Timeline` instance.
|
||||
public init(
|
||||
requestStartTime: CFAbsoluteTime = 0.0,
|
||||
initialResponseTime: CFAbsoluteTime = 0.0,
|
||||
requestCompletedTime: CFAbsoluteTime = 0.0,
|
||||
serializationCompletedTime: CFAbsoluteTime = 0.0)
|
||||
{
|
||||
self.requestStartTime = requestStartTime
|
||||
self.initialResponseTime = initialResponseTime
|
||||
self.requestCompletedTime = requestCompletedTime
|
||||
self.serializationCompletedTime = serializationCompletedTime
|
||||
|
||||
self.latency = initialResponseTime - requestStartTime
|
||||
self.requestDuration = requestCompletedTime - requestStartTime
|
||||
self.serializationDuration = serializationCompletedTime - requestCompletedTime
|
||||
self.totalDuration = serializationCompletedTime - requestStartTime
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CustomStringConvertible
|
||||
|
||||
extension Timeline: CustomStringConvertible {
|
||||
/// The textual representation used when written to an output stream, which includes the latency, the request
|
||||
/// duration and the total duration.
|
||||
public var description: String {
|
||||
let latency = String(format: "%.3f", self.latency)
|
||||
let requestDuration = String(format: "%.3f", self.requestDuration)
|
||||
let serializationDuration = String(format: "%.3f", self.serializationDuration)
|
||||
let totalDuration = String(format: "%.3f", self.totalDuration)
|
||||
|
||||
// NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
|
||||
// fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
|
||||
let timings = [
|
||||
"\"Latency\": " + latency + " secs",
|
||||
"\"Request Duration\": " + requestDuration + " secs",
|
||||
"\"Serialization Duration\": " + serializationDuration + " secs",
|
||||
"\"Total Duration\": " + totalDuration + " secs"
|
||||
]
|
||||
|
||||
return "Timeline: { " + timings.joined(separator: ", ") + " }"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CustomDebugStringConvertible
|
||||
|
||||
extension Timeline: CustomDebugStringConvertible {
|
||||
/// The textual representation used when written to an output stream, which includes the request start time, the
|
||||
/// initial response time, the request completed time, the serialization completed time, the latency, the request
|
||||
/// duration and the total duration.
|
||||
public var debugDescription: String {
|
||||
let requestStartTime = String(format: "%.3f", self.requestStartTime)
|
||||
let initialResponseTime = String(format: "%.3f", self.initialResponseTime)
|
||||
let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime)
|
||||
let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime)
|
||||
let latency = String(format: "%.3f", self.latency)
|
||||
let requestDuration = String(format: "%.3f", self.requestDuration)
|
||||
let serializationDuration = String(format: "%.3f", self.serializationDuration)
|
||||
let totalDuration = String(format: "%.3f", self.totalDuration)
|
||||
|
||||
// NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
|
||||
// fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
|
||||
let timings = [
|
||||
"\"Request Start Time\": " + requestStartTime,
|
||||
"\"Initial Response Time\": " + initialResponseTime,
|
||||
"\"Request Completed Time\": " + requestCompletedTime,
|
||||
"\"Serialization Completed Time\": " + serializationCompletedTime,
|
||||
"\"Latency\": " + latency + " secs",
|
||||
"\"Request Duration\": " + requestDuration + " secs",
|
||||
"\"Serialization Duration\": " + serializationDuration + " secs",
|
||||
"\"Total Duration\": " + totalDuration + " secs"
|
||||
]
|
||||
|
||||
return "Timeline: { " + timings.joined(separator: ", ") + " }"
|
||||
}
|
||||
}
|
||||
315
Pods/Alamofire/Source/Validation.swift
generated
Normal file
315
Pods/Alamofire/Source/Validation.swift
generated
Normal file
@@ -0,0 +1,315 @@
|
||||
//
|
||||
// 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 validation was successful or encountered an error resulting in a failure.
|
||||
///
|
||||
/// - success: The validation was successful.
|
||||
/// - failure: The validation failed encountering the provided error.
|
||||
public enum ValidationResult {
|
||||
case success
|
||||
case failure(Error)
|
||||
}
|
||||
|
||||
fileprivate struct MIMEType {
|
||||
let type: String
|
||||
let subtype: String
|
||||
|
||||
var isWildcard: Bool { return type == "*" && subtype == "*" }
|
||||
|
||||
init?(_ string: String) {
|
||||
let components: [String] = {
|
||||
let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
#if swift(>=3.2)
|
||||
let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)]
|
||||
#else
|
||||
let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)
|
||||
#endif
|
||||
|
||||
return split.components(separatedBy: "/")
|
||||
}()
|
||||
|
||||
if let type = components.first, let subtype = components.last {
|
||||
self.type = type
|
||||
self.subtype = subtype
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func matches(_ mime: MIMEType) -> Bool {
|
||||
switch (type, subtype) {
|
||||
case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
fileprivate var acceptableStatusCodes: [Int] { return Array(200..<300) }
|
||||
|
||||
fileprivate var acceptableContentTypes: [String] {
|
||||
if let accept = request?.value(forHTTPHeaderField: "Accept") {
|
||||
return accept.components(separatedBy: ",")
|
||||
}
|
||||
|
||||
return ["*/*"]
|
||||
}
|
||||
|
||||
// MARK: Status Code
|
||||
|
||||
fileprivate func validate<S: Sequence>(
|
||||
statusCode acceptableStatusCodes: S,
|
||||
response: HTTPURLResponse)
|
||||
-> ValidationResult
|
||||
where S.Iterator.Element == Int
|
||||
{
|
||||
if acceptableStatusCodes.contains(response.statusCode) {
|
||||
return .success
|
||||
} else {
|
||||
let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode)
|
||||
return .failure(AFError.responseValidationFailed(reason: reason))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Content Type
|
||||
|
||||
fileprivate func validate<S: Sequence>(
|
||||
contentType acceptableContentTypes: S,
|
||||
response: HTTPURLResponse,
|
||||
data: Data?)
|
||||
-> ValidationResult
|
||||
where S.Iterator.Element == String
|
||||
{
|
||||
guard let data = data, data.count > 0 else { return .success }
|
||||
|
||||
guard
|
||||
let responseContentType = response.mimeType,
|
||||
let responseMIMEType = MIMEType(responseContentType)
|
||||
else {
|
||||
for contentType in acceptableContentTypes {
|
||||
if let mimeType = MIMEType(contentType), mimeType.isWildcard {
|
||||
return .success
|
||||
}
|
||||
}
|
||||
|
||||
let error: AFError = {
|
||||
let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes))
|
||||
return AFError.responseValidationFailed(reason: reason)
|
||||
}()
|
||||
|
||||
return .failure(error)
|
||||
}
|
||||
|
||||
for contentType in acceptableContentTypes {
|
||||
if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) {
|
||||
return .success
|
||||
}
|
||||
}
|
||||
|
||||
let error: AFError = {
|
||||
let reason: ErrorReason = .unacceptableContentType(
|
||||
acceptableContentTypes: Array(acceptableContentTypes),
|
||||
responseContentType: responseContentType
|
||||
)
|
||||
|
||||
return AFError.responseValidationFailed(reason: reason)
|
||||
}()
|
||||
|
||||
return .failure(error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension DataRequest {
|
||||
/// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the
|
||||
/// request was valid.
|
||||
public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult
|
||||
|
||||
/// Validates the request, using the specified closure.
|
||||
///
|
||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||
///
|
||||
/// - parameter validation: A closure to validate the request.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func validate(_ validation: @escaping Validation) -> Self {
|
||||
let validationExecution: () -> Void = { [unowned self] in
|
||||
if
|
||||
let response = self.response,
|
||||
self.delegate.error == nil,
|
||||
case let .failure(error) = validation(self.request, response, self.delegate.data)
|
||||
{
|
||||
self.delegate.error = error
|
||||
}
|
||||
}
|
||||
|
||||
validations.append(validationExecution)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
/// Validates that the response has a status code in the specified sequence.
|
||||
///
|
||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||
///
|
||||
/// - parameter range: The range of acceptable status codes.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
|
||||
return validate { [unowned self] _, response, _ in
|
||||
return self.validate(statusCode: acceptableStatusCodes, response: response)
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates that the response has a content type in the specified sequence.
|
||||
///
|
||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||
///
|
||||
/// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func validate<S: Sequence>(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String {
|
||||
return validate { [unowned self] _, response, data in
|
||||
return self.validate(contentType: acceptableContentTypes, response: response, data: data)
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
|
||||
/// type matches any specified in the Accept HTTP header field.
|
||||
///
|
||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func validate() -> Self {
|
||||
return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension DownloadRequest {
|
||||
/// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a
|
||||
/// destination URL, and returns whether the request was valid.
|
||||
public typealias Validation = (
|
||||
_ request: URLRequest?,
|
||||
_ response: HTTPURLResponse,
|
||||
_ temporaryURL: URL?,
|
||||
_ destinationURL: URL?)
|
||||
-> ValidationResult
|
||||
|
||||
/// Validates the request, using the specified closure.
|
||||
///
|
||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||
///
|
||||
/// - parameter validation: A closure to validate the request.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func validate(_ validation: @escaping Validation) -> Self {
|
||||
let validationExecution: () -> Void = { [unowned self] in
|
||||
let request = self.request
|
||||
let temporaryURL = self.downloadDelegate.temporaryURL
|
||||
let destinationURL = self.downloadDelegate.destinationURL
|
||||
|
||||
if
|
||||
let response = self.response,
|
||||
self.delegate.error == nil,
|
||||
case let .failure(error) = validation(request, response, temporaryURL, destinationURL)
|
||||
{
|
||||
self.delegate.error = error
|
||||
}
|
||||
}
|
||||
|
||||
validations.append(validationExecution)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
/// Validates that the response has a status code in the specified sequence.
|
||||
///
|
||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||
///
|
||||
/// - parameter range: The range of acceptable status codes.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func validate<S: Sequence>(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int {
|
||||
return validate { [unowned self] _, response, _, _ in
|
||||
return self.validate(statusCode: acceptableStatusCodes, response: response)
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates that the response has a content type in the specified sequence.
|
||||
///
|
||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||
///
|
||||
/// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func validate<S: Sequence>(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String {
|
||||
return validate { [unowned self] _, response, _, _ in
|
||||
let fileURL = self.downloadDelegate.fileURL
|
||||
|
||||
guard let validFileURL = fileURL else {
|
||||
return .failure(AFError.responseValidationFailed(reason: .dataFileNil))
|
||||
}
|
||||
|
||||
do {
|
||||
let data = try Data(contentsOf: validFileURL)
|
||||
return self.validate(contentType: acceptableContentTypes, response: response, data: data)
|
||||
} catch {
|
||||
return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates that the response has a status code in the default acceptable range of 200...299, and that the content
|
||||
/// type matches any specified in the Accept HTTP header field.
|
||||
///
|
||||
/// If validation fails, subsequent calls to response handlers will have an associated error.
|
||||
///
|
||||
/// - returns: The request.
|
||||
@discardableResult
|
||||
public func validate() -> Self {
|
||||
return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes)
|
||||
}
|
||||
}
|
||||
16
Pods/Manifest.lock
generated
Normal file
16
Pods/Manifest.lock
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
PODS:
|
||||
- Alamofire (4.7.3)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Alamofire (~> 4.7)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- Alamofire
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: c7287b6e5d7da964a70935e5db17046b7fde6568
|
||||
|
||||
PODFILE CHECKSUM: 2287d4bb366efcfb07dc93dc27fa82c8712e6c50
|
||||
|
||||
COCOAPODS: 1.5.3
|
||||
634
Pods/Pods.xcodeproj/project.pbxproj
generated
Normal file
634
Pods/Pods.xcodeproj/project.pbxproj
generated
Normal file
@@ -0,0 +1,634 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
10EB23E9ECC4B33E16933BB1EA560B6A /* Timeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87882A1F5A92C8138D54545E51D51E6F /* Timeline.swift */; };
|
||||
1B9EDEDC964E6B08F78920B4F4B9DB84 /* Alamofire-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 03DECE84DC850B04690604AB7C521277 /* Alamofire-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
2DE163E269CC32CA11778891E86BBFD1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A16F4CFC63FAC439D7A04994F579A03 /* Foundation.framework */; };
|
||||
3626B94094672CB1C9DEA32B9F9502E1 /* TaskDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A01C037B4034EDA3D7955BC5E4E9D9D6 /* TaskDelegate.swift */; };
|
||||
415197F86BCB7175EAC7C482C63B12B2 /* Pods-easyCC-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 69C3B74E466D632B44B0D768DA873FF3 /* Pods-easyCC-dummy.m */; };
|
||||
5387216E723A3C68E851CA15573CDD71 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E230A0448B394DE26E688DAC8E6201E /* Request.swift */; };
|
||||
61200D01A1855D7920CEF835C8BE00B0 /* DispatchQueue+Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FCBF1EED873F61C6D46CE37FA5C39D3 /* DispatchQueue+Alamofire.swift */; };
|
||||
62F65AD8DC4F0F9610F4B8B4738EC094 /* ServerTrustPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B030D27CAC730C5EB0F22390645310 /* ServerTrustPolicy.swift */; };
|
||||
7B5FE28C7EA4122B0598738E54DBEBD8 /* SessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D73DD9EF275A3C56569E2B1CA8026 /* SessionDelegate.swift */; };
|
||||
7D8CC01E8C9EFFF9F4D65406CDE0AB66 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D60BC9955B4F7FFA62D7440CB385C11 /* Result.swift */; };
|
||||
834F4D9819C6010E611A7E151B8899C3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A16F4CFC63FAC439D7A04994F579A03 /* Foundation.framework */; };
|
||||
9ED2BB2981896E0A39EFA365503F58CE /* AFError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AF006B0AD5765D1BFA8253C2DCBB126 /* AFError.swift */; };
|
||||
A2A6F71B727312BD45CC7A4AAD7B0AB7 /* NetworkReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A8AA5F9EDED0A0BDDE7E830BF4AEE0 /* NetworkReachabilityManager.swift */; };
|
||||
A9EEEA7477981DEEBC72432DE9990A4B /* Alamofire-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D1F50F37DFB6DC0B7B9D8C373E5A3EAE /* Alamofire-dummy.m */; };
|
||||
AE1EF48399533730D0066E04B22CA2D6 /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46CDAC6C1187C5467E576980E1062C8B /* SessionManager.swift */; };
|
||||
B65FCF589DA398C3EFE0128064E510EC /* MultipartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 155538D91ACEEEDF82069ACF6C1A02E7 /* MultipartFormData.swift */; };
|
||||
BBEFE2F9CEB73DC7BD97FFA66A0D9D4F /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B029DBC43E49A740F12B5E4D2E6DD452 /* Validation.swift */; };
|
||||
BDE528DF3B2D00C4BE974BFD0F480B10 /* Pods-easyCC-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 307F428E880901B6C5AA56FDBB67D741 /* Pods-easyCC-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
BE5C67A07E289FE1F9BE27335B159997 /* ParameterEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6639346628280A0D0FAD35196BF56108 /* ParameterEncoding.swift */; };
|
||||
CB6D60925223897FFA2662667DF83E8A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F47F5C9CDB035C5AFADEBA5BF44F1C /* Response.swift */; };
|
||||
EFD264FC408EBF3BA2528E70B08DDD94 /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A46F517F0AF7E85A16D723F6406896 /* Notifications.swift */; };
|
||||
F6BECD98B97CBFEBE2C96F0E9E72A6C0 /* ResponseSerialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2F9510473F6FFD7AA66524DB16C2263 /* ResponseSerialization.swift */; };
|
||||
F8B3D3092ED0417E8CDF32033F6122F5 /* Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFCB8C44DE758E906C0BCDA455937B85 /* Alamofire.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
18A61F94D10EC85C88C3700A22003785 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 88E9EC28B8B46C3631E6B242B50F4442;
|
||||
remoteInfo = Alamofire;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
03DECE84DC850B04690604AB7C521277 /* Alamofire-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-umbrella.h"; sourceTree = "<group>"; };
|
||||
04F47F5C9CDB035C5AFADEBA5BF44F1C /* Response.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Source/Response.swift; sourceTree = "<group>"; };
|
||||
0FB20BB094E29733394B0B355418AF4D /* Pods-easyCC-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-easyCC-frameworks.sh"; sourceTree = "<group>"; };
|
||||
0FCBF1EED873F61C6D46CE37FA5C39D3 /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Alamofire.swift"; path = "Source/DispatchQueue+Alamofire.swift"; sourceTree = "<group>"; };
|
||||
117EFB31D9AD9673BAF51B48596F19E2 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
155538D91ACEEEDF82069ACF6C1A02E7 /* MultipartFormData.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartFormData.swift; path = Source/MultipartFormData.swift; sourceTree = "<group>"; };
|
||||
17E1C157C1CA0BB1070DEFCFDC481040 /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Alamofire.framework; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
195D73DD9EF275A3C56569E2B1CA8026 /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Source/SessionDelegate.swift; sourceTree = "<group>"; };
|
||||
1E230A0448B394DE26E688DAC8E6201E /* Request.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Request.swift; path = Source/Request.swift; sourceTree = "<group>"; };
|
||||
210971763CB2FC0DC4E378271A37BE32 /* Alamofire.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Alamofire.modulemap; sourceTree = "<group>"; };
|
||||
307F428E880901B6C5AA56FDBB67D741 /* Pods-easyCC-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-easyCC-umbrella.h"; sourceTree = "<group>"; };
|
||||
32B030D27CAC730C5EB0F22390645310 /* ServerTrustPolicy.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServerTrustPolicy.swift; path = Source/ServerTrustPolicy.swift; sourceTree = "<group>"; };
|
||||
33A04B5D27E86AF4B84D95E21CF3F452 /* Alamofire.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.xcconfig; sourceTree = "<group>"; };
|
||||
3D60BC9955B4F7FFA62D7440CB385C11 /* Result.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Result.swift; path = Source/Result.swift; sourceTree = "<group>"; };
|
||||
46CDAC6C1187C5467E576980E1062C8B /* SessionManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionManager.swift; path = Source/SessionManager.swift; sourceTree = "<group>"; };
|
||||
4AF006B0AD5765D1BFA8253C2DCBB126 /* AFError.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AFError.swift; path = Source/AFError.swift; sourceTree = "<group>"; };
|
||||
5A16F4CFC63FAC439D7A04994F579A03 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
|
||||
61D920D6E48023BCBF18CD83450D05F5 /* Alamofire-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Alamofire-prefix.pch"; sourceTree = "<group>"; };
|
||||
6639346628280A0D0FAD35196BF56108 /* ParameterEncoding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoding.swift; path = Source/ParameterEncoding.swift; sourceTree = "<group>"; };
|
||||
66A46F517F0AF7E85A16D723F6406896 /* Notifications.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Notifications.swift; path = Source/Notifications.swift; sourceTree = "<group>"; };
|
||||
66F9BA24143329BDA000B01D286C460D /* Pods-easyCC.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-easyCC.modulemap"; sourceTree = "<group>"; };
|
||||
69C3B74E466D632B44B0D768DA873FF3 /* Pods-easyCC-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-easyCC-dummy.m"; sourceTree = "<group>"; };
|
||||
6FCBA67D56B4ED03DAFF99388814715C /* Pods-easyCC-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-easyCC-acknowledgements.plist"; sourceTree = "<group>"; };
|
||||
792CB1EA48E75E6951273B20216D52FE /* Pods_easyCC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_easyCC.framework; path = "Pods-easyCC.framework"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
87882A1F5A92C8138D54545E51D51E6F /* Timeline.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Timeline.swift; path = Source/Timeline.swift; sourceTree = "<group>"; };
|
||||
93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
|
||||
9E35F59BD43A93AEC00477A5D8126DDB /* Pods-easyCC-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-easyCC-acknowledgements.markdown"; sourceTree = "<group>"; };
|
||||
A01C037B4034EDA3D7955BC5E4E9D9D6 /* TaskDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TaskDelegate.swift; path = Source/TaskDelegate.swift; sourceTree = "<group>"; };
|
||||
AF7E4764CB2AE072A698D167132E47DA /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
B029DBC43E49A740F12B5E4D2E6DD452 /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Validation.swift; sourceTree = "<group>"; };
|
||||
B9EE5FF9411960F70122A41A2AEB141B /* Pods-easyCC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-easyCC.release.xcconfig"; sourceTree = "<group>"; };
|
||||
BBF76BFD6D6BBEF9320B5135374C1EF8 /* Pods-easyCC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-easyCC.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
D1F50F37DFB6DC0B7B9D8C373E5A3EAE /* Alamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Alamofire-dummy.m"; sourceTree = "<group>"; };
|
||||
DFCB8C44DE758E906C0BCDA455937B85 /* Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Alamofire.swift; path = Source/Alamofire.swift; sourceTree = "<group>"; };
|
||||
E2F9510473F6FFD7AA66524DB16C2263 /* ResponseSerialization.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResponseSerialization.swift; path = Source/ResponseSerialization.swift; sourceTree = "<group>"; };
|
||||
E5A8AA5F9EDED0A0BDDE7E830BF4AEE0 /* NetworkReachabilityManager.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = NetworkReachabilityManager.swift; path = Source/NetworkReachabilityManager.swift; sourceTree = "<group>"; };
|
||||
E8369483E17A0841271944E904F6960C /* Pods-easyCC-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-easyCC-resources.sh"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
7E25686B05D57671AE2F0EB916772632 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2DE163E269CC32CA11778891E86BBFD1 /* Foundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
99195E4207764744AEC07ECCBCD550EB /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
834F4D9819C6010E611A7E151B8899C3 /* Foundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
200D10EB20F0397D47F022B50CF0433F /* Alamofire */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4AF006B0AD5765D1BFA8253C2DCBB126 /* AFError.swift */,
|
||||
DFCB8C44DE758E906C0BCDA455937B85 /* Alamofire.swift */,
|
||||
0FCBF1EED873F61C6D46CE37FA5C39D3 /* DispatchQueue+Alamofire.swift */,
|
||||
155538D91ACEEEDF82069ACF6C1A02E7 /* MultipartFormData.swift */,
|
||||
E5A8AA5F9EDED0A0BDDE7E830BF4AEE0 /* NetworkReachabilityManager.swift */,
|
||||
66A46F517F0AF7E85A16D723F6406896 /* Notifications.swift */,
|
||||
6639346628280A0D0FAD35196BF56108 /* ParameterEncoding.swift */,
|
||||
1E230A0448B394DE26E688DAC8E6201E /* Request.swift */,
|
||||
04F47F5C9CDB035C5AFADEBA5BF44F1C /* Response.swift */,
|
||||
E2F9510473F6FFD7AA66524DB16C2263 /* ResponseSerialization.swift */,
|
||||
3D60BC9955B4F7FFA62D7440CB385C11 /* Result.swift */,
|
||||
32B030D27CAC730C5EB0F22390645310 /* ServerTrustPolicy.swift */,
|
||||
195D73DD9EF275A3C56569E2B1CA8026 /* SessionDelegate.swift */,
|
||||
46CDAC6C1187C5467E576980E1062C8B /* SessionManager.swift */,
|
||||
A01C037B4034EDA3D7955BC5E4E9D9D6 /* TaskDelegate.swift */,
|
||||
87882A1F5A92C8138D54545E51D51E6F /* Timeline.swift */,
|
||||
B029DBC43E49A740F12B5E4D2E6DD452 /* Validation.swift */,
|
||||
2E5925946A4DE3B8F7E4137BACAD9618 /* Support Files */,
|
||||
);
|
||||
name = Alamofire;
|
||||
path = Alamofire;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2E5925946A4DE3B8F7E4137BACAD9618 /* Support Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
210971763CB2FC0DC4E378271A37BE32 /* Alamofire.modulemap */,
|
||||
33A04B5D27E86AF4B84D95E21CF3F452 /* Alamofire.xcconfig */,
|
||||
D1F50F37DFB6DC0B7B9D8C373E5A3EAE /* Alamofire-dummy.m */,
|
||||
61D920D6E48023BCBF18CD83450D05F5 /* Alamofire-prefix.pch */,
|
||||
03DECE84DC850B04690604AB7C521277 /* Alamofire-umbrella.h */,
|
||||
117EFB31D9AD9673BAF51B48596F19E2 /* Info.plist */,
|
||||
);
|
||||
name = "Support Files";
|
||||
path = "../Target Support Files/Alamofire";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
35F128EB69B6F7FB7DA93BBF6C130FAE /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
200D10EB20F0397D47F022B50CF0433F /* Alamofire */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
557D049BB8C29C3E34774D3B7C8A2D97 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
17E1C157C1CA0BB1070DEFCFDC481040 /* Alamofire.framework */,
|
||||
792CB1EA48E75E6951273B20216D52FE /* Pods_easyCC.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
55AC2BB4A01275D639242EFBD1377E09 /* Targets Support Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CB3825B51D37239C8A4617C1B2B76BF1 /* Pods-easyCC */,
|
||||
);
|
||||
name = "Targets Support Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5E0D919E635D23B70123790B8308F8EF /* iOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5A16F4CFC63FAC439D7A04994F579A03 /* Foundation.framework */,
|
||||
);
|
||||
name = iOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7DB346D0F39D3F0E887471402A8071AB = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */,
|
||||
BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */,
|
||||
35F128EB69B6F7FB7DA93BBF6C130FAE /* Pods */,
|
||||
557D049BB8C29C3E34774D3B7C8A2D97 /* Products */,
|
||||
55AC2BB4A01275D639242EFBD1377E09 /* Targets Support Files */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5E0D919E635D23B70123790B8308F8EF /* iOS */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CB3825B51D37239C8A4617C1B2B76BF1 /* Pods-easyCC */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AF7E4764CB2AE072A698D167132E47DA /* Info.plist */,
|
||||
66F9BA24143329BDA000B01D286C460D /* Pods-easyCC.modulemap */,
|
||||
9E35F59BD43A93AEC00477A5D8126DDB /* Pods-easyCC-acknowledgements.markdown */,
|
||||
6FCBA67D56B4ED03DAFF99388814715C /* Pods-easyCC-acknowledgements.plist */,
|
||||
69C3B74E466D632B44B0D768DA873FF3 /* Pods-easyCC-dummy.m */,
|
||||
0FB20BB094E29733394B0B355418AF4D /* Pods-easyCC-frameworks.sh */,
|
||||
E8369483E17A0841271944E904F6960C /* Pods-easyCC-resources.sh */,
|
||||
307F428E880901B6C5AA56FDBB67D741 /* Pods-easyCC-umbrella.h */,
|
||||
BBF76BFD6D6BBEF9320B5135374C1EF8 /* Pods-easyCC.debug.xcconfig */,
|
||||
B9EE5FF9411960F70122A41A2AEB141B /* Pods-easyCC.release.xcconfig */,
|
||||
);
|
||||
name = "Pods-easyCC";
|
||||
path = "Target Support Files/Pods-easyCC";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
0AD3EEDE043FAD2198A0767608CBDAD1 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BDE528DF3B2D00C4BE974BFD0F480B10 /* Pods-easyCC-umbrella.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
B4002B6E97835FDCCAA5963EFE09A3E0 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1B9EDEDC964E6B08F78920B4F4B9DB84 /* Alamofire-umbrella.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
790170DE2E174C423AD5E4A43A74F76E /* Pods-easyCC */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 91711423E8A5CF8B3C82785247824B35 /* Build configuration list for PBXNativeTarget "Pods-easyCC" */;
|
||||
buildPhases = (
|
||||
4EACBE0AF5DC573C88FDC4423D5F0239 /* Sources */,
|
||||
7E25686B05D57671AE2F0EB916772632 /* Frameworks */,
|
||||
0AD3EEDE043FAD2198A0767608CBDAD1 /* Headers */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
8CEC94C2FD2416BA0F555CA048B80F88 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "Pods-easyCC";
|
||||
productName = "Pods-easyCC";
|
||||
productReference = 792CB1EA48E75E6951273B20216D52FE /* Pods_easyCC.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
88E9EC28B8B46C3631E6B242B50F4442 /* Alamofire */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 419E5D95491847CD79841B971A8A3277 /* Build configuration list for PBXNativeTarget "Alamofire" */;
|
||||
buildPhases = (
|
||||
32B9974868188C4803318E36329C87FE /* Sources */,
|
||||
99195E4207764744AEC07ECCBCD550EB /* Frameworks */,
|
||||
B4002B6E97835FDCCAA5963EFE09A3E0 /* Headers */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Alamofire;
|
||||
productName = Alamofire;
|
||||
productReference = 17E1C157C1CA0BB1070DEFCFDC481040 /* Alamofire.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
D41D8CD98F00B204E9800998ECF8427E /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0930;
|
||||
LastUpgradeCheck = 0930;
|
||||
};
|
||||
buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 7DB346D0F39D3F0E887471402A8071AB;
|
||||
productRefGroup = 557D049BB8C29C3E34774D3B7C8A2D97 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
88E9EC28B8B46C3631E6B242B50F4442 /* Alamofire */,
|
||||
790170DE2E174C423AD5E4A43A74F76E /* Pods-easyCC */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
32B9974868188C4803318E36329C87FE /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9ED2BB2981896E0A39EFA365503F58CE /* AFError.swift in Sources */,
|
||||
A9EEEA7477981DEEBC72432DE9990A4B /* Alamofire-dummy.m in Sources */,
|
||||
F8B3D3092ED0417E8CDF32033F6122F5 /* Alamofire.swift in Sources */,
|
||||
61200D01A1855D7920CEF835C8BE00B0 /* DispatchQueue+Alamofire.swift in Sources */,
|
||||
B65FCF589DA398C3EFE0128064E510EC /* MultipartFormData.swift in Sources */,
|
||||
A2A6F71B727312BD45CC7A4AAD7B0AB7 /* NetworkReachabilityManager.swift in Sources */,
|
||||
EFD264FC408EBF3BA2528E70B08DDD94 /* Notifications.swift in Sources */,
|
||||
BE5C67A07E289FE1F9BE27335B159997 /* ParameterEncoding.swift in Sources */,
|
||||
5387216E723A3C68E851CA15573CDD71 /* Request.swift in Sources */,
|
||||
CB6D60925223897FFA2662667DF83E8A /* Response.swift in Sources */,
|
||||
F6BECD98B97CBFEBE2C96F0E9E72A6C0 /* ResponseSerialization.swift in Sources */,
|
||||
7D8CC01E8C9EFFF9F4D65406CDE0AB66 /* Result.swift in Sources */,
|
||||
62F65AD8DC4F0F9610F4B8B4738EC094 /* ServerTrustPolicy.swift in Sources */,
|
||||
7B5FE28C7EA4122B0598738E54DBEBD8 /* SessionDelegate.swift in Sources */,
|
||||
AE1EF48399533730D0066E04B22CA2D6 /* SessionManager.swift in Sources */,
|
||||
3626B94094672CB1C9DEA32B9F9502E1 /* TaskDelegate.swift in Sources */,
|
||||
10EB23E9ECC4B33E16933BB1EA560B6A /* Timeline.swift in Sources */,
|
||||
BBEFE2F9CEB73DC7BD97FFA66A0D9D4F /* Validation.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4EACBE0AF5DC573C88FDC4423D5F0239 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
415197F86BCB7175EAC7C482C63B12B2 /* Pods-easyCC-dummy.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
8CEC94C2FD2416BA0F555CA048B80F88 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = Alamofire;
|
||||
target = 88E9EC28B8B46C3631E6B242B50F4442 /* Alamofire */;
|
||||
targetProxy = 18A61F94D10EC85C88C3700A22003785 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
199D972A13F2B4C56847F7A89CCA83BC /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGNING_ALLOWED = NO;
|
||||
CODE_SIGNING_REQUIRED = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"POD_CONFIGURATION_DEBUG=1",
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRIP_INSTALLED_PRODUCT = NO;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SYMROOT = "${SRCROOT}/../build";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
5D6E1109C9897559A28F6EA4F0F7CCE7 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = BBF76BFD6D6BBEF9320B5135374C1EF8 /* Pods-easyCC.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "Target Support Files/Pods-easyCC/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MACH_O_TYPE = staticlib;
|
||||
MODULEMAP_FILE = "Target Support Files/Pods-easyCC/Pods-easyCC.modulemap";
|
||||
OTHER_LDFLAGS = "";
|
||||
OTHER_LIBTOOLFLAGS = "";
|
||||
PODS_ROOT = "$(SRCROOT)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7B0415700290D1DEBAB6B173951CC0C7 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33A04B5D27E86AF4B84D95E21CF3F452 /* Alamofire.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch";
|
||||
INFOPLIST_FILE = "Target Support Files/Alamofire/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap";
|
||||
PRODUCT_MODULE_NAME = Alamofire;
|
||||
PRODUCT_NAME = Alamofire;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
95DBEF0AB0B74932A7CEF4BB6099470D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33A04B5D27E86AF4B84D95E21CF3F452 /* Alamofire.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch";
|
||||
INFOPLIST_FILE = "Target Support Files/Alamofire/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MODULEMAP_FILE = "Target Support Files/Alamofire/Alamofire.modulemap";
|
||||
PRODUCT_MODULE_NAME = Alamofire;
|
||||
PRODUCT_NAME = Alamofire;
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) ";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
D781C58103B1A287E99380ADAEDB2534 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = B9EE5FF9411960F70122A41A2AEB141B /* Pods-easyCC.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = "Target Support Files/Pods-easyCC/Info.plist";
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
MACH_O_TYPE = staticlib;
|
||||
MODULEMAP_FILE = "Target Support Files/Pods-easyCC/Pods-easyCC.modulemap";
|
||||
OTHER_LDFLAGS = "";
|
||||
OTHER_LIBTOOLFLAGS = "";
|
||||
PODS_ROOT = "$(SRCROOT)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = iphoneos;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
FDB2FC4A1E5891381CD9D922145497F1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGNING_ALLOWED = NO;
|
||||
CODE_SIGNING_REQUIRED = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"POD_CONFIGURATION_RELEASE=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRIP_INSTALLED_PRODUCT = NO;
|
||||
SYMROOT = "${SRCROOT}/../build";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
199D972A13F2B4C56847F7A89CCA83BC /* Debug */,
|
||||
FDB2FC4A1E5891381CD9D922145497F1 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
419E5D95491847CD79841B971A8A3277 /* Build configuration list for PBXNativeTarget "Alamofire" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7B0415700290D1DEBAB6B173951CC0C7 /* Debug */,
|
||||
95DBEF0AB0B74932A7CEF4BB6099470D /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
91711423E8A5CF8B3C82785247824B35 /* Build configuration list for PBXNativeTarget "Pods-easyCC" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
5D6E1109C9897559A28F6EA4F0F7CCE7 /* Debug */,
|
||||
D781C58103B1A287E99380ADAEDB2534 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */;
|
||||
}
|
||||
60
Pods/Pods.xcodeproj/xcuserdata/h_mal.xcuserdatad/xcschemes/Alamofire.xcscheme
generated
Normal file
60
Pods/Pods.xcodeproj/xcuserdata/h_mal.xcuserdatad/xcschemes/Alamofire.xcscheme
generated
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForAnalyzing = "YES"
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "88E9EC28B8B46C3631E6B242B50F4442"
|
||||
BlueprintName = "Alamofire"
|
||||
ReferencedContainer = "container:Pods.xcodeproj"
|
||||
BuildableName = "Alamofire.framework">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
buildConfiguration = "Debug"
|
||||
allowLocationSimulation = "YES">
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
71
Pods/Pods.xcodeproj/xcuserdata/h_mal.xcuserdatad/xcschemes/Pods-easyCC.xcscheme
generated
Normal file
71
Pods/Pods.xcodeproj/xcuserdata/h_mal.xcuserdatad/xcschemes/Pods-easyCC.xcscheme
generated
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "790170DE2E174C423AD5E4A43A74F76E"
|
||||
BuildableName = "Pods_easyCC.framework"
|
||||
BlueprintName = "Pods-easyCC"
|
||||
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>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</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">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "790170DE2E174C423AD5E4A43A74F76E"
|
||||
BuildableName = "Pods_easyCC.framework"
|
||||
BlueprintName = "Pods-easyCC"
|
||||
ReferencedContainer = "container:Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
32
Pods/Pods.xcodeproj/xcuserdata/h_mal.xcuserdatad/xcschemes/xcschememanagement.plist
generated
Normal file
32
Pods/Pods.xcodeproj/xcuserdata/h_mal.xcuserdatad/xcschemes/xcschememanagement.plist
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>Alamofire.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>Pods-easyCC.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>790170DE2E174C423AD5E4A43A74F76E</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>88E9EC28B8B46C3631E6B242B50F4442</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
5
Pods/Target Support Files/Alamofire/Alamofire-dummy.m
generated
Normal file
5
Pods/Target Support Files/Alamofire/Alamofire-dummy.m
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_Alamofire : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_Alamofire
|
||||
@end
|
||||
12
Pods/Target Support Files/Alamofire/Alamofire-prefix.pch
generated
Normal file
12
Pods/Target Support Files/Alamofire/Alamofire-prefix.pch
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
#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
|
||||
|
||||
16
Pods/Target Support Files/Alamofire/Alamofire-umbrella.h
generated
Normal file
16
Pods/Target Support Files/Alamofire/Alamofire-umbrella.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
#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[];
|
||||
|
||||
6
Pods/Target Support Files/Alamofire/Alamofire.modulemap
generated
Normal file
6
Pods/Target Support Files/Alamofire/Alamofire.modulemap
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
framework module Alamofire {
|
||||
umbrella header "Alamofire-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
9
Pods/Target Support Files/Alamofire/Alamofire.xcconfig
generated
Normal file
9
Pods/Target Support Files/Alamofire/Alamofire.xcconfig
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Alamofire
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
26
Pods/Target Support Files/Alamofire/Info.plist
generated
Normal file
26
Pods/Target Support Files/Alamofire/Info.plist
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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>en</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>4.7.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
26
Pods/Target Support Files/Pods-easyCC/Info.plist
generated
Normal file
26
Pods/Target Support Files/Pods-easyCC/Info.plist
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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>en</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>1.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
26
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-acknowledgements.markdown
generated
Normal file
26
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-acknowledgements.markdown
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
# Acknowledgements
|
||||
This application makes use of the following third party libraries:
|
||||
|
||||
## Alamofire
|
||||
|
||||
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.
|
||||
|
||||
Generated by CocoaPods - https://cocoapods.org
|
||||
58
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-acknowledgements.plist
generated
Normal file
58
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-acknowledgements.plist
generated
Normal file
@@ -0,0 +1,58 @@
|
||||
<?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>PreferenceSpecifiers</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>This application makes use of the following third party libraries:</string>
|
||||
<key>Title</key>
|
||||
<string>Acknowledgements</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>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.
|
||||
</string>
|
||||
<key>License</key>
|
||||
<string>MIT</string>
|
||||
<key>Title</key>
|
||||
<string>Alamofire</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Generated by CocoaPods - https://cocoapods.org</string>
|
||||
<key>Title</key>
|
||||
<string></string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>StringsTable</key>
|
||||
<string>Acknowledgements</string>
|
||||
<key>Title</key>
|
||||
<string>Acknowledgements</string>
|
||||
</dict>
|
||||
</plist>
|
||||
5
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-dummy.m
generated
Normal file
5
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-dummy.m
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_Pods_easyCC : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_Pods_easyCC
|
||||
@end
|
||||
153
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-frameworks.sh
generated
Executable file
153
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-frameworks.sh
generated
Executable file
@@ -0,0 +1,153 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
|
||||
# If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
|
||||
# frameworks to, so exit 0 (signalling the script phase was successful).
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
|
||||
COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
|
||||
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
|
||||
|
||||
# Used as a return value for each invocation of `strip_invalid_archs` function.
|
||||
STRIP_BINARY_RETVAL=0
|
||||
|
||||
# This protects against multiple targets copying the same framework dependency at the same time. The solution
|
||||
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
|
||||
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
|
||||
|
||||
# Copies and strips a vendored framework
|
||||
install_framework()
|
||||
{
|
||||
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
|
||||
local source="${BUILT_PRODUCTS_DIR}/$1"
|
||||
elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
|
||||
local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
|
||||
elif [ -r "$1" ]; then
|
||||
local source="$1"
|
||||
fi
|
||||
|
||||
local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
|
||||
if [ -L "${source}" ]; then
|
||||
echo "Symlinked..."
|
||||
source="$(readlink "${source}")"
|
||||
fi
|
||||
|
||||
# Use filter instead of exclude so missing patterns don't throw errors.
|
||||
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
|
||||
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
|
||||
|
||||
local basename
|
||||
basename="$(basename -s .framework "$1")"
|
||||
binary="${destination}/${basename}.framework/${basename}"
|
||||
if ! [ -r "$binary" ]; then
|
||||
binary="${destination}/${basename}"
|
||||
fi
|
||||
|
||||
# Strip invalid architectures so "fat" simulator / device frameworks work on device
|
||||
if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
|
||||
strip_invalid_archs "$binary"
|
||||
fi
|
||||
|
||||
# Resign the code if required by the build settings to avoid unstable apps
|
||||
code_sign_if_enabled "${destination}/$(basename "$1")"
|
||||
|
||||
# Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
|
||||
if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
|
||||
local swift_runtime_libs
|
||||
swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
|
||||
for lib in $swift_runtime_libs; do
|
||||
echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
|
||||
rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
|
||||
code_sign_if_enabled "${destination}/${lib}"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Copies and strips a vendored dSYM
|
||||
install_dsym() {
|
||||
local source="$1"
|
||||
if [ -r "$source" ]; then
|
||||
# Copy the dSYM into a the targets temp dir.
|
||||
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
|
||||
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
|
||||
|
||||
local basename
|
||||
basename="$(basename -s .framework.dSYM "$source")"
|
||||
binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
|
||||
|
||||
# Strip invalid architectures so "fat" simulator / device frameworks work on device
|
||||
if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then
|
||||
strip_invalid_archs "$binary"
|
||||
fi
|
||||
|
||||
if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
|
||||
# Move the stripped file into its final destination.
|
||||
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
|
||||
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
|
||||
else
|
||||
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
|
||||
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Signs a framework with the provided identity
|
||||
code_sign_if_enabled() {
|
||||
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
|
||||
# Use the current code_sign_identitiy
|
||||
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
|
||||
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
|
||||
|
||||
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
|
||||
code_sign_cmd="$code_sign_cmd &"
|
||||
fi
|
||||
echo "$code_sign_cmd"
|
||||
eval "$code_sign_cmd"
|
||||
fi
|
||||
}
|
||||
|
||||
# Strip invalid architectures
|
||||
strip_invalid_archs() {
|
||||
binary="$1"
|
||||
# Get architectures for current target binary
|
||||
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
|
||||
# Intersect them with the architectures we are building for
|
||||
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
|
||||
# If there are no archs supported by this binary then warn the user
|
||||
if [[ -z "$intersected_archs" ]]; then
|
||||
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
|
||||
STRIP_BINARY_RETVAL=0
|
||||
return
|
||||
fi
|
||||
stripped=""
|
||||
for arch in $binary_archs; do
|
||||
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
|
||||
# Strip non-valid architectures in-place
|
||||
lipo -remove "$arch" -output "$binary" "$binary" || exit 1
|
||||
stripped="$stripped $arch"
|
||||
fi
|
||||
done
|
||||
if [[ "$stripped" ]]; then
|
||||
echo "Stripped $binary of architectures:$stripped"
|
||||
fi
|
||||
STRIP_BINARY_RETVAL=1
|
||||
}
|
||||
|
||||
|
||||
if [[ "$CONFIGURATION" == "Debug" ]]; then
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
|
||||
fi
|
||||
if [[ "$CONFIGURATION" == "Release" ]]; then
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
|
||||
fi
|
||||
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
|
||||
wait
|
||||
fi
|
||||
118
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-resources.sh
generated
Executable file
118
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-resources.sh
generated
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then
|
||||
# If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy
|
||||
# resources to, so exit 0 (signalling the script phase was successful).
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||
|
||||
RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
|
||||
> "$RESOURCES_TO_COPY"
|
||||
|
||||
XCASSET_FILES=()
|
||||
|
||||
# This protects against multiple targets copying the same framework dependency at the same time. The solution
|
||||
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
|
||||
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
|
||||
|
||||
case "${TARGETED_DEVICE_FAMILY:-}" in
|
||||
1,2)
|
||||
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
|
||||
;;
|
||||
1)
|
||||
TARGET_DEVICE_ARGS="--target-device iphone"
|
||||
;;
|
||||
2)
|
||||
TARGET_DEVICE_ARGS="--target-device ipad"
|
||||
;;
|
||||
3)
|
||||
TARGET_DEVICE_ARGS="--target-device tv"
|
||||
;;
|
||||
4)
|
||||
TARGET_DEVICE_ARGS="--target-device watch"
|
||||
;;
|
||||
*)
|
||||
TARGET_DEVICE_ARGS="--target-device mac"
|
||||
;;
|
||||
esac
|
||||
|
||||
install_resource()
|
||||
{
|
||||
if [[ "$1" = /* ]] ; then
|
||||
RESOURCE_PATH="$1"
|
||||
else
|
||||
RESOURCE_PATH="${PODS_ROOT}/$1"
|
||||
fi
|
||||
if [[ ! -e "$RESOURCE_PATH" ]] ; then
|
||||
cat << EOM
|
||||
error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
|
||||
EOM
|
||||
exit 1
|
||||
fi
|
||||
case $RESOURCE_PATH in
|
||||
*.storyboard)
|
||||
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
|
||||
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
|
||||
;;
|
||||
*.xib)
|
||||
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
|
||||
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
|
||||
;;
|
||||
*.framework)
|
||||
echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
|
||||
mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
|
||||
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
;;
|
||||
*.xcdatamodel)
|
||||
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
|
||||
xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
|
||||
;;
|
||||
*.xcdatamodeld)
|
||||
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
|
||||
xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
|
||||
;;
|
||||
*.xcmappingmodel)
|
||||
echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
|
||||
xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
|
||||
;;
|
||||
*.xcassets)
|
||||
ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
|
||||
XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
|
||||
;;
|
||||
*)
|
||||
echo "$RESOURCE_PATH" || true
|
||||
echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||
if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
|
||||
mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||
rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||
fi
|
||||
rm -f "$RESOURCES_TO_COPY"
|
||||
|
||||
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ]
|
||||
then
|
||||
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
|
||||
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
|
||||
while read line; do
|
||||
if [[ $line != "${PODS_ROOT}*" ]]; then
|
||||
XCASSET_FILES+=("$line")
|
||||
fi
|
||||
done <<<"$OTHER_XCASSETS"
|
||||
|
||||
if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then
|
||||
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||
else
|
||||
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist"
|
||||
fi
|
||||
fi
|
||||
16
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-umbrella.h
generated
Normal file
16
Pods/Target Support Files/Pods-easyCC/Pods-easyCC-umbrella.h
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
#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 Pods_easyCCVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char Pods_easyCCVersionString[];
|
||||
|
||||
11
Pods/Target Support Files/Pods-easyCC/Pods-easyCC.debug.xcconfig
generated
Normal file
11
Pods/Target Support Files/Pods-easyCC/Pods-easyCC.debug.xcconfig
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers"
|
||||
OTHER_LDFLAGS = $(inherited) -framework "Alamofire"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
|
||||
PODS_ROOT = ${SRCROOT}/Pods
|
||||
6
Pods/Target Support Files/Pods-easyCC/Pods-easyCC.modulemap
generated
Normal file
6
Pods/Target Support Files/Pods-easyCC/Pods-easyCC.modulemap
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
framework module Pods_easyCC {
|
||||
umbrella header "Pods-easyCC-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
11
Pods/Target Support Files/Pods-easyCC/Pods-easyCC.release.xcconfig
generated
Normal file
11
Pods/Target Support Files/Pods-easyCC/Pods-easyCC.release.xcconfig
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers"
|
||||
OTHER_LDFLAGS = $(inherited) -framework "Alamofire"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
|
||||
PODS_ROOT = ${SRCROOT}/Pods
|
||||
@@ -14,6 +14,7 @@
|
||||
647522C9212F895400F06A1B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 647522C7212F895400F06A1B /* LaunchScreen.storyboard */; };
|
||||
647522D4212F895400F06A1B /* easyCCTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647522D3212F895400F06A1B /* easyCCTests.swift */; };
|
||||
647522DF212F895400F06A1B /* easyCCUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647522DE212F895400F06A1B /* easyCCUITests.swift */; };
|
||||
AD16DFB459D342A2ACE549CD /* Pods_easyCC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E9AC5790CB8FD511B83D0A9 /* Pods_easyCC.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -34,6 +35,9 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
073C0080E986B9DF961A5BE2 /* Pods_easyCCUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_easyCCUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1DE2E01EC13D9FB592123728 /* Pods-easyCC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-easyCC.release.xcconfig"; path = "Pods/Target Support Files/Pods-easyCC/Pods-easyCC.release.xcconfig"; sourceTree = "<group>"; };
|
||||
37DF5F0032479FB0FF847F75 /* Pods-easyCC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-easyCC.debug.xcconfig"; path = "Pods/Target Support Files/Pods-easyCC/Pods-easyCC.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
647522BB212F895400F06A1B /* easyCC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = easyCC.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
647522BE212F895400F06A1B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
647522C0212F895400F06A1B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -47,6 +51,8 @@
|
||||
647522DA212F895400F06A1B /* easyCCUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = easyCCUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
647522DE212F895400F06A1B /* easyCCUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = easyCCUITests.swift; sourceTree = "<group>"; };
|
||||
647522E0212F895400F06A1B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
6E9AC5790CB8FD511B83D0A9 /* Pods_easyCC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_easyCC.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EED5884D459E0768F0E09BEB /* Pods_easyCCTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_easyCCTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -54,6 +60,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AD16DFB459D342A2ACE549CD /* Pods_easyCC.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -74,6 +81,16 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
41A411B88F77BF0337F08CAB /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6E9AC5790CB8FD511B83D0A9 /* Pods_easyCC.framework */,
|
||||
EED5884D459E0768F0E09BEB /* Pods_easyCCTests.framework */,
|
||||
073C0080E986B9DF961A5BE2 /* Pods_easyCCUITests.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
647522B2212F895400F06A1B = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -81,6 +98,8 @@
|
||||
647522D2212F895400F06A1B /* easyCCTests */,
|
||||
647522DD212F895400F06A1B /* easyCCUITests */,
|
||||
647522BC212F895400F06A1B /* Products */,
|
||||
7F2F59131A8B0EE007D41B8E /* Pods */,
|
||||
41A411B88F77BF0337F08CAB /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -125,6 +144,15 @@
|
||||
path = easyCCUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7F2F59131A8B0EE007D41B8E /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
37DF5F0032479FB0FF847F75 /* Pods-easyCC.debug.xcconfig */,
|
||||
1DE2E01EC13D9FB592123728 /* Pods-easyCC.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -132,9 +160,11 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 647522E3212F895400F06A1B /* Build configuration list for PBXNativeTarget "easyCC" */;
|
||||
buildPhases = (
|
||||
8A3CA7BE322C310D3CA1C099 /* [CP] Check Pods Manifest.lock */,
|
||||
647522B7212F895400F06A1B /* Sources */,
|
||||
647522B8212F895400F06A1B /* Frameworks */,
|
||||
647522B9212F895400F06A1B /* Resources */,
|
||||
61160B645749553C5F419391 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -254,6 +284,45 @@
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
61160B645749553C5F419391 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-easyCC/Pods-easyCC-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-easyCC/Pods-easyCC-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
8A3CA7BE322C310D3CA1C099 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-easyCC-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
647522B7212F895400F06A1B /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
@@ -412,6 +481,7 @@
|
||||
};
|
||||
647522E4212F895400F06A1B /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 37DF5F0032479FB0FF847F75 /* Pods-easyCC.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = easyCC/Info.plist;
|
||||
@@ -424,6 +494,7 @@
|
||||
};
|
||||
647522E5212F895400F06A1B /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 1DE2E01EC13D9FB592123728 /* Pods-easyCC.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = easyCC/Info.plist;
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "easyCC/ViewController.swift"
|
||||
timestampString = "556774887.786877"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "23"
|
||||
endingLineNumber = "23"
|
||||
landmarkName = "ViewController"
|
||||
landmarkType = "3">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "easyCC/ViewController.swift"
|
||||
timestampString = "556774887.786877"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "20"
|
||||
endingLineNumber = "20"
|
||||
landmarkName = "ViewController"
|
||||
landmarkType = "3">
|
||||
<Locations>
|
||||
<Location
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
symbolName = "easyCC.ViewController.currencyList.getter : Swift.Array<Swift.String>"
|
||||
moduleName = "easyCC"
|
||||
usesParentBreakpointCondition = "Yes"
|
||||
urlString = "file:///Users/h_mal/Documents/Ios%20App/easyCC/easyCC/ViewController.swift"
|
||||
timestampString = "556771544.8885601"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "20"
|
||||
endingLineNumber = "20"
|
||||
offsetFromSymbolStart = "38">
|
||||
</Location>
|
||||
<Location
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
symbolName = "easyCC.ViewController.currencyList.setter : Swift.Array<Swift.String>"
|
||||
moduleName = "easyCC"
|
||||
usesParentBreakpointCondition = "Yes"
|
||||
urlString = "file:///Users/h_mal/Documents/Ios%20App/easyCC/easyCC/ViewController.swift"
|
||||
timestampString = "556771544.892626"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "20"
|
||||
endingLineNumber = "20"
|
||||
offsetFromSymbolStart = "76">
|
||||
</Location>
|
||||
<Location
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
symbolName = "easyCC.ViewController.init (nibName : Swift.Optional<Swift.String>, bundle : Swift.Optional<__ObjC.Bundle>) -> easyCC.ViewController"
|
||||
moduleName = "easyCC"
|
||||
usesParentBreakpointCondition = "Yes"
|
||||
urlString = "file:///Users/h_mal/Documents/Ios%20App/easyCC/easyCC/ViewController.swift"
|
||||
timestampString = "556771544.894362"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "20"
|
||||
endingLineNumber = "20"
|
||||
offsetFromSymbolStart = "438">
|
||||
</Location>
|
||||
<Location
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
symbolName = "easyCC.ViewController.init (coder : __ObjC.NSCoder) -> Swift.Optional<easyCC.ViewController>"
|
||||
moduleName = "easyCC"
|
||||
usesParentBreakpointCondition = "Yes"
|
||||
urlString = "file:///Users/h_mal/Documents/Ios%20App/easyCC/easyCC/ViewController.swift"
|
||||
timestampString = "556771544.8957779"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "20"
|
||||
endingLineNumber = "20"
|
||||
offsetFromSymbolStart = "340">
|
||||
</Location>
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
@@ -5,6 +5,22 @@
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "647522BA212F895400F06A1B"
|
||||
BuildableName = "easyCC.app"
|
||||
BlueprintName = "easyCC"
|
||||
ReferencedContainer = "container:easyCC.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -12,7 +28,36 @@
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "647522CE212F895400F06A1B"
|
||||
BuildableName = "easyCCTests.xctest"
|
||||
BlueprintName = "easyCCTests"
|
||||
ReferencedContainer = "container:easyCC.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "647522D9212F895400F06A1B"
|
||||
BuildableName = "easyCCUITests.xctest"
|
||||
BlueprintName = "easyCCUITests"
|
||||
ReferencedContainer = "container:easyCC.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "647522BA212F895400F06A1B"
|
||||
BuildableName = "easyCC.app"
|
||||
BlueprintName = "easyCC"
|
||||
ReferencedContainer = "container:easyCC.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
@@ -26,6 +71,16 @@
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "647522BA212F895400F06A1B"
|
||||
BuildableName = "easyCC.app"
|
||||
BlueprintName = "easyCC"
|
||||
ReferencedContainer = "container:easyCC.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
@@ -35,6 +90,16 @@
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "647522BA212F895400F06A1B"
|
||||
BuildableName = "easyCC.app"
|
||||
BlueprintName = "easyCC"
|
||||
ReferencedContainer = "container:easyCC.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
|
||||
10
easyCC.xcworkspace/contents.xcworkspacedata
generated
Normal file
10
easyCC.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:easyCC.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
type = "0"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "easyCC/ViewController.swift"
|
||||
timestampString = "556847420.009944"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "112"
|
||||
endingLineNumber = "112"
|
||||
landmarkName = "downloadData()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "easyCC/ViewController.swift"
|
||||
timestampString = "556847420.009944"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "53"
|
||||
endingLineNumber = "53"
|
||||
landmarkName = "textFieldChangeListener(_:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
@@ -1,14 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="easyCC" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
@@ -16,11 +21,96 @@
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="52C-6q-x8K">
|
||||
<rect key="frame" x="16" y="28" width="343" height="30"/>
|
||||
<nil key="textColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="BYZ-38-t0r" id="R0M-rb-xZv"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Insert Currency One" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="s4Z-H2-NTI">
|
||||
<rect key="frame" x="16" y="66" width="343" height="30"/>
|
||||
<nil key="textColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" keyboardType="numberPad"/>
|
||||
<connections>
|
||||
<action selector="textFieldChangeListener:" destination="BYZ-38-t0r" eventType="editingChanged" id="P5a-Ve-5DY"/>
|
||||
<outlet property="delegate" destination="BYZ-38-t0r" id="Qbr-AB-HET"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="gOb-AC-a8R">
|
||||
<rect key="frame" x="16" y="104" width="343" height="30"/>
|
||||
<nil key="textColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="BYZ-38-t0r" id="WXh-gB-ZBs"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Insert Currency Two" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="s84-D2-Z6S">
|
||||
<rect key="frame" x="16" y="142" width="343" height="30"/>
|
||||
<nil key="textColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" keyboardType="numberPad"/>
|
||||
<connections>
|
||||
<action selector="textChangeListenerTwo:" destination="BYZ-38-t0r" eventType="editingChanged" id="HbP-Ba-w32"/>
|
||||
<outlet property="delegate" destination="BYZ-38-t0r" id="886-rA-OY8"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<pickerView tag="1" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yUx-aC-4ek">
|
||||
<rect key="frame" x="16" y="66" width="343" height="216"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="BYZ-38-t0r" id="BEc-sB-ERF"/>
|
||||
<outlet property="delegate" destination="BYZ-38-t0r" id="ap9-xn-69a"/>
|
||||
</connections>
|
||||
</pickerView>
|
||||
<pickerView tag="2" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="t8J-WK-23N">
|
||||
<rect key="frame" x="16" y="142" width="343" height="216"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="BYZ-38-t0r" id="8WA-hD-Udv"/>
|
||||
<outlet property="delegate" destination="BYZ-38-t0r" id="YD2-WJ-bWm"/>
|
||||
</connections>
|
||||
</pickerView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="gOb-AC-a8R" firstAttribute="top" secondItem="s4Z-H2-NTI" secondAttribute="bottom" constant="8" id="6SV-fL-Y8u"/>
|
||||
<constraint firstItem="s84-D2-Z6S" firstAttribute="top" secondItem="gOb-AC-a8R" secondAttribute="bottom" constant="8" id="Cir-UV-QMz"/>
|
||||
<constraint firstItem="gOb-AC-a8R" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="DF6-aJ-HaO"/>
|
||||
<constraint firstItem="s84-D2-Z6S" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="EXF-RC-KfF"/>
|
||||
<constraint firstItem="52C-6q-x8K" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="KwK-8r-TvX"/>
|
||||
<constraint firstItem="s4Z-H2-NTI" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="NGq-8J-r6P"/>
|
||||
<constraint firstItem="52C-6q-x8K" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="OqN-xN-xOw"/>
|
||||
<constraint firstItem="s4Z-H2-NTI" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="RbV-Ds-F3F"/>
|
||||
<constraint firstItem="yUx-aC-4ek" firstAttribute="top" secondItem="52C-6q-x8K" secondAttribute="bottom" constant="8" id="b4W-eV-L3k"/>
|
||||
<constraint firstItem="gOb-AC-a8R" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="bjg-rB-FXt"/>
|
||||
<constraint firstItem="gOb-AC-a8R" firstAttribute="top" secondItem="s4Z-H2-NTI" secondAttribute="bottom" constant="8" id="c77-lI-jFp"/>
|
||||
<constraint firstItem="t8J-WK-23N" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="cHL-nr-uDQ"/>
|
||||
<constraint firstItem="s84-D2-Z6S" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="cMG-9W-xEp"/>
|
||||
<constraint firstItem="t8J-WK-23N" firstAttribute="top" secondItem="gOb-AC-a8R" secondAttribute="bottom" constant="8" id="gRX-x6-Ibq"/>
|
||||
<constraint firstItem="yUx-aC-4ek" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="jNn-Gf-Hl0"/>
|
||||
<constraint firstItem="s4Z-H2-NTI" firstAttribute="top" secondItem="52C-6q-x8K" secondAttribute="bottom" constant="8" id="kWx-YV-NXN"/>
|
||||
<constraint firstItem="52C-6q-x8K" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" constant="8" id="nq1-ii-YE0"/>
|
||||
<constraint firstItem="t8J-WK-23N" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="pcs-6v-nte"/>
|
||||
<constraint firstItem="yUx-aC-4ek" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="rac-pG-7fy"/>
|
||||
<constraint firstItem="t8J-WK-23N" firstAttribute="top" secondItem="gOb-AC-a8R" secondAttribute="bottom" constant="8" id="yyc-QF-HFD"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="currencyChoiceOne" destination="52C-6q-x8K" id="Xgb-op-nVl"/>
|
||||
<outlet property="currencyChoiceTwo" destination="gOb-AC-a8R" id="WkE-UE-YZP"/>
|
||||
<outlet property="inputOne" destination="s4Z-H2-NTI" id="zCa-zX-eVM"/>
|
||||
<outlet property="inputTwo" destination="s84-D2-Z6S" id="ij0-d7-SrY"/>
|
||||
<outlet property="spinnerOne" destination="yUx-aC-4ek" id="tVp-B2-jMc"/>
|
||||
<outlet property="spinnerTwo" destination="t8J-WK-23N" id="t80-9W-uJ2"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="117.59999999999999" y="122.78860569715144"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
||||
@@ -7,11 +7,37 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Alamofire
|
||||
|
||||
class ViewController: UIViewController {
|
||||
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate{
|
||||
|
||||
@IBOutlet weak var currencyChoiceOne: UITextField!
|
||||
@IBOutlet weak var inputOne: UITextField!
|
||||
@IBOutlet weak var currencyChoiceTwo: UITextField!
|
||||
@IBOutlet weak var inputTwo: UITextField!
|
||||
@IBOutlet weak var spinnerOne: UIPickerView!
|
||||
@IBOutlet weak var spinnerTwo: UIPickerView!
|
||||
|
||||
var rateOne: String = ""
|
||||
var rateTwo: String = ""
|
||||
|
||||
var exchangeRate: Double = 1
|
||||
|
||||
var currencyList = ["AFN - Afghan Afghani","DZD - Algerian Dinar","AOA - Angolan Kwanza","ARS - Argentine Peso","AMD - Armenian Dram","AWG - Aruban Florin","AUD - Australian Dollar","AZN - Azerbaijani Manat","BSD - Bahamian Dollar","BHD - Bahraini Dinar","BDT - Bangladeshi Taka","BBD - Barbadian Dollar","BYR - Belarusian Ruble","BZD - Belize Dollar","BTN - Bhutanese Ngultrum","BTC - Bitcoin","BOB - Bolivian Boliviano","BAM - Bosnia And Herzegovina Konvertibilna Marka","BWP - Botswana Pula","BRL - Brazilian Real","GBP - British Pound","BND - Brunei Dollar","BGN - Bulgarian Lev","BIF - Burundi Franc","KHR - Cambodian Riel","CAD - Canadian Dollar","CVE - Cape Verdean Escudo","KYD - Cayman Islands Dollar","XAF - Central African CFA Franc","XPF - CFP Franc","CLP - Chilean Peso","CNY - Chinese Yuan","COP - Colombian Peso","KMF - Comorian Franc","CDF - Congolese Franc","CRC - Costa Rican Colon","HRK - Croatian Kuna","CUP - Cuban Peso","CZK - Czech Koruna","DKK - Danish Krone","DJF - Djiboutian Franc","DOP - Dominican Peso","XCD - East Caribbean Dollar","EGP - Egyptian Pound","ERN - Eritrean Nakfa","ETB - Ethiopian Birr","EUR - Euro","FKP - Falkland Islands Pound","FJD - Fijian Dollar","GMD - Gambian Dalasi","GEL - Georgian Lari","GHS - Ghanaian Cedi","GIP - Gibraltar Pound","GTQ - Guatemalan Quetzal","GNF - Guinean Franc","GYD - Guyanese Dollar","HTG - Haitian Gourde","HNL - Honduran Lempira","HKD - Hong Kong Dollar","HUF - Hungarian Forint","ISK - Icelandic Kruna","INR - Indian Rupee","IDR - Indonesian Rupiah","IRR - Iranian Rial","IQD - Iraqi Dinar","ILS - Israeli New Sheqel","JMD - Jamaican Dollar","JPY - Japanese Yen","JOD - Jordanian Dinar","KZT - Kazakhstani Tenge","KES - Kenyan Shilling","KWD - Kuwaiti Dinar","KGS - Kyrgyzstani Som","LAK - Lao Kip","LVL - Latvian Lats","LBP - Lebanese Lira","LSL - Lesotho Loti","LRD - Liberian Dollar","LYD - Libyan Dinar","MOP - Macanese Pataca","MKD - Macedonian Denar","MGA - Malagasy Ariary","MWK - Malawian Kwacha","MYR - Malaysian Ringgit","MVR - Maldivian Rufiyaa","MRO - Mauritanian Ouguiya","MUR - Mauritian Rupee","MXN - Mexican Peso","MDL - Moldovan Leu","MNT - Mongolian Tugrik","MAD - Moroccan Dirham","MZN - Mozambican Metical","MMK - Myanma Kyat","NAD - Namibian Dollar","NPR - Nepalese Rupee","ANG - Netherlands Antillean Gulden","TWD - New Taiwan Dollar","NZD - New Zealand Dollar","NIO - Nicaraguan Cordoba","NGN - Nigerian Naira","KPW - North Korean Won","NOK - Norwegian Krone","OMR - Omani Rial","TOP - Paanga","PKR - Pakistani Rupee","PAB - Panamanian Balboa","PGK - Papua New Guinean Kina","PYG - Paraguayan Guarani","PEN - Peruvian Nuevo Sol","PHP - Philippine Peso","PLN - Polish Zloty","QAR - Qatari Riyal","RON - Romanian Leu","RUB - Russian Ruble","RWF - Rwandan Franc","SHP - Saint Helena Pound","WST - Samoan Tala","STD - Sao Tome And Principe Dobra","SAR - Saudi Riyal","RSD - Serbian Dinar","SCR - Seychellois Rupee","SLL - Sierra Leonean Leone","SGD - Singapore Dollar","SBD - Solomon Islands Dollar","SOS - Somali Shilling","ZAR - South African Rand","KRW - South Korean Won","XDR - Special Drawing Rights","LKR - Sri Lankan Rupee","SDG - Sudanese Pound","SRD - Surinamese Dollar","SZL - Swazi Lilangeni","SEK - Swedish Krona","CHF - Swiss Franc","SYP - Syrian Pound","TJS - Tajikistani Somoni","TZS - Tanzanian Shilling","THB - Thai Baht","TTD - Trinidad and Tobago Dollar","TND - Tunisian Dinar","TRY - Turkish New Lira","TMT - Turkmenistan Manat","AED - UAE Dirham","UGX - Ugandan Shilling","UAH - Ukrainian Hryvnia","USD - United States Dollar","UYU - Uruguayan Peso","UZS - Uzbekistani Som","VUV - Vanuatu Vatu","VEF - Venezuelan Bolivar","VND - Vietnamese Dong","XOF - West African CFA Franc","YER - Yemeni Rial","ZMW - Zambian Kwacha"]
|
||||
|
||||
|
||||
var converstionRate:Double?
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.currencyChoiceOne.text = self.currencyList[1]
|
||||
self.currencyChoiceTwo.text = self.currencyList[1]
|
||||
|
||||
self.spinnerOne.isHidden = true
|
||||
self.spinnerTwo.isHidden = true
|
||||
|
||||
// Do any additional setup after loading the view, typically from a nib.
|
||||
}
|
||||
|
||||
@@ -19,7 +45,88 @@ class ViewController: UIViewController {
|
||||
super.didReceiveMemoryWarning()
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
@IBAction func textFieldChangeListener(_ sender: Any) {
|
||||
let valueOne = Double(inputOne.text!)
|
||||
let valueTwo = (valueOne ?? 1)*exchangeRate
|
||||
self.inputTwo.text = String(describing: valueTwo)
|
||||
}
|
||||
|
||||
@IBAction func textChangeListenerTwo(_ sender: Any) {
|
||||
let valueOne = Double(inputTwo.text!)
|
||||
let valueTwo = (valueOne ?? 1)*exchangeRate
|
||||
self.inputOne.text = String(valueTwo)
|
||||
}
|
||||
|
||||
public func numberOfComponents(in pickerView: UIPickerView) -> Int{
|
||||
return 1
|
||||
|
||||
}
|
||||
|
||||
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
|
||||
|
||||
return currencyList.count
|
||||
|
||||
}
|
||||
|
||||
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
|
||||
|
||||
self.view.endEditing(true)
|
||||
return currencyList[row]
|
||||
|
||||
}
|
||||
|
||||
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
|
||||
|
||||
if (pickerView.tag == 1){
|
||||
self.currencyChoiceOne.text = self.currencyList[row]
|
||||
downloadData()
|
||||
self.spinnerOne.isHidden = true
|
||||
}else if(pickerView.tag == 2){
|
||||
self.currencyChoiceTwo.text = self.currencyList[row]
|
||||
downloadData()
|
||||
self.spinnerTwo.isHidden = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||
|
||||
if textField == self.currencyChoiceOne {
|
||||
self.spinnerOne.isHidden = false
|
||||
//if you dont want the users to se the keyboard type:
|
||||
|
||||
textField.endEditing(true)
|
||||
}else if textField == self.currencyChoiceTwo{
|
||||
self.spinnerTwo.isHidden = false
|
||||
|
||||
textField.endEditing(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func downloadData() {
|
||||
Alamofire.request(urlBuilder()).responseJSON { response in
|
||||
|
||||
if let json = response.result.value as? Dictionary<String,Double> {
|
||||
self.exchangeRate = json["\(self.rateOne)_\(self.rateTwo)"]!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func urlBuilder() -> String{
|
||||
|
||||
var url = "https://free.currencyconverterapi.com/api/v6/convert?q="
|
||||
|
||||
rateOne = String(currencyChoiceOne.text!.characters.prefix(3))
|
||||
rateTwo = String(currencyChoiceTwo.text!.characters.prefix(3))
|
||||
|
||||
url = url + rateOne + "_" + rateTwo + "&compact=ultra"
|
||||
|
||||
print(url)
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user