Alamofire3.+

重点来源:翻译自官方github

添加依赖

  1. CocoaPods

    pod 'Alamofire', '~> 5.0.0.beta.1'

Alamofire 4.0使用

ps 写该文时官方最新文档还没有出,so...

Alamofire 4.0是Alamofire的最新主要版本,它是一个用于iOS、tvOS、macOS和watchOS的HTTP网络库,使用Swift编写。作为一个主要的版本,遵循语义版本约定,4.0引入了api破坏的更改。

提供此指南是为了使用Alamofire 3简化现有应用程序的转换。x到最新的api,以及说明设计和结构的新功能和更新。

环境支持

  • iOS 8.0+, macOS 10.10.0+, tvOS 9.0+ and watchOS 2.0+
  • Xcode 8.1+
  • Swift 3.0+

4.0优点

  • 完整的Swift 3兼容性:包括完全采用新的API设计指南。
  • 新错误系统:使用新的AFError类型来遵守SE-0112中提出的新模式
  • 新的RequestAdapter协议:允许在实例化请求之前检查和调整每个URLRequest,允许轻松修改授权头等属性。
  • 新的RequestRetrier协议:允许在必要时检查和重试任何失败的请求,允许围绕一组请求构建自定义身份验证解决方案(OAuth1、OAuth2、xAuth、Basic Auth等)。
  • 新的参数编码协议:替换参数编码枚举,允许更容易的扩展和定制,并在失败时抛出错误,而不是返回元组。
  • 新的请求类型:包括DataRequest、DownloadRequest、UploadRequest和StreamRequest,它们实现每个请求类型的特定进程、验证和序列化api以及行为。
  • 新进度api:包括同时支持Progress和Int64类型的downloadProgress和uploadProgress api,并调用一个默认为.main的指定调度队列。
  • 增强的响应验证:现在包括数据或temporaryURL和destinationURL,如果验证失败,允许内联闭包解析服务器数据以获取错误消息。
  • 新的下载目的地:通过禁用文件系统上的move操作、删除前一个文件和创建中间目录,允许您完全控制文件系统上的move操作。
  • 新的响应类型:统一响应API签名,并公开临时url和downloadURL属性,以便在较新的平台上进行下载和所有新的任务指标。

新版本变化

Alamofire 4完全采用了所有新的Swift 3更改和约定,包括新的API设计指南。因此,Alamofire中的几乎所有API都以某种方式进行了修改。我们不可能记录每一个更改,因此我们将尝试确定最常见的api以及它们是如何更改的,以帮助您克服有时没有多大帮助的编译器错误。

命名空间变化

一些公共类已被移到全局命名空间中,以使它们更容易使用,并使它们成为第一类类型。

  • Manager is now SessionManager
  • Request.TaskDelegate is now TaskDelegate
  • Request.DataTaskDelegate is now DataTaskDelegate
  • Request.DownloadTaskDelegate is now DownloadTaskDelegate
  • Request.UploadTaskDelegate is now UploadTaskDelegate

创建请求

由于发出请求肯定是Alamofire中最常见的操作,下面是Alamofire 3的一些示例。x请求与它们在Alamofire 4中的新等价物进行比较。

Data Request - Simple with URL string
// Alamofire 3
Alamofire.request(.GET, urlString).response { request, response, data, error in
    print(request)
    print(response)
    print(data)
    print(error)
}

// Alamofire 4
Alamofire.request(urlString).response { response in // method defaults to `.get`
    debugPrint(response)
}
Data Request - Complex with URL string
// Alamofire 3
let parameters: [String: AnyObject] = ["foo": "bar"]

Alamofire.request(.GET, urlString, parameters: parameters, encoding: .JSON)
    .progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
        print("Bytes: \(bytesRead), Total Bytes: \(totalBytesRead), Total Bytes Expected: \(totalBytesExpectedToRead)")
    }
    .validate { request, response in
        // Custom evaluation closure (no access to server data)
        return .success
    }
    .responseJSON { response in
        debugPrint(response)
    }

// Alamofire 4
let parameters: Parameters = ["foo": "bar"]

Alamofire.request(urlString, method: .get, parameters: parameters, encoding: JSONEncoding.default)
    .downloadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
        print("Progress: \(progress.fractionCompleted)")
    }
    .validate { request, response, data in
        // Custom evaluation closure now includes data (allows you to parse data to dig out error messages if necessary)
        return .success
    }
    .responseJSON { response in
        debugPrint(response)
    }
Download Request - Simple with URL string
// Alamofire 3
let destination = DownloadRequest.suggestedDownloadDestination()

Alamofire.download(.GET, urlString, destination: destination).response { request, response, data, error in
    // What is fileURL...not easy to get
    print(request)
    print(response)
    print(data)
    print(error)
}

// Alamofire 4
let destination = DownloadRequest.suggestedDownloadDestination()

Alamofire.download(urlString, to: destination).response { response in // method defaults to `.get`
    print(response.request)
    print(response.response)
    print(response.temporaryURL)
    print(response.destinationURL)
    print(response.error)
}
Download Request - Simple with URL request
// Alamofire 3
let destination = DownloadRequest.suggestedDownloadDestination()

Alamofire.download(urlRequest, destination: destination).validate().responseData { response in
    // What is fileURL...not easy to get
    debugPrint(response)
}

// Alamofire 4
Alamofire.download(urlRequest, to: destination).validate().responseData { response in
    debugPrint(response)
    print(response.temporaryURL)
    print(response.destinationURL)
}
Download Request - Complex with URL string
// Alamofire 3
let fileURL: NSURL
let destination: Request.DownloadFileDestination = { _, _ in fileURL }
let parameters: [String: AnyObject] = ["foo": "bar"]

Alamofire.download(.GET, urlString, parameters: parameters, encoding: .JSON, to: destination)
    .progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
        print("Bytes: \(bytesRead), Total Bytes: \(totalBytesRead), Total Bytes Expected: \(totalBytesExpectedToRead)")
    }
    .validate { request, response in
        // Custom evaluation implementation (no access to temporary or destination URLs)
        return .success
    }
    .responseJSON { response in
        print(fileURL) // Only accessible if captured in closure scope, not ideal
        debugPrint(response)
    }

// Alamofire 4
let fileURL: URL
let destination: DownloadRequest.DownloadFileDestination = { _, _ in 
    return (fileURL, [.createIntermediateDirectories, .removePreviousFile]) 
}
let parameters: Parameters = ["foo": "bar"]

Alamofire.download(urlString, method: .get, parameters: parameters, encoding: JSONEncoding.default, to: destination)
    .downloadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
        print("Progress: \(progress.fractionCompleted)")
    }
    .validate { request, response, temporaryURL, destinationURL in
        // Custom evaluation closure now includes file URLs (allows you to parse out error messages if necessary)
        return .success
    }
    .responseJSON { response in
        debugPrint(response)
        print(response.temporaryURL)
        print(response.destinationURL)
    }
Upload Request - Simple with URL string
// Alamofire 3
Alamofire.upload(.POST, urlString, data: data).response { request, response, data, error in
    print(request)
    print(response)
    print(data)
    print(error)
}

// Alamofire 4
Alamofire.upload(data, to: urlString).response { response in // method defaults to `.post`
    debugPrint(response)
}
Upload Request - Simple with URL request
// Alamofire 3
Alamofire.upload(urlRequest, file: fileURL).validate().responseData { response in
    debugPrint(response)
}

// Alamofire 4
Alamofire.upload(fileURL, with: urlRequest).validate().responseData { response in
    debugPrint(response)
}
Upload Request - Complex with URL string
// Alamofire 3
Alamofire.upload(.PUT, urlString, file: fileURL)
    .progress { bytes, totalBytes, totalBytesExpected in
        // Are these for upload or for downloading the response?
        print("Bytes: \(bytesRead), Total Bytes: \(totalBytesRead), Total Bytes Expected: \(totalBytesExpectedToRead)")
    }
    .validate { request, response in
        // Custom evaluation implementation (no access to server data)
        return .success
    }
    .responseJSON { response in
        debugPrint(response)
    }

// Alamofire 4
Alamofire.upload(fileURL, to: urlString, method: .put)
    .uploadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
        print("Upload Progress: \(progress.fractionCompleted)")
    }
    .downloadProgress { progress in // called on main queue by default
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .validate { request, response, data in
        // Custom evaluation closure now includes data (allows you to parse data to dig out error messages if necessary)
        return .success
    }
    .responseJSON { response in
        debugPrint(response)
    }

URLStringConvertible协议变化

URLConvertible的变化

命名修改

//Alamofire 3.X版本
public protocol URLStringConvertible {
    var URLString: String { get }
}
//Alamofire 4版本
public protocol URLConvertible {
    func asURL() throws -> URL
}

修改原因:

Alamofire中一个非常常见的问题是,用户忘记按百分比转义URL字符串,Alamofire将崩溃。到目前为止,我们(Alamofire团队)一直认为这就是Alamofire的设计方式,您的url需要符合RFC 2396。这对于社区来说当然不理想,因为我们都希望Alamofire告诉我们,我们的URL是无效的,而不是崩溃。

新的URLConvertible协议。Alamofire之前无法安全处理无效URL字符串的原因实际上是由于URLStringConvertible上缺乏安全性。Alamofire不可能知道如何智能地使无效的URL字符串有效。因此,如果无法从URLConvertible创建URL,则为AFError。抛出invalidURL错误。

这个更改(以及其他许多更改)允许Alamofire安全地处理无效url并在响应处理程序中报告错误。

URLRequest一致性变化

URLRequest不再符合URLStringConvertible协议,现在是URLConvertible协议。在以前的Alamofire版本中,这总是有点牵强,实际上没有必要。它还很有可能在许多Alamofire api中引入歧义。由于这些原因,URLRequest不再符合URLStringConvertible(现在是URLConvertible)。

下面的操作不再支持:

let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
let urlString = urlRequest.urlString

取而代之的是:

let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
let urlString = urlRequest.url?.absoluteString

关于PR-1505点这里

新功能

1. Request Adapter

public protocol RequestAdapter {
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest
}

它允许在创建SessionManager之前对每个请求进行检查和调整。使用适配器的一种非常特定的方式是将授权头附加到特定类型的身份验证后的请求。

class AccessTokenAdapter: RequestAdapter {
    private let accessToken: String

    init(accessToken: String) {
        self.accessToken = accessToken
    }

    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        var urlRequest = urlRequest

        if urlRequest.urlString.hasPrefix("https://httpbin.org") {
            urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
        }

        return urlRequest
    }
}

let sessionManager = SessionManager()
sessionManager.adapter = AccessTokenAdapter(accessToken: "1234")

sessionManager.request("https://httpbin.org/get")

如果在调整过程中发生错误,应该抛出错误,并将其提交到请求的响应处理程序中。
可以查看PR-1450更多内容

2. Request Retrier(请求检索)

public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void

public protocol RequestRetrier {
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
}

它允许在执行时遇到错误的请求重试,如果指定了可选的延迟。

class OAuth2Handler: RequestAdapter, RequestRetrier {
    public func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: RequestRetryCompletion) {
        if let response = request.task.response as? HTTPURLResponse, response.statusCode == 401 {
            completion(true, 1.0) // retry after 1 second
        } else {
            completion(false, 0.0) // don't retry
        }
    }
}

let sessionManager = SessionManager()
sessionManager.retrier = OAuth2Handler()

sessionManager.request(urlString).responseJSON { response in
    debugPrint(response)
}

检索器允许您在请求完成后检查它,并运行所有验证闭包以确定是否应该重试。当同时使用RequestAdapter和RequestRetrier协议时,可以为OAuth1、OAuth2、Basic Auth甚至指数级后退重试策略创建凭据刷新系统。可能性是无限的。有关此主题的更多信息和示例,请参阅README。
更多信息请查看 PR-1391PR-1450

3. Task Metrics

在iOS、tvOS 10和macOS 10.12中,苹果推出了新的URLSessionTaskMetrics api。任务指标封装了关于请求和响应执行的一些奇妙的统计信息。这个API非常类似于Alamofire的时间轴,但是提供了更多Alamofire无法计算的统计信息。我们对这些api感到非常兴奋,并在每种响应类型上公开了它们,这意味着它们不容易使用。

Alamofire.request(urlString).response { response in
    debugPrint(response.metrics)
}

需要注意的是,这些api只在iOS、tvOS 10+和macOS 10.12+上可用。因此,根据您的部署目标,您可能需要使用以下内部可用性检查:

Alamofire.request(urlString).response { response in
    if #available(iOS 10.0, *) {
        debugPrint(response.metrics)
    }
}

更多请查看 PR-1492

更新的功能

Alamofire 4包含许多新特性和对现有特性的增强。本节简要概述这些特性并演示它们的用途。有关每一个的更多信息,请参考链接的拉请求

Errors

Alamofire 4包含一个全新的错误系统,它采用了 SE-0112 中提出的新模式。新错误系统的核心是AFError,这是一种由五种主要情况支持的新错误类型枚举。

  • .invalidurl (url: URLConvertible)——当URLConvertible类型无法创建有效url时返回。
  • .parameterencodingfailed(reason:ParameterEncodingFailureReason) -当参数编码对象在编码过程中抛出错误时返回。
  • .multipartencodingfailed(reason:MultipartEncodingFailureReason) -当多部分编码过程中的某个步骤失败时返回。
  • .responsevalidationfailed(reason:ResponseValidationFailureReason)——当validate()调用失败时返回。
  • .responseserializationfailed(reason:ResponseSerializationFailureReason)——当响应序列化器在序列化过程中遇到错误时返回。

每个案例都包含一个特定的失败原因,这是另一个嵌套枚举,其中有多个案例包含关于所发生错误的确切类型的额外信息。这最终意味着在Alamofire中更容易识别错误的来源和处理方法。

Alamofire.request(urlString).responseJSON { response in
    guard case let .failure(error) = response.result else { return }

    if let error = error as? AFError {
        switch error {
        case .invalidURL(let url):
            print("Invalid URL: \(url) - \(error.localizedDescription)")
        case .parameterEncodingFailed(let reason):
            print("Parameter encoding failed: \(error.localizedDescription)")
            print("Failure Reason: \(reason)")
        case .multipartEncodingFailed(let reason):
            print("Multipart encoding failed: \(error.localizedDescription)")
            print("Failure Reason: \(reason)")
        case .responseValidationFailed(let reason):
            print("Response validation failed: \(error.localizedDescription)")
            print("Failure Reason: \(reason)")

            switch reason {
            case .dataFileNil, .dataFileReadFailed:
                print("Downloaded file could not be read")
            case .missingContentType(let acceptableContentTypes):
                print("Content Type Missing: \(acceptableContentTypes)")
            case .unacceptableContentType(let acceptableContentTypes, let responseContentType):
                print("Response content type: \(responseContentType) was unacceptable: \(acceptableContentTypes)")
            case .unacceptableStatusCode(let code):
                print("Response status code was unacceptable: \(code)")
            }
        case .responseSerializationFailed(let reason):
            print("Response serialization failed: \(error.localizedDescription)")
            print("Failure Reason: \(reason)")
        }

        print("Underlying error: \(error.underlyingError)")
    } else if let error = error as? URLError {
        print("URLError occurred: \(error)")
    } else {
        print("Unknown error: \(error)")
    }
}

这种新设计允许您尽可能深入地研究错误,以便找出继续进行的最佳方法。它还使开发人员不必到处处理NSError类型。通过切换到Alamofire中的自定义错误类型,我们可以简化结果和响应泛型类型,只需要一个泛型参数。这简化了响应序列化逻辑。
更多请查看 PR-1419

Parameter Encoding Protocol

到目前为止,参数编码枚举已经为我们提供了两年多的良好服务。然而,它有一些限制,我们想在Alamofire 4中解决。

  • .url根据HTTP方法选择目的地,所以它总是有点令人困惑在处理.url案例的行为时
  • .urlencodedinurl案例一直是一个令人头疼的问题。
  • .json和.propertylist编码不能接受格式化或写入选项。
    自定义编码对于用户来说有点难掌握。

由于这些原因,我们决定在Alamofire 4中完全消除枚举!现在,ParameterEncoding是由三个具体的URLEncoding、JSONEncoding和PropertyList编码结构支持的协议,它们具有一个新的参数类型别名,用于创建参数字典。

public typealias Parameters = [String: Any]

public protocol ParameterEncoding {
    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest
}

URL Encoding

新的URLEncoding结构包含一个目标枚举,支持三种类型的目标:

  • .methodDependent——将已编码的查询字符串结果应用于现有的查询字符串,用于GET、HEAD和DELETE请求,并将set作为使用任何其他HTTP方法的请求的HTTP主体
  • .querystring—设置或追加已编码的查询字符串结果到现有查询字符串。
  • .httpbody—将已编码的查询字符串结果设置为URL请求的HTTP体。

这些目的地使得控制将参数编码到URLRequest上的位置变得容易得多。在参数编码方面,创建请求仍然使用与以前相同的签名,并且具有相同的默认行为。

let parameters: Parameters = ["foo": "bar"]

Alamofire.request(urlString, parameters: parameters) // Encoding => URLEncoding(destination: .methodDependent)
Alamofire.request(urlString, parameters: parameters, encoding: URLEncoding(destination: .queryString))
Alamofire.request(urlString, parameters: parameters, encoding: URLEncoding(destination: .httpBody))

// Static convenience properties (we'd like to encourage everyone to use this more concise form)
Alamofire.request(urlString, parameters: parameters, encoding: URLEncoding.default)
Alamofire.request(urlString, parameters: parameters, encoding: URLEncoding.queryString)
Alamofire.request(urlString, parameters: parameters, encoding: URLEncoding.httpBody)

JSON Encoding

新的JSONEncoding结构公开了定制JSON编写选项的能力

let parameters: Parameters = ["foo": "bar"]

Alamofire.request(urlString, parameters: parameters, encoding: JSONEncoding(options: []))
Alamofire.request(urlString, parameters: parameters, encoding: JSONEncoding(options: .prettyPrinted))

// Static convenience properties (we'd like to encourage everyone to use this more concise form)
Alamofire.request(urlString, parameters: parameters, encoding: JSONEncoding.default)
Alamofire.request(urlString, parameters: parameters, encoding: JSONEncoding.prettyPrinted)

Property List Encoding

新的PropertyListEncoding结构允许定制plist格式和write选项。

let parameters: Parameters = ["foo": "bar"]

Alamofire.request(urlString, parameters: parameters, encoding: PropertyListEncoding(format: .xml, options: 0))
Alamofire.request(urlString, parameters: parameters, encoding: PropertyListEncoding(format: .binary, options: 0))

// Static convenience properties (we'd like to encourage everyone to use this more concise form)
Alamofire.request(urlString, parameters: parameters, encoding: PropertyListEncoding.xml)
Alamofire.request(urlString, parameters: parameters, encoding: PropertyListEncoding.binary)

Custom Encoding

创建自定义自定义参数编码现在与实现协议一样简单。有关如何做到这一点的更多示例,请参阅README。更多请查看 PR-1465

Request 子类

在 Alamofire 4中,request, download, upload 和 stream API不再返回 Request 对象。相反,它们返回特定类型的 Request 的子类。原因:

  • Progress:progress方法的行为对于上传请求来说是混乱的。
  • Response Serializers:是为数据和上传请求而设计的,而不是为下载或流请求而设计的。

在高版本上,Alamofire 4现在有四个请求子类,每个子类都支持自己的自定义链接api。这允许每个子类创建针对特定类型请求的扩展。

open class Request {
    // Contains common properties, authentication and state methods as well as
    // CustomStringConvertible and CustomDebugStringConvertible conformance
}

open class DataRequest: Request {
    // Contains stream (not to be confused with StreamRequest) and download progress methods.
}

open class DownloadRequest: Request {
    // Contains download destination and options, resume data and download progress methods.
}

open class UploadRequest: DataRequest {
    // Inherits all DataRequest APIs and also contains upload progress methods.
}

open class StreamRequest: Request {
    // Only inherits Request APIs, there are no other custom APIs at this time.
}

通过这种分割,Alamofire 4能够为每种类型的请求创建定制的链接api。这提供了各种各样的可能性,但是让我们花一点时间来关注一下这个变化在进度报告和下载目的地方面意味着什么。

Download and Upload Progress(上传和下载进度)

数据、下载和上传请求的进度报告系统已经完全重新设计。每个请求类型都包含通过返回底层的进度实例在每次进度更新期间执行闭包的进度api。闭包将在默认为main的指定队列上调用。

Data Request Progress
Alamofire.request(urlString)
    .downloadProgress { progress in
        // Called on main dispatch queue by default
        print("Download progress: \(progress.fractionCompleted)")
    }
    .responseJSON { response in
        debugPrint(response)
    }
Download Request Progress
Alamofire.download(urlString, to: destination)
    .downloadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
        // Called on utility dispatch queue
        print("Download progress: \(progress.fractionCompleted)")
    }
    .responseJSON { response in
        debugPrint(response)
    }
Upload Request Progress
Alamofire.upload(data, to: urlString, withMethod: .post)
    .uploadProgress { progress in
        // Called on main dispatch queue by default
        print("Upload progress: \(progress.fractionCompleted)")
    }
    .downloadProgress { progress in
        // Called on main dispatch queue by default
        print("Download progress: \(progress.fractionCompleted)")
    }
    .responseData { response in
        debugPrint(response)
    }

下载指定文件

Alamofire 3.x,成功的下载请求总是将临时文件移动到目的地闭包提供的最终目的地URL。虽然这是一个很好的方便,但它有几个限制:

  • 强制性 -强制提供一个目标闭包来移动文件,即使不想移动
  • 限制性 -在移动文件之前,无法调整文件系统。

这些限制导致了Alamofire 4中的一些增强。第一个是目标闭包的可选性。现在,默认情况下,目标闭包为nil,这意味着文件不会移动到文件系统的任何位置,并且会返回临时URL。

Alamofire.download(urlString).responseData { response in
    print("Temporary URL: \(response.temporaryURL)")
}

下载选项

另一个主要的变化是在目标闭包中添加了下载选项,允许对移动操作进行更多的文件系统控制。为此,创建了DownloadOptions类型并将其添加到DownloadFileDestination闭包中。

public typealias DownloadFileDestination = (
    _ temporaryURL: URL,
    _ response: HTTPURLResponse)
    -> (destinationURL: URL, options: DownloadOptions)

目前支持的可选项:

  • .createintermediatedirectory—如果指定,则为目标URL创建中间目录。
  • .removepreviousfile -如果指定,从目标URL中删除前一个文件。
let destination: DownloadRequest.DownloadFileDestination = { _, _ in 
    return (fileURL, [.removePreviousFile, .createIntermediateDirectories]) 
}

Alamofire.download(urlString, to: destination).response { response in
    debugPrint(response)
}

如果在文件系统操作期间发生错误,DownloadResponse上的错误类型为URLError。

Response Validation(响应确认)

在Alamofire 4中有几个改进响应验证系统的机会领域。这些领域包括:

  • 将底层数据公开给验证闭包。
  • 不同请求子类类型之间的自定义验证允许为下载请求公开temporaryURL和destinationURL。

通过创建请求子类,验证闭包类型别名和请求api能够针对每个请求类型进行定制。

Data Request

在DataRequest(通过UploadRequest继承)上公开的验证闭包现在如下所示:

extension DataRequest {
    public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult
}

通过公开 Data? 属性,您不再需要根据请求编写扩展来访问它。现在你可以这样做:

Alamofire.request(urlString)
    .validate { request, response, data in
        guard let data = data else { return .failure(customError) }

        // 1) Validate the response to make sure everything looks good
        // 2) If validation fails, you can now parse the error message out of the
        //    data if necessary and add that to your custom error if you wish.

        return .success
    }
    .response { response in
        debugPrint(response)
    }

Download Request

DownloadRequest上的验证闭包非常类似于DataRequest API,但更适合于下载。

extension DownloadRequest {
    public typealias Validation = (
        _ request: URLRequest?, 
        _ response: HTTPURLResponse, 
        _ temporaryURL: URL?, 
        _ destinationURL: URL?) 
        -> ValidationResult
}

temporaryURL和destinationURL参数现在允许您直接在内联闭包中访问服务器返回的数据。这允许您在确定需要创建自定义错误时检查文件中的数据。

Alamofire.download(urlString)
    .validate { request, response, temporaryURL, destinationURL in
        guard let fileURL = temporaryURL else { return .failure(customError) }

        do {
            let _ = try Data(contentsOf: fileURL)
            return .success
        } catch {
            return .failure(customError)
        }
    }
    .response { response in
        debugPrint(response)
    }

通过将底层服务器数据直接暴露给内联闭包,可以在验证闭包中解析这些响应中嵌入的错误消息,以创建包括服务器错误消息在内的自定义错误。如果负载与响应序列化器闭包中使用的模式相同,则可以调用响应序列化器来解析错误消息,而不是复制逻辑。有关如何做到这一点的示例,请参阅README。

Response Serializers(响应序列化器)

Alamofire 3.x中的响应序列化系统有几个非常严重的限制:

  • 响应序列化api可以应用于下载和流请求,但会导致未定义的行为。
  • 响应API返回4个参数,而不是封装响应类型。

在Alamofire 3.x中这个系统有一些非常强大的限制。因此,在Alamofire 4中,请求类型首先被分解为子类,这为为特定类型的请求创建定制的响应序列化器和api提供了机会。在深入讨论响应序列化器之前,我们应该先浏览一下新的响应类型。

Default Data Response

DefaultDataResponse表示未序列化的服务器响应。没有发生Alamofire处理,它只是从SessionDelegate api收集所有响应信息,并以一个简单的结构返回。

public struct DefaultDataResponse {
    public let request: URLRequest?
    public let response: HTTPURLResponse?
    public let data: Data?
    public let error: Error?
    public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
}

这是您将从 DataRequest.response API返回的响应类型

Alamofire.request(urlString).response { response in
    debugPrint(response)
}

Alamofire.upload(file, to: urlString).response { response in
    debugPrint(response)
}
Data Response

泛型DataResponse类型与Alamofire 3中的泛型响应相同。但是重构后包含了新的metrics属性。

public struct DataResponse<Value> {
    public let request: URLRequest?
    public let response: HTTPURLResponse?
    public let data: Data?
    public let result: Result<Value>
    public let timeline: Timeline
    public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
}

您仍然可以访问DataRequest和UploadRequest类型上与以前相同的响应序列化api。

Alamofire.request(urlString).responseJSON { response in
    debugPrint(response)
    print(response.result.isSuccess)
}

Alamofire.upload(fileURL, to: urlString).responseData { response in
    debugPrint(response)
    print(response.result.isSuccess)
}
Default Download Response

由于下载与数据和上传请求的工作方式不同,Alamofire 4包含定制的下载响应类型,以适应其行为。DefaultDownloadResponse类型表示DownloadRequest的非序列化服务器响应,DownloadRequest将所有SessionDelegate信息收集到一个简单的结构中。

public struct DefaultDownloadResponse {
    public let request: URLRequest?
    public let response: HTTPURLResponse?
    public let temporaryURL: URL?
    public let destinationURL: URL?
    public let resumeData: Data?
    public let error: Error?
    public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
}

在使用新的DownloadRequest.response API 时,将返回DefaultDownloadResponse类型。

Alamofire.download(urlString).response { response in
    debugPrint(response)
    print(response.temporaryURL)
}
Download Response

新的通用DownloadResponse类型类似于通用DataResponse类型,但包含为下载请求定制的信息。DownloadResponse类型在DownloadRequest类型上公开的四个新api之一时返回。这些新api与DataRequest api匹配,并通过从底层临时URL或目标URL加载数据提供相同的功能。

Alamofire.download(urlString, to: destination)
    .responseData { response in
        debugPrint(response)
    }
    .responseString { response in
        debugPrint(response)
    }
    .responseJSON { response in
        debugPrint(response)
    }
    .responsePropertyList { response in
        debugPrint(response)
    }

这些新的响应序列化api使将请求下载到文件并在单个调用中序列化响应变得容易得多。

自定义响应序列化器

如果您已经创建了自己的自定义响应序列化器,您可能希望跨数据和下载请求扩展支持,就像我们对Alamofire响应序列化器所做的那样。如果您决定这样做,那么仔细看看Alamofire是如何通过将响应序列化器实现移动到请求来共享这两种请求类型之间的响应序列化器实现的。这使我们能够耗尽逻辑以避免类型之间的重复。
查看更多: PR-1457

推荐阅读更多精彩内容