mirror of
https://github.com/qinyuhang/ShadowsocksX-NG-R.git
synced 2026-10-20 22:44:11 +00:00
update pod Alamofire to 4.2.0
This commit is contained in:
@@ -6,7 +6,7 @@ target 'ShadowsocksX-NG' do
|
||||
use_frameworks!
|
||||
|
||||
# Pods for ShadowsocksX-NG
|
||||
pod 'Alamofire', '~> 4.0.1'
|
||||
pod 'Alamofire', '~> 4.2.0'
|
||||
pod 'GCDWebServer', '~> 3.0'
|
||||
|
||||
target 'ShadowsocksX-NGTests' do
|
||||
|
||||
+5
-5
@@ -1,20 +1,20 @@
|
||||
PODS:
|
||||
- Alamofire (4.0.1)
|
||||
- Alamofire (4.2.0)
|
||||
- BRLOptionParser (0.3.1)
|
||||
- GCDWebServer (3.3.3):
|
||||
- GCDWebServer/Core (= 3.3.3)
|
||||
- GCDWebServer/Core (3.3.3)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Alamofire (~> 4.0)
|
||||
- Alamofire (~> 4.2.0)
|
||||
- BRLOptionParser (~> 0.3.1)
|
||||
- GCDWebServer (~> 3.0)
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: 7682d43245de14874acd142ec137b144aa1dd335
|
||||
Alamofire: aa2e09d871c9160ac53c90e83c68064a94e3dfbe
|
||||
BRLOptionParser: a03256a8ff003ca1f5376c55f55f210e085a3958
|
||||
GCDWebServer: 1c39a1f0763e4eb492bee021e4270fce097d3555
|
||||
|
||||
PODFILE CHECKSUM: fe577fe89bbdf6636ba715331bf8d97bbbee45bb
|
||||
PODFILE CHECKSUM: 8e98b2fb2d6be56882a44b2ff9241a800101e1d4
|
||||
|
||||
COCOAPODS: 1.1.0.rc.2
|
||||
COCOAPODS: 1.1.1
|
||||
|
||||
Generated
+22
-16
@@ -55,8 +55,8 @@ In order to keep Alamofire focused specifically on core networking implementatio
|
||||
|
||||
## Requirements
|
||||
|
||||
- iOS 9.0+ / macOS 10.11+ / tvOS 9.0+ / watchOS 2.0+
|
||||
- Xcode 8.0+
|
||||
- iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
|
||||
- Xcode 8.1+
|
||||
- Swift 3.0+
|
||||
|
||||
## Migration Guides
|
||||
@@ -130,13 +130,13 @@ If you prefer not to use either of the aforementioned dependency managers, you c
|
||||
|
||||
- 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
|
||||
```bash
|
||||
$ git init
|
||||
```
|
||||
|
||||
- Add Alamofire as a git [submodule](http://git-scm.com/docs/git-submodule) by running the following command:
|
||||
|
||||
```bash
|
||||
```bash
|
||||
$ git submodule add https://github.com/Alamofire/Alamofire.git
|
||||
```
|
||||
|
||||
@@ -158,7 +158,7 @@ $ git submodule add https://github.com/Alamofire/Alamofire.git
|
||||
|
||||
- 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.
|
||||
> 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.
|
||||
|
||||
---
|
||||
|
||||
@@ -199,7 +199,7 @@ Alamofire contains five different response handlers by default including:
|
||||
// Response Handler - Unserialized Response
|
||||
func response(
|
||||
queue: DispatchQueue?,
|
||||
completionHandler: @escaping (DefaultDownloadResponse) -> Void)
|
||||
completionHandler: @escaping (DefaultDataResponse) -> Void)
|
||||
-> Self
|
||||
|
||||
// Response Data Handler - Serialized into Data
|
||||
@@ -240,7 +240,7 @@ The `response` handler does NOT evaluate any of the response data. It merely for
|
||||
Alamofire.request("https://httpbin.org/get").response { response in
|
||||
print("Request: \(response.request)")
|
||||
print("Response: \(response.response)")
|
||||
print("Error: \(response.data)")
|
||||
print("Error: \(response.error)")
|
||||
|
||||
if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
|
||||
print("Data: \(utf8Text)")
|
||||
@@ -331,7 +331,7 @@ By default, Alamofire treats any completed request to be successful, regardless
|
||||
Alamofire.request("https://httpbin.org/get")
|
||||
.validate(statusCode: 200..<300)
|
||||
.validate(contentType: ["application/json"])
|
||||
.response { response in
|
||||
.responseData { response in
|
||||
switch response.result {
|
||||
case .success:
|
||||
print("Validation Successful")
|
||||
@@ -360,7 +360,7 @@ Alamofire.request("https://httpbin.org/get").validate().responseJSON { response
|
||||
|
||||
Response Caching is handled on the system framework level by [`URLCache`](https://developer.apple.com/reference/foundation/urlcache). It provides a composite in-memory and on-disk cache and lets you manipulate the sizes of both the in-memory and on-disk portions.
|
||||
|
||||
> By default, Alamofire leverages the shared `URLCache`. In order to customize it, see the [Session Manager Configurations](#session-manager-configurations) section.
|
||||
> By default, Alamofire leverages the shared `URLCache`. In order to customize it, see the [Session Manager Configurations](#session-manager) section.
|
||||
|
||||
### HTTP Methods
|
||||
|
||||
@@ -520,7 +520,7 @@ Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON
|
||||
}
|
||||
```
|
||||
|
||||
> For HTTP headers that do not change, it is recommended to set them on the `URLSessionConfiguration` so they are automatically applied to any `URLSessionTask` created by the underlying `URLSession`. For more information, see the [Session Manager Configurations](#session-manager-configurations) section.
|
||||
> For HTTP headers that do not change, it is recommended to set them on the `URLSessionConfiguration` so they are automatically applied to any `URLSessionTask` created by the underlying `URLSession`. For more information, see the [Session Manager Configurations](#session-manager) section.
|
||||
|
||||
The default Alamofire `SessionManager` provides a default set of headers for every `Request`. These include:
|
||||
|
||||
@@ -528,7 +528,7 @@ The default Alamofire `SessionManager` provides a default set of headers for eve
|
||||
- `Accept-Language`, which defaults to up to the top 6 preferred languages on the system, formatted like `en;q=1.0`, per [RFC 7231 §5.3.5](https://tools.ietf.org/html/rfc7231#section-5.3.5).
|
||||
- `User-Agent`, which contains versioning information about the current app. For example: `iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0`, per [RFC 7231 §5.5.3](https://tools.ietf.org/html/rfc7231#section-5.5.3).
|
||||
|
||||
If you need to customize these headers, a custom `URLSessionManagerConfiguration` should be created, the `defaultHTTPHeaders` property updated and the configuration applied to a new `SessionManager` instance.
|
||||
If you need to customize these headers, a custom `URLSessionConfiguration` should be created, the `defaultHTTPHeaders` property updated and the configuration applied to a new `SessionManager` instance.
|
||||
|
||||
### Authentication
|
||||
|
||||
@@ -603,7 +603,7 @@ Alamofire.download("https://httpbin.org/image/png").responseData { response in
|
||||
}
|
||||
```
|
||||
|
||||
> The `Alamofire.download` APIs should also be used if you need to download data while your app is in the background. For more information, please see the [Session Manager Configurations](#session-manager-configurations) section.
|
||||
> The `Alamofire.download` APIs should also be used if you need to download data while your app is in the background. For more information, please see the [Session Manager Configurations](#session-manager) section.
|
||||
|
||||
#### Download File Destination
|
||||
|
||||
@@ -623,7 +623,7 @@ let destination: DownloadRequest.DownloadFileDestination = { _, _ in
|
||||
Alamofire.download(urlString, to: destination).response { response in
|
||||
print(response)
|
||||
|
||||
if response.result.isSuccess, let imagePath = response.destinationURL?.path {
|
||||
if response.error == nil, let imagePath = response.destinationURL?.path {
|
||||
let image = UIImage(contentsOfFile: imagePath)
|
||||
}
|
||||
}
|
||||
@@ -672,6 +672,8 @@ Alamofire.download("https://httpbin.org/image/png")
|
||||
|
||||
If a `DownloadRequest` is cancelled or interrupted, the underlying URL session may generate resume data for the active `DownloadRequest`. If this happens, the resume data can be re-used to restart the `DownloadRequest` where it left off. The resume data can be accessed through the download response, then reused when trying to restart the request.
|
||||
|
||||
> **IMPORTANT:** 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 see this Stack Overflow [post](http://stackoverflow.com/a/39347461/1342462).
|
||||
|
||||
```swift
|
||||
class ImageRequestor {
|
||||
private var resumeData: Data?
|
||||
@@ -711,7 +713,7 @@ class ImageRequestor {
|
||||
|
||||
When sending relatively small amounts of data to a server using JSON or URL encoded parameters, the `Alamofire.request` APIs are usually sufficient. If you need to send much larger amounts of data from a file URL or an `InputStream`, then the `Alamofire.upload` APIs are what you want to use.
|
||||
|
||||
> The `Alamofire.upload` APIs should also be used if you need to upload data while your app is in the background. For more information, please see the [Session Manager Configurations](#session-manager-configurations) section.
|
||||
> The `Alamofire.upload` APIs should also be used if you need to upload data while your app is in the background. For more information, please see the [Session Manager Configurations](#session-manager) section.
|
||||
|
||||
#### Uploading Data
|
||||
|
||||
@@ -1289,8 +1291,12 @@ class OAuth2Handler: RequestAdapter, RequestRetrier {
|
||||
.responseJSON { [weak self] response in
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
if let json = response.result.value as? [String: String] {
|
||||
completion(true, json["access_token"], json["refresh_token"])
|
||||
if
|
||||
let json = response.result.value as? [String: Any],
|
||||
let accessToken = json["access_token"] as? String,
|
||||
let refreshToken = json["refresh_token"] as? String
|
||||
{
|
||||
completion(true, accessToken, refreshToken)
|
||||
} else {
|
||||
completion(false, nil, nil)
|
||||
}
|
||||
|
||||
Generated
+10
@@ -132,6 +132,16 @@ public enum AFError: Error {
|
||||
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 {
|
||||
|
||||
Generated
+9
@@ -222,6 +222,13 @@ public func download(
|
||||
/// 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.
|
||||
@@ -435,6 +442,7 @@ public func upload(
|
||||
///
|
||||
/// - 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)
|
||||
}
|
||||
@@ -449,6 +457,7 @@ public func stream(withHostName hostName: String, port: Int) -> StreamRequest {
|
||||
///
|
||||
/// - 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)
|
||||
}
|
||||
|
||||
+61
-1
@@ -199,7 +199,39 @@ public struct URLEncoding: ParameterEncoding {
|
||||
var allowedCharacterSet = CharacterSet.urlQueryAllowed
|
||||
allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
|
||||
|
||||
return string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
|
||||
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.substring(with: range)
|
||||
|
||||
escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? substring
|
||||
|
||||
index = endIndex
|
||||
}
|
||||
}
|
||||
|
||||
return escaped
|
||||
}
|
||||
|
||||
private func query(_ parameters: [String: Any]) -> String {
|
||||
@@ -289,6 +321,34 @@ public struct JSONEncoding: ParameterEncoding {
|
||||
|
||||
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: -
|
||||
|
||||
Generated
+72
-25
@@ -110,6 +110,9 @@ open class Request {
|
||||
/// 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?
|
||||
@@ -351,13 +354,25 @@ open class DataRequest: Request {
|
||||
let urlRequest: URLRequest
|
||||
|
||||
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||
let urlRequest = try self.urlRequest.adapt(using: adapter)
|
||||
return queue.syncResult { session.dataTask(with: urlRequest) }
|
||||
do {
|
||||
let urlRequest = try self.urlRequest.adapt(using: adapter)
|
||||
return queue.syncResult { 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 }
|
||||
|
||||
@@ -438,22 +453,37 @@ open class DownloadRequest: Request {
|
||||
case resumeData(Data)
|
||||
|
||||
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||
let task: URLSessionTask
|
||||
do {
|
||||
let task: URLSessionTask
|
||||
|
||||
switch self {
|
||||
case let .request(urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.downloadTask(with: urlRequest) }
|
||||
case let .resumeData(resumeData):
|
||||
task = queue.syncResult { session.downloadTask(withResumeData: resumeData) }
|
||||
switch self {
|
||||
case let .request(urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.downloadTask(with: urlRequest) }
|
||||
case let .resumeData(resumeData):
|
||||
task = queue.syncResult { session.downloadTask(withResumeData: resumeData) }
|
||||
}
|
||||
|
||||
return task
|
||||
} catch {
|
||||
throw AdaptError(error: error)
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
}
|
||||
|
||||
// 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 }
|
||||
|
||||
@@ -471,7 +501,7 @@ open class DownloadRequest: Request {
|
||||
NotificationCenter.default.post(
|
||||
name: Notification.Name.Task.DidCancel,
|
||||
object: self,
|
||||
userInfo: [Notification.Key.Task: task]
|
||||
userInfo: [Notification.Key.Task: task as Any]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -528,26 +558,42 @@ open class UploadRequest: DataRequest {
|
||||
case stream(InputStream, URLRequest)
|
||||
|
||||
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
|
||||
let task: URLSessionTask
|
||||
do {
|
||||
let task: URLSessionTask
|
||||
|
||||
switch self {
|
||||
case let .data(data, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.uploadTask(with: urlRequest, from: data) }
|
||||
case let .file(url, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.uploadTask(with: urlRequest, fromFile: url) }
|
||||
case let .stream(_, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.uploadTask(withStreamedRequest: urlRequest) }
|
||||
switch self {
|
||||
case let .data(data, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.uploadTask(with: urlRequest, from: data) }
|
||||
case let .file(url, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.uploadTask(with: urlRequest, fromFile: url) }
|
||||
case let .stream(_, urlRequest):
|
||||
let urlRequest = try urlRequest.adapt(using: adapter)
|
||||
task = queue.syncResult { session.uploadTask(withStreamedRequest: urlRequest) }
|
||||
}
|
||||
|
||||
return task
|
||||
} catch {
|
||||
throw AdaptError(error: error)
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
}
|
||||
|
||||
// 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 }
|
||||
|
||||
@@ -577,6 +623,7 @@ open class UploadRequest: DataRequest {
|
||||
#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)
|
||||
|
||||
Generated
+12
-3
@@ -38,13 +38,17 @@ public struct DefaultDataResponse {
|
||||
/// 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?
|
||||
|
||||
init(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) {
|
||||
init(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?, timeline: Timeline = Timeline()) {
|
||||
self.request = request
|
||||
self.response = response
|
||||
self.data = data
|
||||
self.error = error
|
||||
self.timeline = timeline
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +68,7 @@ public struct DataResponse<Value> {
|
||||
/// The result of response serialization.
|
||||
public let result: Result<Value>
|
||||
|
||||
/// The timeline of the complete lifecycle of the `Request`.
|
||||
/// The timeline of the complete lifecycle of the request.
|
||||
public let timeline: Timeline
|
||||
|
||||
var _metrics: AnyObject?
|
||||
@@ -139,6 +143,9 @@ public struct DefaultDownloadResponse {
|
||||
/// 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?
|
||||
|
||||
init(
|
||||
@@ -147,7 +154,8 @@ public struct DefaultDownloadResponse {
|
||||
temporaryURL: URL?,
|
||||
destinationURL: URL?,
|
||||
resumeData: Data?,
|
||||
error: Error?)
|
||||
error: Error?,
|
||||
timeline: Timeline = Timeline())
|
||||
{
|
||||
self.request = request
|
||||
self.response = response
|
||||
@@ -155,6 +163,7 @@ public struct DefaultDownloadResponse {
|
||||
self.destinationURL = destinationURL
|
||||
self.resumeData = resumeData
|
||||
self.error = error
|
||||
self.timeline = timeline
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-24
@@ -84,6 +84,22 @@ public struct DownloadResponseSerializer<Value>: DownloadResponseSerializerProto
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Timeline
|
||||
|
||||
extension Request {
|
||||
var timeline: Timeline {
|
||||
let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
|
||||
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
|
||||
|
||||
return Timeline(
|
||||
requestStartTime: self.startTime ?? CFAbsoluteTimeGetCurrent(),
|
||||
initialResponseTime: initialResponseTime,
|
||||
requestCompletedTime: requestCompletedTime,
|
||||
serializationCompletedTime: CFAbsoluteTimeGetCurrent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Default
|
||||
|
||||
extension DataRequest {
|
||||
@@ -101,7 +117,8 @@ extension DataRequest {
|
||||
request: self.request,
|
||||
response: self.response,
|
||||
data: self.delegate.data,
|
||||
error: self.delegate.error
|
||||
error: self.delegate.error,
|
||||
timeline: self.timeline
|
||||
)
|
||||
|
||||
dataResponse.add(self.delegate.metrics)
|
||||
@@ -136,22 +153,12 @@ extension DataRequest {
|
||||
self.delegate.error
|
||||
)
|
||||
|
||||
let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
|
||||
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
|
||||
|
||||
let timeline = Timeline(
|
||||
requestStartTime: self.startTime ?? CFAbsoluteTimeGetCurrent(),
|
||||
initialResponseTime: initialResponseTime,
|
||||
requestCompletedTime: requestCompletedTime,
|
||||
serializationCompletedTime: CFAbsoluteTimeGetCurrent()
|
||||
)
|
||||
|
||||
var dataResponse = DataResponse<T.SerializedObject>(
|
||||
request: self.request,
|
||||
response: self.response,
|
||||
data: self.delegate.data,
|
||||
result: result,
|
||||
timeline: timeline
|
||||
timeline: self.timeline
|
||||
)
|
||||
|
||||
dataResponse.add(self.delegate.metrics)
|
||||
@@ -184,7 +191,8 @@ extension DownloadRequest {
|
||||
temporaryURL: self.downloadDelegate.temporaryURL,
|
||||
destinationURL: self.downloadDelegate.destinationURL,
|
||||
resumeData: self.downloadDelegate.resumeData,
|
||||
error: self.downloadDelegate.error
|
||||
error: self.downloadDelegate.error,
|
||||
timeline: self.timeline
|
||||
)
|
||||
|
||||
downloadResponse.add(self.delegate.metrics)
|
||||
@@ -219,16 +227,6 @@ extension DownloadRequest {
|
||||
self.downloadDelegate.error
|
||||
)
|
||||
|
||||
let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
|
||||
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime
|
||||
|
||||
let timeline = Timeline(
|
||||
requestStartTime: self.startTime ?? CFAbsoluteTimeGetCurrent(),
|
||||
initialResponseTime: initialResponseTime,
|
||||
requestCompletedTime: requestCompletedTime,
|
||||
serializationCompletedTime: CFAbsoluteTimeGetCurrent()
|
||||
)
|
||||
|
||||
var downloadResponse = DownloadResponse<T.SerializedObject>(
|
||||
request: self.request,
|
||||
response: self.response,
|
||||
@@ -236,7 +234,7 @@ extension DownloadRequest {
|
||||
destinationURL: self.downloadDelegate.destinationURL,
|
||||
resumeData: self.downloadDelegate.resumeData,
|
||||
result: result,
|
||||
timeline: timeline
|
||||
timeline: self.timeline
|
||||
)
|
||||
|
||||
downloadResponse.add(self.delegate.metrics)
|
||||
|
||||
+55
-15
@@ -108,16 +108,53 @@ open class SessionDelegate: NSObject {
|
||||
#if !os(watchOS)
|
||||
|
||||
/// Overrides default behavior for URLSessionStreamDelegate method `urlSession(_:readClosedFor:)`.
|
||||
open var streamTaskReadClosed: ((URLSession, URLSessionStreamTask) -> Void)?
|
||||
@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:)`.
|
||||
open var streamTaskWriteClosed: ((URLSession, URLSessionStreamTask) -> Void)?
|
||||
@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:)`.
|
||||
open var streamTaskBetterRouteDiscovered: ((URLSession, URLSessionStreamTask) -> Void)?
|
||||
@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:)`.
|
||||
open var streamTaskDidBecomeInputAndOutputStreams: ((URLSession, URLSessionStreamTask, InputStream, OutputStream) -> Void)?
|
||||
@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
|
||||
|
||||
@@ -166,17 +203,19 @@ open class SessionDelegate: NSObject {
|
||||
#endif
|
||||
|
||||
#if !os(watchOS)
|
||||
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
|
||||
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
|
||||
|
||||
@@ -637,6 +676,7 @@ extension SessionDelegate: URLSessionDownloadDelegate {
|
||||
|
||||
#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.
|
||||
///
|
||||
|
||||
+143
-32
@@ -231,12 +231,14 @@ open class SessionManager {
|
||||
headers: HTTPHeaders? = nil)
|
||||
-> DataRequest
|
||||
{
|
||||
var originalRequest: URLRequest?
|
||||
|
||||
do {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
|
||||
originalRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
|
||||
return request(encodedURLRequest)
|
||||
} catch {
|
||||
return request(failedWith: error)
|
||||
return request(originalRequest, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,9 +250,11 @@ open class SessionManager {
|
||||
///
|
||||
/// - returns: The created `DataRequest`.
|
||||
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
|
||||
var originalRequest: URLRequest?
|
||||
|
||||
do {
|
||||
let originalRequest = try urlRequest.asURLRequest()
|
||||
let originalTask = DataRequest.Requestable(urlRequest: originalRequest)
|
||||
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))
|
||||
@@ -261,15 +265,29 @@ open class SessionManager {
|
||||
|
||||
return request
|
||||
} catch {
|
||||
return request(failedWith: error)
|
||||
return request(originalRequest, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private - Request Implementation
|
||||
|
||||
private func request(failedWith error: Error) -> DataRequest {
|
||||
let request = DataRequest(session: session, requestTask: .data(nil, nil), error: error)
|
||||
if startRequestsImmediately { request.resume() }
|
||||
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
|
||||
}
|
||||
|
||||
@@ -308,7 +326,7 @@ open class SessionManager {
|
||||
let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
|
||||
return download(encodedURLRequest, to: destination)
|
||||
} catch {
|
||||
return download(failedWith: error)
|
||||
return download(nil, to: destination, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +352,7 @@ open class SessionManager {
|
||||
let urlRequest = try urlRequest.asURLRequest()
|
||||
return download(.request(urlRequest), to: destination)
|
||||
} catch {
|
||||
return download(failedWith: error)
|
||||
return download(nil, to: destination, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,6 +366,13 @@ open class SessionManager {
|
||||
///
|
||||
/// 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.
|
||||
@@ -372,23 +397,43 @@ open class SessionManager {
|
||||
{
|
||||
do {
|
||||
let task = try downloadable.task(session: session, adapter: adapter, queue: queue)
|
||||
let request = DownloadRequest(session: session, requestTask: .download(downloadable, task))
|
||||
let download = DownloadRequest(session: session, requestTask: .download(downloadable, task))
|
||||
|
||||
request.downloadDelegate.destination = destination
|
||||
download.downloadDelegate.destination = destination
|
||||
|
||||
delegate[task] = request
|
||||
delegate[task] = download
|
||||
|
||||
if startRequestsImmediately { request.resume() }
|
||||
if startRequestsImmediately { download.resume() }
|
||||
|
||||
return request
|
||||
return download
|
||||
} catch {
|
||||
return download(failedWith: error)
|
||||
return download(downloadable, to: destination, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func download(failedWith error: Error) -> DownloadRequest {
|
||||
let download = DownloadRequest(session: session, requestTask: .download(nil, nil), error: error)
|
||||
if startRequestsImmediately { download.resume() }
|
||||
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
|
||||
}
|
||||
|
||||
@@ -418,7 +463,7 @@ open class SessionManager {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
return upload(fileURL, with: urlRequest)
|
||||
} catch {
|
||||
return upload(failedWith: error)
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +481,7 @@ open class SessionManager {
|
||||
let urlRequest = try urlRequest.asURLRequest()
|
||||
return upload(.file(fileURL, urlRequest))
|
||||
} catch {
|
||||
return upload(failedWith: error)
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,7 +509,7 @@ open class SessionManager {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
return upload(data, with: urlRequest)
|
||||
} catch {
|
||||
return upload(failedWith: error)
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,7 +527,7 @@ open class SessionManager {
|
||||
let urlRequest = try urlRequest.asURLRequest()
|
||||
return upload(.data(data, urlRequest))
|
||||
} catch {
|
||||
return upload(failedWith: error)
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,7 +555,7 @@ open class SessionManager {
|
||||
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
|
||||
return upload(stream, with: urlRequest)
|
||||
} catch {
|
||||
return upload(failedWith: error)
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,7 +573,7 @@ open class SessionManager {
|
||||
let urlRequest = try urlRequest.asURLRequest()
|
||||
return upload(.stream(stream, urlRequest))
|
||||
} catch {
|
||||
return upload(failedWith: error)
|
||||
return upload(nil, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,6 +659,8 @@ open class SessionManager {
|
||||
let formData = MultipartFormData()
|
||||
multipartFormData(formData)
|
||||
|
||||
var tempFileURL: URL?
|
||||
|
||||
do {
|
||||
var urlRequestWithContentType = try urlRequest.asURLRequest()
|
||||
urlRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type")
|
||||
@@ -637,6 +684,8 @@ open class SessionManager {
|
||||
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
|
||||
@@ -652,16 +701,37 @@ open class SessionManager {
|
||||
|
||||
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: self.upload(fileURL, with: urlRequestWithContentType),
|
||||
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)) }
|
||||
}
|
||||
}
|
||||
@@ -684,13 +754,26 @@ open class SessionManager {
|
||||
|
||||
return upload
|
||||
} catch {
|
||||
return upload(failedWith: error)
|
||||
return upload(uploadable, failedWith: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func upload(failedWith error: Error) -> UploadRequest {
|
||||
let upload = UploadRequest(session: session, requestTask: .upload(nil, nil), error: error)
|
||||
if startRequestsImmediately { upload.resume() }
|
||||
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
|
||||
}
|
||||
|
||||
@@ -709,6 +792,7 @@ open class SessionManager {
|
||||
///
|
||||
/// - 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))
|
||||
}
|
||||
@@ -723,12 +807,14 @@ open class SessionManager {
|
||||
///
|
||||
/// - 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)
|
||||
@@ -744,6 +830,7 @@ open class SessionManager {
|
||||
}
|
||||
}
|
||||
|
||||
@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() }
|
||||
@@ -762,6 +849,7 @@ open class SessionManager {
|
||||
|
||||
request.delegate.task = task // resets all task delegate data
|
||||
|
||||
request.retryCount += 1
|
||||
request.startTime = CFAbsoluteTimeGetCurrent()
|
||||
request.endTime = nil
|
||||
|
||||
@@ -769,8 +857,31 @@ open class SessionManager {
|
||||
|
||||
return true
|
||||
} catch {
|
||||
request.delegate.error = error
|
||||
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
|
||||
}
|
||||
|
||||
let retrySucceeded = strongSelf.retry(request)
|
||||
|
||||
if retrySucceeded, let task = request.task {
|
||||
strongSelf.delegate[task] = request
|
||||
} else {
|
||||
if strongSelf.startRequestsImmediately { request.resume() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+26
-22
@@ -33,13 +33,16 @@ open class TaskDelegate: NSObject {
|
||||
/// The serial operation queue used to execute all operations after the task completes.
|
||||
open 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? {
|
||||
didSet { reset() }
|
||||
}
|
||||
|
||||
var data: Data? { return nil }
|
||||
var error: Error?
|
||||
|
||||
var initialResponseTime: CFAbsoluteTime?
|
||||
var credential: URLCredential?
|
||||
var metrics: AnyObject? // URLSessionTaskMetrics
|
||||
@@ -331,29 +334,30 @@ class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate {
|
||||
{
|
||||
temporaryURL = location
|
||||
|
||||
if let destination = destination {
|
||||
let result = destination(location, downloadTask.response as! HTTPURLResponse)
|
||||
let destination = result.destinationURL
|
||||
let options = result.options
|
||||
guard
|
||||
let destination = destination,
|
||||
let response = downloadTask.response as? HTTPURLResponse
|
||||
else { return }
|
||||
|
||||
do {
|
||||
destinationURL = destination
|
||||
let result = destination(location, response)
|
||||
let destinationURL = result.destinationURL
|
||||
let options = result.options
|
||||
|
||||
if options.contains(.removePreviousFile) {
|
||||
if FileManager.default.fileExists(atPath: destination.path) {
|
||||
try FileManager.default.removeItem(at: destination)
|
||||
}
|
||||
}
|
||||
self.destinationURL = destinationURL
|
||||
|
||||
if options.contains(.createIntermediateDirectories) {
|
||||
let directory = destination.deletingLastPathComponent()
|
||||
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
|
||||
try FileManager.default.moveItem(at: location, to: destination)
|
||||
} catch {
|
||||
self.error = error
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+5
-5
@@ -1,20 +1,20 @@
|
||||
PODS:
|
||||
- Alamofire (4.0.1)
|
||||
- Alamofire (4.2.0)
|
||||
- BRLOptionParser (0.3.1)
|
||||
- GCDWebServer (3.3.3):
|
||||
- GCDWebServer/Core (= 3.3.3)
|
||||
- GCDWebServer/Core (3.3.3)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Alamofire (~> 4.0)
|
||||
- Alamofire (~> 4.2.0)
|
||||
- BRLOptionParser (~> 0.3.1)
|
||||
- GCDWebServer (~> 3.0)
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Alamofire: 7682d43245de14874acd142ec137b144aa1dd335
|
||||
Alamofire: aa2e09d871c9160ac53c90e83c68064a94e3dfbe
|
||||
BRLOptionParser: a03256a8ff003ca1f5376c55f55f210e085a3958
|
||||
GCDWebServer: 1c39a1f0763e4eb492bee021e4270fce097d3555
|
||||
|
||||
PODFILE CHECKSUM: fe577fe89bbdf6636ba715331bf8d97bbbee45bb
|
||||
PODFILE CHECKSUM: 8e98b2fb2d6be56882a44b2ff9241a800101e1d4
|
||||
|
||||
COCOAPODS: 1.1.0.rc.2
|
||||
COCOAPODS: 1.1.1
|
||||
|
||||
Generated
+522
-522
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,6 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
||||
|
||||
FOUNDATION_EXPORT double AlamofireVersionNumber;
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.0.1</string>
|
||||
<string>4.2.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
||||
#import "GCDWebServer.h"
|
||||
#import "GCDWebServerConnection.h"
|
||||
|
||||
+2
-8
@@ -23,12 +23,6 @@ case "${TARGETED_DEVICE_FAMILY}" in
|
||||
;;
|
||||
esac
|
||||
|
||||
realpath() {
|
||||
DIRECTORY="$(cd "${1%/*}" && pwd)"
|
||||
FILENAME="${1##*/}"
|
||||
echo "$DIRECTORY/$FILENAME"
|
||||
}
|
||||
|
||||
install_resource()
|
||||
{
|
||||
if [[ "$1" = /* ]] ; then
|
||||
@@ -70,7 +64,7 @@ EOM
|
||||
xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
|
||||
;;
|
||||
*.xcassets)
|
||||
ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH")
|
||||
ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
|
||||
XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
|
||||
;;
|
||||
*)
|
||||
@@ -93,7 +87,7 @@ 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 != "`realpath $PODS_ROOT`*" ]]; then
|
||||
if [[ $line != "${PODS_ROOT}*" ]]; then
|
||||
XCASSET_FILES+=("$line")
|
||||
fi
|
||||
done <<<"$OTHER_XCASSETS"
|
||||
|
||||
+2
@@ -1,4 +1,6 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
||||
|
||||
FOUNDATION_EXPORT double Pods_ShadowsocksX_NGVersionNumber;
|
||||
|
||||
Generated
+2
-8
@@ -23,12 +23,6 @@ case "${TARGETED_DEVICE_FAMILY}" in
|
||||
;;
|
||||
esac
|
||||
|
||||
realpath() {
|
||||
DIRECTORY="$(cd "${1%/*}" && pwd)"
|
||||
FILENAME="${1##*/}"
|
||||
echo "$DIRECTORY/$FILENAME"
|
||||
}
|
||||
|
||||
install_resource()
|
||||
{
|
||||
if [[ "$1" = /* ]] ; then
|
||||
@@ -70,7 +64,7 @@ EOM
|
||||
xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
|
||||
;;
|
||||
*.xcassets)
|
||||
ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH")
|
||||
ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
|
||||
XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
|
||||
;;
|
||||
*)
|
||||
@@ -93,7 +87,7 @@ 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 != "`realpath $PODS_ROOT`*" ]]; then
|
||||
if [[ $line != "${PODS_ROOT}*" ]]; then
|
||||
XCASSET_FILES+=("$line")
|
||||
fi
|
||||
done <<<"$OTHER_XCASSETS"
|
||||
|
||||
+2
@@ -1,4 +1,6 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
||||
|
||||
FOUNDATION_EXPORT double Pods_ShadowsocksX_NGTestsVersionNumber;
|
||||
|
||||
Generated
+1
-1
@@ -16,7 +16,7 @@
|
||||
<key>FooterText</key>
|
||||
<string>(The MIT License)
|
||||
|
||||
Copyright © 2013-2015 Stephen Celis (<stephen@stephencelis.com>)
|
||||
Copyright © 2013-2015 Stephen Celis (<stephen@stephencelis.com>)
|
||||
|
||||
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
|
||||
|
||||
+2
-8
@@ -23,12 +23,6 @@ case "${TARGETED_DEVICE_FAMILY}" in
|
||||
;;
|
||||
esac
|
||||
|
||||
realpath() {
|
||||
DIRECTORY="$(cd "${1%/*}" && pwd)"
|
||||
FILENAME="${1##*/}"
|
||||
echo "$DIRECTORY/$FILENAME"
|
||||
}
|
||||
|
||||
install_resource()
|
||||
{
|
||||
if [[ "$1" = /* ]] ; then
|
||||
@@ -70,7 +64,7 @@ EOM
|
||||
xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
|
||||
;;
|
||||
*.xcassets)
|
||||
ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH")
|
||||
ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
|
||||
XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
|
||||
;;
|
||||
*)
|
||||
@@ -93,7 +87,7 @@ 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 != "`realpath $PODS_ROOT`*" ]]; then
|
||||
if [[ $line != "${PODS_ROOT}*" ]]; then
|
||||
XCASSET_FILES+=("$line")
|
||||
fi
|
||||
done <<<"$OTHER_XCASSETS"
|
||||
|
||||
@@ -585,7 +585,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
shellScript = "diff \"${PODS_ROOT}/../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";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3545247EFCD033C3FA63EA6C /* [CP] Check Pods Manifest.lock */ = {
|
||||
@@ -600,7 +600,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
shellScript = "diff \"${PODS_ROOT}/../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";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3E819BD46B855EDB116A3C70 /* [CP] Copy Pods Resources */ = {
|
||||
@@ -660,7 +660,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
shellScript = "diff \"${PODS_ROOT}/../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";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
D8C2D6AF002916F4095E15E3 /* [CP] Copy Pods Resources */ = {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "79C040AFDDCE1BCBF6D8B5EB0B85887F"
|
||||
BlueprintIdentifier = "88E9EC28B8B46C3631E6B242B50F4442"
|
||||
BuildableName = "Alamofire.framework"
|
||||
BlueprintName = "Alamofire"
|
||||
ReferencedContainer = "container:Pods/Pods.xcodeproj">
|
||||
@@ -28,7 +28,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7AD154F318B10A340D705FD3003EAAC6"
|
||||
BlueprintIdentifier = "3CB2B8006B2B1ACAF9ADCA1DC82E2290"
|
||||
BuildableName = "libBRLOptionParser.a"
|
||||
BlueprintName = "BRLOptionParser"
|
||||
ReferencedContainer = "container:Pods/Pods.xcodeproj">
|
||||
@@ -42,7 +42,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F69966EE760BB16DE7009DBE04D8B18D"
|
||||
BlueprintIdentifier = "76AB1BA247F8CB57FCB2BA6577D40FE6"
|
||||
BuildableName = "Pods_ShadowsocksX_NG.framework"
|
||||
BlueprintName = "Pods-ShadowsocksX-NG"
|
||||
ReferencedContainer = "container:Pods/Pods.xcodeproj">
|
||||
@@ -56,7 +56,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9D7253CF594B2D7CB8127F7CCAFF916F"
|
||||
BlueprintIdentifier = "339CC546E4A1696296EFEDC2FA79ADE0"
|
||||
BuildableName = "Pods_ShadowsocksX_NGTests.framework"
|
||||
BlueprintName = "Pods-ShadowsocksX-NGTests"
|
||||
ReferencedContainer = "container:Pods/Pods.xcodeproj">
|
||||
@@ -70,7 +70,7 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3F0FDAFB5467946C22EE9F2EC643E2B0"
|
||||
BlueprintIdentifier = "05800FAF969AA3CD7F8AEB1C36988B53"
|
||||
BuildableName = "libPods-proxy_conf_helper.a"
|
||||
BlueprintName = "Pods-proxy_conf_helper"
|
||||
ReferencedContainer = "container:Pods/Pods.xcodeproj">
|
||||
|
||||
Reference in New Issue
Block a user