Session
- 请求
- 使用
RequestInterceptor
适应和重试请求 - 安全
- 子类化服务器信任策略管理器
- 自定义缓存和重定向处理
- 使用
EventMonitor
s - 发出请求
- 响应处理
- 网络可达性
高级用法
Alamofire建立在URLSession
和基础URL加载系统之上。为了充分利用此框架,建议您熟悉基础网络堆栈的概念和功能。
推荐读物
Session
Alamofire的Session
职责与其维护的URLSession
实例大致相同:它提供API以产生封装不同URLSessionTask
子类的各种Request
子类,以及封装应用于实例所产生的所有Request
的各种配置。
Session
提供一个default
单例实例,该实例为AF
enum名称空间中的顶级API 提供动力。因此,以下两个语句是等效的:
AF.request("https://httpbin.org/get")
let session = Session.default
session.request("https://httpbin.org/get")
创建自定义Session
实例
大多数应用程序将需要以各种方式来自定义Session
实例的行为。实现此目的的最简单方法是使用以下便捷初始化程序,并将结果存储在整个应用中使用的单例中。
public convenience init(configuration: URLSessionConfiguration = URLSessionConfiguration.af.default,
delegate: SessionDelegate = SessionDelegate(),
rootQueue: DispatchQueue = DispatchQueue(label: "org.alamofire.session.rootQueue"),
startRequestsImmediately: Bool = true,
requestQueue: DispatchQueue? = nil,
serializationQueue: DispatchQueue? = nil,
interceptor: RequestInterceptor? = nil,
serverTrustManager: ServerTrustManager? = nil,
redirectHandler: RedirectHandler? = nil,
cachedResponseHandler: CachedResponseHandler? = nil,
eventMonitors: [EventMonitor] = [])
此初始化程序允许自定义所有基本Session
行为。
用URLSessionConfiguration
创建一个Session
为了自定义基础URLSession
行为,可以提供自定义URLSessionConfiguration
实例。推荐从URLSessionConfiguration.af.default
实例开始,因为它增加了由Alamofire提供的默认Accept-Encoding
,Accept-Language
以及User-Agent
头文件,但任何URLSessionConfiguration
都可以使用。
let configuration = URLSessionConfiguration.af.default
configuration.allowsCellularAccess = false
let session = Session(configuration: configuration)
不推荐在
URLSessionConfiguration
设置Authorization
或Content-Type
头。而是使用提供的headers
API ,ParameterEncoder
,或RequestAdapter
,将它们添加到Request
中。
正如Apple在其文档中所指出的那样,将实例添加到中
URLSession
(或在Alamofire的情况下,用于初始化一个Session
)之后对URLSessionConfiguration
属性进行更改无效。
SessionDelegate
一个SessionDelegate
实例封装不同的URLSessionDelegate
所有操作和相关的协议回调。SessionDelegate
还为Alamofire产生的每个Request
充当SessionStateDelegate
角色,从而允许Request
间接导入由Session
实例创建的状态。SessionDelegate
可以被自定义以特定FileManager
实例,该实例将用于任何磁盘访问,例如访问UploadRequest
s 上传的文件或DownloadRequest
s 下载的文件。
let delegate = SessionDelegate(fileManager: .default)
startRequestsImmediately
默认情况下,一旦添加了至少一个响应处理,Session
它将立即在一个Request
上面调用resume()
。设置startRequestsImmediately
为false
需要所有Request
都手动调用resume()
。
let session = Session(startRequestsImmediately: false)
一个Session
的DispatchQueue
默认情况下,Session
实例为所有异步工作使用单个DispatchQueue
实例。这包括underlyingQueue
(URLSession
的delegate
OperationQueue
),为所有的URLRequest
创建,所有响应系列化工作,所有内部Session
和Request
状态突变。如果围绕URLRequest
创建或响应序列化(response serialization),性能分析显示了特定瓶颈,则可以为每个工作领域提供单独的DispatchQueue
给Session
。
let rootQueue = DispatchQueue(label: "com.app.session.rootQueue")
let requestQueue = DispatchQueue(label: "com.app.session.requestQueue")
let serializationQueue = DispatchQueue(label: "com.app.session.serializationQueue")
let session = Session(rootQueue: rootQueue,
requestQueue: requestQueue,
serializationQueue: serializationQueue)
任何提供的自定义rootQueue
必须是串行队列中,但requestQueue
和serializationQueue
可以是串行或并行的队列。建议使用串行队列作为默认队列,除非性能分析显示工作被延迟(在这种情况下,使队列并行可以提高整体性能)。
添加一个 RequestInterceptor
Alamofire的RequestInterceptor
协议(RequestAdapter & RequestRetrier
)提供了重要而强大的请求自适应和重试功能。它可以同时在Session
和Request
级别应用。有关RequestInterceptor
Alamofire的详细信息以及各种实现RetryPolicy
,请参见下文。
let policy = RetryPolicy()
let session = Session(interceptor: policy)
添加一个 ServerTrustManager
Alamofire的ServerTrustManager
类封装了域和ServerTrustEvaluating
-conforming类型的实例之间的映射,从而提供了自定义TLS安全性Session
处理的功能。这包括证书的使用和公钥固定以及证书吊销检查。有关更多信息,请参见关于ServerTrustManager
和的部分ServerTrustEvaluating
。初始化一个 ServerTrustManger
就像提供域和要执行的评估类型之间的映射一样简单:
let manager = ServerTrustManager(evaluators: ["httpbin.org": PinnedCertificatesTrustEvaluator()])
let session = Session(serverTrustManager: manager)
有关评估服务器信任的更多详细信息,请参阅下面的详细文档。
添加一个 RedirectHandler
Alamofire的RedirectHandler
协议可自定义HTTP重定向响应的处理。它可以同时在Session
和Request
级别应用。Alamofire包括遵循RedirectHandler
的Redirector
类型,并提供对重定向的简单控制。有关更多详细信息RedirectHandler
,请参见下面的详细文档。
let redirector = Redirector(behavior: .follow)
let session = Session(redirectHandler: redirector)
添加一个 CachedResponseHandler
Alamofire的CachedResponseHandler
协议可自定义响应的缓存,并且可以在Session
和Request
级别应用。Alamofire包括遵循CachedResponseHandler
的ResponseCacher
类型,并提供对响应缓存的简单控制的。有关更多详细信息,请参见下面的详细文档。
let cacher = ResponseCacher(behavior: .cache)
let session = Session(cachedResponseHandler: cacher))
添加EventMonitor
Alamofire的EventMonitor
协议可提供有关Alamofire内部事件的强大见解。它可用于提供日志记录和其他基于事件的功能。在初始化时Session
接受一个EventMonitor
-conforming实例的数组。
let monitor = ClosureEventMonitor()
monitor.requestDidCompleteTaskWithError = { (request, task, error) in
debugPrint(request)
}
let session = Session(eventMonitors: [monitor])
从URLSession
创建实例
除了前面提到的convenience
初始化器外,Session
还可以直接从URLSession
初始化。但是,使用此初始化器时要牢记一些要求,因此建议使用便捷初始化器。这些包括:
- Alamofire不支持
URLSession
配置用于后台使用。因为Session
初始化时,这将导致运行时错误。 - 一个
SessionDelegate
实例必须被创建和被用为URLSession
的delegate
,以及传递给Session
初始化器。 - 自定义
OperationQueue
必须被传递为URLSession
的delegateQueue
。该队列必须是一个串行队列,它必须有一个后盾DispatchQueue
,而且DispatchQueue
必须传递给Session
它的rootQueue
。
let rootQueue = DispatchQueue(label: "org.alamofire.customQueue")
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
queue.underlyingQueue = rootQueue
let delegate = SessionDelegate()
let configuration = URLSessionConfiguration.af.default
let urlSession = URLSession(configuration: configuration,
delegate: delegate,
delegateQueue: queue)
let session = Session(session: urlSession, delegate: delegate, rootQueue: rootQueue)
请求
通过Alamofire执行的每个请求是由特定的类封装DataRequest
、UploadRequest
和DownloadRequest
。每个这些类的封装功能对每个类型的请求唯一,但DataRequest
和DownloadRequest
继承自共同父类,Request
(UploadRequest
继承自DataRequest
)。Request
实例从不直接被创建,而是通过各种request
方法之一从Session
实例出售。
请求管道
一旦Request
使用其初始参数或URLRequestConvertible
值创建了一个子类,它将通过构成Alamofire请求管道的一系列步骤传递。对于成功的请求,这些包括:
- 初始参数(例如HTTP方法,头和参数)被封装到内部
URLRequestConvertible
值中。如果URLRequestConvertible
直接传递值,则该值将不变使用。 asURLRequest()
在URLRequestConvertible
值上调用,创建第一个URLRequest
值。此值传递到Request
并存储在requests
中。如果URLRequestConvertible
值是从传递给Session
方法的参数被创建,则在URLRequest
被创建时,任何提供的RequestModifier
将被调用。- 如果有任何
Session
或Request
RequestAdapter
或RequestInterceptor
,他们用以前创建的URLRequest
来调用。适配的URLRequest
然后将被传递到Request
并存储在request
中。 Session
调用Request
来创建URLSessionTask
,以执行基于URLRequest
上面的网路请求。- 一旦
URLSessionTask
完成,URLSessionTaskMetrics
已经采集,Request
执行它的Validator
。 - Request执行任何响应处理,例如已附加的
responseDecodable
。
在这些步骤中的任何一个步骤中,都可以通过创建或接收的Error
值来指示失败,然后将其传递给关联的Request
。例如,除了步骤1和4外,上述所有步骤都可以创建一个Error
,然后将其传递到响应处理或可重试。以下是在整个Request
管道中可能失败或不失败的一些示例。
- 参数封装不会失败。
- 调用
asURLRequest()
时,任何URLRequestConvertible
值都会产生错误。这允许各种URLRequest
属性的初始验证或参数编码的失败。 RequestAdapter
可能在适应期间失败,可能是由于缺少授权令牌。URLSessionTask
创建不会失败。URLSessionTask
由于各种原因(包括网络可用性和取消)可能会出现错误。这些Error
值将传递回Request
。- 响应处理可以产生任何
Error
,通常是由于无效的响应或其他解析错误所致。
一旦将错误传递给Request
,Request
将会尝试运行与Session
或Request
关联的任何RequestRetrier
。如果RequestRetrier
选择重试Request
,则再次运行完整管道。RequestRetrier
也可以产生Error
,而不会触发重试。
Request
尽管Request
不封装任何特定类型的请求,但它包含Alamofire执行的所有请求共有的状态和功能。这包括:
状态
所有Request
类型都包含状态的概念,指示状态下Request
生命中的主要事件。
public enum State {
case initialized
case resumed
case suspended
case cancelled
case finished
}
Request
在创建后开始进入.initialized
状态。Request
可以通过调用适当的生命周期方法来暂停,恢复和取消。
resume()
恢复或启动Request
的网络流量。如果startRequestsImmediately
是true
,则将响应处理程序添加到中后,将自动调用此方法Request
。suspend()
暂停或暂停Request
和及其网络流量。Request
可以恢复此状态下的,但是只能DownloadRequests
继续传输数据。其他Request
将重新开始。cancel()
取消Request
。一旦处于此状态,aRequest
将无法恢复或暂停。当cancel()
被调用时,Request
的error
属性将与设置AFError.explicitlyCancelled
实例。如果aRequest
恢复并且以后不取消,则在.finished
所有响应验证器和响应序列化程序运行之后,它将达到状态。但是,如果Request
在达到.finished
状态后将附加的响应序列化器添加到该状态,它将转换回该.resumed
状态并再次执行网络请求。
进度
为了跟踪请求的进度,同时Request
提供 uploadProgress
和downloadProgress
属性以及基于闭包的uploadProgress
和downloadProgress
方法。像所有基于闭包的Request
API一样,进度API可以Request
与其他方法链接在一起。与其他基于闭包的API一样,也应_在_添加任何响应处理程序(如)_之前_将它们添加到请求中responseDecodable
。
AF.request(...)
.uploadProgress { progress in
print(progress)
}
.downloadProgress { progress in
print(progress)
}
.responseDecodable(of: SomeType.self) { response in
debugPrint(response)
}
重要的是,并非所有Request
子类都能够准确报告其进度,或者可能有其他依赖项来报告。
- 对于上传进度,可以通过以下方式确定进度:
- 按
Data
提供给的上载主体的对象的长度UploadRequest
。 - 由磁盘上作为的上载主体提供的文件的长度
UploadRequest
。 - 通过
Content-Length
请求上标头的值(如果已手动设置)。
- 按
- 对于下载进度,有一个要求:
- 服务器响应必须包含
Content-Length
标头。不幸的是,对于进度报告可能还有其他未记录的要求,URLSession
因此无法进行准确的进度报告。
- 服务器响应必须包含
处理重定向
Alamofire的RedirectHandler
协议可为的重定向处理提供控制和自定义Request
。除了per-Session
RedirectHandler
,每个人Request
都可以指定自己的名字,RedirectHandler
该名字优先于Session
。
let redirector = Redirector(behavior: .follow)
AF.request(...)
.redirect(using: redirector)
.responseDecodable(of: SomeType.self) { response in
debugPrint(response)
}
注意:只能
RedirectHandler
在上设置一个Request
。尝试设置多个将导致运行时异常。
自定义缓存
Alamofire的CachedResponseHandler
协议提供了对响应缓存的控制和自定义。除了per-Session
CachedResponseHandler
,每个人Request
都可以指定自己的名字,CachedResponseHandler
该名字优先于Session
。
let cacher = ResponseCacher(behavior: .cache)
AF.request(...)
.cacheResponse(using: cacher)
.responseDecodable(of: SomeType.self) { response in
debugPrint(response)
}
注意:只能
CachedResponseHandler
在上设置一个Request
。尝试设置多个将导致运行时异常。
证书
为了利用URLSession
所提供的自动凭据处理功能,Alamofire提供了基于Request
的API,以允许将URLCredential
实例自动添加到请求中。其中包括使用用户名和密码进行HTTP身份验证的便捷API,以及任何URLCredential
实例。
添加凭据以自动回复任何HTTP身份验证挑战很简单:
AF.request(...)
.authenticate(username: "user@example.domain", password: "password")
.responseDecodable(of: SomeType.self) { response in
debugPrint(response)
}
注意:此机制仅支持HTTP身份验证提示。如果请求要求
Authentication
所有请求都包含标头,则应将其直接提供(作为的一部分Request
)或通过提供RequestInterceptor
。
此外,添加原始文件URLCredential
也很容易:
let credential = URLCredential(...)
AF.request(...)
.authenticate(using: credential)
.responseDecodable(of: SomeType.self) { response in
debugPrint(response)
}
一个Request
的URLRequest
由一个Request
发出的每个网络请求最终都封装在一个URLRequest
值中,该值是根据传递给Session
请求方法之一的各种参数创建的。Request
会将这些URLRequest
的副本保留在其requests
array属性中。这些值包括根据传递的参数创建的初始URLRequest
以及由RequestInterceptor
创建的任何URLRequest
。但是,该数组不包括由代表Request
发出的由URLSessionTask
执行的URLRequest
。要检查这些值,该tasks
属性可以访问由Request
执行的所有URLSessionTasks
。
URLSessionTask
在许多方面,各种Request
子类都充当的包装器URLSessionTask
,提供用于与特定类型任务交互的特定API。这些任务Request
通过tasks
array属性在实例上可见。这包括为创建的初始任务Request
以及作为重试过程的一部分创建的任何后续任务,每个重试都有一个任务。
响应
请求完成后,每一个都Request
可能具有HTTPURLResponse
可用的值。仅当未取消请求且没有失败发出网络请求时,此值才可用。此外,如果重试该请求,则只有_最后一个_响应可用。中间响应可以从属性中的URLSessionTask
s 派生tasks
。
URLSessionTaskMetrics
Alamofire会URLSessionTaskMetrics
为每场URLSessionTask
表演收集值Request
。这些值在metrics
属性中可用,每个值对应于URLSessionTask
in tasks
中的相同索引。
URLSessionTaskMetrics
也可用于Alamofire的各种响应类型,例如DataResponse
。例如:
AF.request(...)
.responseDecodable(of: SomeType.self) { response in {
print(response.metrics)
}
由于
FB7624529
,URLSessionTaskMetrics
当前禁止在watchOS上进行收集。
DataRequest
DataRequest
是Request
子类,它封装一个URLSessionDataTask
下载服务器响应到内存中的Data
。因此,认识到下载量过大可能会对系统性能产生不利影响是重要的。对于那些类型的下载,建议使用DownloadRequest
将数据保存到磁盘。
附加状态
DataRequest
除了Request
所提供的属性外,还具有一些属性。这些包括data
,它是来自服务器的响应积集的Data
,还有convertible
,它是在DataRequest
创建时用到的URLRequestConvertible
,包含创建实例的原始参数。
验证方式
DataRequest
默认情况下不验证响应。相反,必须将validate()
调用添加到请求中,以验证各种属性是否有效。
public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> Result<Void, Error>
默认情况下,添加validate()
可确保响应状态代码在该200..<300
范围内,并且响应的Content-Type
与请求的Accept
值匹配。可以通过传递Validation
闭包来进一步自定义验证:
AF.request(...)
.validate { request, response, data in
...
}
DataStreamRequest
DataStreamRequest
是Request
一个子类,它封装一个 URLSessionDataTask
和来自一个HTTP连接超时的流Data
。
附加状态
DataStreamRequest
不包含任何其他公共状态。
验证方式
DataStreamRequest
s默认情况下不验证响应。相反,validate()
必须将调用添加到请求中,以验证各种属性是否有效。
public typealias Validation = (_ request: URLRequest?, _ response: HTTPURLResponse) -> Result<Void, Error>
默认情况下,添加validate()
可确保响应状态代码在该200..<300
范围内,并且响应Content-Type
的Accept
值与请求的值匹配。可以通过传递Validation
闭包来进一步自定义验证:
AF.request(...)
.validate { request, response in
...
}
UploadRequest
UploadRequest
是的子类,DataRequest
它封装URLSessionUploadTask
,将Data
值,文件上传到磁盘或InputStream
远程服务器。
附加状态
UploadRequest
除了所提供的属性外,还具有一些属性DataRequest
。其中包括一个FileManager
实例,该实例用于自定义在上载文件时对磁盘的访问,并且upload
,它封装了URLRequestConvertible
用于描述请求的值以及Uploadable
,它确定了要执行的上载类型。
DownloadRequest
DownloadRequest
是一个具体的子类,Request
其中封装了对磁盘的URLSessionDownloadTask
下载响应Data
。
附加状态
DownloadRequest
除了所提供的属性外,还具有一些属性Request
。这些措施包括resumeData
,将Data
产生取消时DownloadRequest
,可用于后继续下载,并且fileURL
,将URL
下载的文件可一旦下载完成,在这。
取消
除了支持所cancel()
提供的方法外Request
,DownloadRequest
还包括cancel(producingResumeData shouldProduceResumeData: Bool)
,resumeData
如果可能的话,还包括(可选)在取消时填充属性;以及cancel(byProducingResumeData completionHandler: @escaping (_ data: Data?) -> Void)
,将提供的恢复数据提供给传递的闭包。
AF.download(...)
.cancel { resumeData in
...
}
验证方式
DownloadRequest
由于验证数据已下载到磁盘,因此支持的验证版本与DataRequest
和稍有不同UploadRequest
。
public typealias Validation = (_ request: URLRequest?, _ response: HTTPURLResponse, _ fileURL: URL?)
不能Data
直接访问下载的文件,而必须使用fileURL
提供的文件进行访问。否则,DownloadRequest
的验证器的功能与的相同DataRequest
。
使用RequestInterceptor
适应和重试请求
Alamofire的RequestInterceptor
协议(RequestAdapter
和RequestRetrier
协议组成)激活强大的完全Session
和完全Request
能力。这些包括身份验证系统,在该系统中,将通用头添加到每个Request
,并Request
在授权到期时重试。此外,Alamofire包括一个内置RetryPolicy
类型,当由于各种常见的网络错误而导致请求失败时,它可以轻松重试。
RequestAdapter
Alamofire的 RequestAdapter
协议允许每个由Session
执行的URLRequest
操作都经过检查和转变,然后才能通过网络发布。适配器的一种非常常见的用法是在某种类型的身份验证后面向请求添加Authorization
头。
该RequestAdapter
协议仅有一必须条件:
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void)
其参数包括:
urlRequest
:由参数或用于创建Request
的URLRequestConvertible
值最初创建的URLRequest
。session
:这里的Session
,创建了Request
,为它调用适配器。completion
:必须 调用该异步completion回调来指示适配器已完成的。它具有异步特性,在通过网络发送Request
之前,能够使RequestAdapter
从网络或磁盘访问异步资源。提供给completion
闭包的Result
可以返回带有修改后的URLRequest
的一个值.success
,或一个值.failure
具有相关联的Error
,这将随后被用于失败Request
。例如,添加Authorization
头需要修改URLRequest
,然后调用completion回调。
let accessToken: String
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
var urlRequest = urlRequest
urlRequest.headers.add(.authorization(bearerToken: accessToken))
completion(.success(urlRequest))
}
RequestRetrier
Alamofire的RequestRetrier
协议允许Request
执行中遇到的Error
时进行重试。这包括在Alamofire 请求管道的任何阶段生成的Error
。
RequestRetrier
协议仅有一必须条件。
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void)
其参数包括:
request
:遇到错误的Request
。session
:管理Request
的Session
。error
:触发了重试尝试的Error
。通常是AFError
。completion
:必须 调用异步completion回调来指示Request
是否应重试的。必须使用RetryResult
来调用它。
该RetryResult
类型表示在RequestRetrier
中实现的任何逻辑的结果。定义为:
///确定是否需要重试的结果。
public enum RetryResult {
///应立即尝试重试。
case retry
///应该在关联的`TimeInterval`之后尝试重试。
case retryWithDelay(TimeInterval)
///不重试。
case doNotRetry
///由于相关的“错误”,请勿重试。
case doNotRetryWithError(Error)
}
例如,如果请求是幂等的,则Alamofire的RetryPolicy
类型将自动重试由于某种网络错误而失败的Request
。
open func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
if request.retryCount < retryLimit,
let httpMethod = request.request?.method,
retryableHTTPMethods.contains(httpMethod),
shouldRetry(response: request.response, error: error) {
let timeDelay = pow(Double(exponentialBackoffBase), Double(request.retryCount)) * exponentialBackoffScale
completion(.retryWithDelay(timeDelay))
} else {
completion(.doNotRetry)
}
}
使用多个RequestInterceptor
Alamofire支持使用多的RequestInterceptor
,在两者Session
和Request
水平上,通过使用Interceptor
型。Interceptor
可以由适配器和重试器闭包组成,单个RequestAdapter
和RequestRetrier
的组合,或RequestAdapter
,RequestRetrier
,和RequestInterceptor
数组的组合。
let adapter = // Some RequestAdapter
let retrier = // Some RequestRetrier
let interceptor = // Some RequestInterceptor
let adapterAndRetrier = Interceptor(adapter: adapter, retrier: retrier)
let composite = Interceptor(interceptors: [adapterAndRetrier, interceptor])
当由多个RequestAdapter
s 组成时,Interceptor
将依次调用每个RequestAdapter
。如果它们全部成功,则将使用RequestAdapter
链中的最后一个URLRequest
执行请求。如果失败,则适配将停止,并且Request
失败并返回错误。同样,当由多个RequestRetrier
组成时,重试的执行顺序与将重试器添加到实例的顺序相同,直到它们全部完成或其中一个因错误而失败。
AuthenticationInterceptor
Alamofire的AuthenticationInterceptor
类是一个旨在处理身份验证请求所涉及的队列和线程复杂性的RequestInterceptor
。它利用注入的Authenticator
协议来管理匹配AuthenticationCredential
的生命周期。这是一个简单的示例,说明如何与OAuthCredential
一起实现OAuthAuthenticator
类。
OAuthCredential
struct OAuthCredential: AuthenticationCredential {
let accessToken: String
let refreshToken: String
let userID: String
let expiration: Date
//如果在过期5分钟内需要刷新,则
var requiresRefresh: Bool { Date(timeIntervalSinceNow: 60 * 5) > expiration }
}
OAuthAuthenticator
class OAuthAuthenticator: Authenticator {
func apply(_ credential: OAuthCredential, to urlRequest: inout URLRequest) {
urlRequest.headers.add(.authorization(bearerToken: credential.accessToken))
}
func refresh(_ credential: OAuthCredential,
for session: Session,
completion: @escaping (Result<OAuthCredential, Error>) -> Void) {
//使用refresh token刷新credential...然后使用新credential调用completion。
//新的credential将自动存储在“ AuthenticationInterceptor”中。将来的请求将
//使用apply(_:to:)方法使用新credential进行身份验证。
}
func didRequest(_ urlRequest: URLRequest,
with response: HTTPURLResponse,
failDueToAuthenticationError error: Error) -> Bool {
//如果身份验证服务器无法使credentials无效,则返回`false`
return false
//如果身份验证服务器可以使凭据无效,则检查响应与
//身份验证服务器作为身份验证失败返回的
//内容相是否匹配。通常是带有自定义header值的一个401。
//返回response.statusCode == 401
}
func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: OAuthCredential) -> Bool {
//如果验证服务器无法使凭证无效,则返回 true
return true
//如果验证服务器可以使凭据无效,则将
// URLRequest中的
//“ Authorization”头值与使用“ Credential”访问令牌生成的Bearer令牌进行比较。
// let bearerToken = HTTPHeader.authorization(bearerToken: credential.accessToken).value
// return urlRequest.headers["Authorization"] == bearerToken
}
}
用法
//通常加载 从钥匙串如果它存在
let credential = OAuthCredential(accessToken: "a0",
refreshToken: "r0",
userID: "u0",
expiration: Date(timeIntervalSinceNow: 60 * 60))
// 创建拦截器
let authenticator = OAuthAuthenticator()
let interceptor = AuthenticationInterceptor(authenticator: authenticator,
credential: credential)
// 使用拦截器执行请求
let session = Session()
let urlRequest = URLRequest(url: URL(string: "https://api.example.com/example/user")!)
session.request(urlRequest, interceptor: interceptor)
安全
与服务器和Web服务进行通信时使用安全的HTTPS连接是保护敏感数据的重要步骤。默认情况下,Alamofire收到与相同的自动TLS证书和证书链验证URLSession
。虽然这可以确保证书链有效,但不能防止中间人(MITM)攻击或其他潜在漏洞。为了减轻MITM攻击,处理敏感客户数据或财务信息的应用程序应使用Alamofire ServerTrustEvaluating
协议提供的证书或公钥固定。
使用ServerTrustManager
和评估服务器信任ServerTrustEvaluating
ServerTrustEvaluating
该ServerTrustEvaluating
协议提供了一种执行任何类型的服务器信任评估的方法。它有一个要求:
func evaluate(_ trust: SecTrust, forHost host: String) throws
该方法提供了从底层获得的SecTrust
价值和主体String
,URLSession
并提供了进行各种评估的机会。
Alamofire包括许多不同类型的信任评估器,可对评估过程提供可组合的控制:
DefaultTrustEvaluator
:使用默认的服务器信任评估,同时允许您控制是否验证质询提供的主机。RevocationTrustEvaluator
:检查收到的证书的状态,以确保尚未被吊销。由于它需要网络请求,因此通常不会在每个请求上执行此操作。PinnedCertificatesTrustEvaluator
:使用提供的证书来验证服务器信任。如果固定证书之一与服务器证书之一匹配,则服务器信任被视为有效。该评估者还可以接受自签名证书。PublicKeysTrustEvaluator
:使用提供的公共密钥来验证服务器信任。如果固定的公用密钥之一与服务器证书公用密钥之一匹配,则认为服务器信任有效。CompositeTrustEvaluator
:计算ServerTrustEvaluating
值的数组,只有在所有值都成功时才成功。此类型可用于组合例如RevocationTrustEvaluator
和PinnedCertificatesTrustEvaluator
。DisabledTrustEvaluator
注意:此评估程序仅应在调试方案中使用,因为它会禁用所有评估,这反过来将始终将任何服务器信任视为有效。切勿在生产环境中使用该评估器!
ServerTrustManager
该ServerTrustManager
负责存储的内部映射ServerTrustEvaluating
值到特定的主机。这使Alamofire可以使用不同的评估程序评估每个主机。
let evaluators: [String: ServerTrustEvaluating] = [
//默认情况下,应用程序捆绑包中包含的证书会自动固定。
"cert.example.com": PinnedCertificatesTrustEvaluator(),
//默认情况下,会自动使用应用程序捆绑包中包含的证书的公钥。
"keys.example.com": PublicKeysTrustEvaluator(),
]
let manager = ServerTrustManager(evaluators: serverTrustPolicies)
这ServerTrustManager
将具有以下行为:
cert.example.com
将始终使用默认情况下启用证书锁定并启用主机验证的证书固定,因此需要满足以下条件才能使TLS握手成功:- 证书链_必须_有效。
- 证书链_必须_包含固定证书之一。
- 质询主机_必须_与证书链的叶证书中的主机匹配。
keys.example.com
将始终使用默认设置并启用主机验证的公钥固定,因此需要满足以下条件才能使TLS握手成功:- 证书链_必须_有效。
- 证书链_必须_包含固定的公共密钥之一。
- 质询主机_必须_与证书链的叶证书中的主机匹配。
- 对其他主机的请求将产生错误,因为
ServerTrustManager
默认情况下要求对所有主机进行评估。
子类化服务器信任策略管理器
如果您发现自己需要更灵活的服务器信任策略匹配行为(即通配符域),则使用子类ServerTrustManager
并serverTrustEvaluator(forHost:)
使用您自己的自定义实现覆盖该方法。
final class CustomServerTrustPolicyManager: ServerTrustPolicyManager {
override func serverTrustEvaluator(forHost host: String) -> ServerTrustEvaluating? {
var policy: ServerTrustPolicy?
//实现您的自定义域匹配行为...
return policy
}
}
应用程式传输安全性
通过在iOS 9中添加应用传输安全性(ATS),有可能对ServerTrustManager
多个ServerTrustEvaluating
对象使用自定义设置无效。如果持续CFNetwork SSLHandshake failed (-9806)
出现错误,则可能是您遇到了此问题。除非您在应用程序的plist中配置ATS设置以禁用足够多的ATS设置以允许您的应用程序评估服务器信任度,否则Apple的ATS系统将覆盖整个挑战系统。如果您遇到此问题(使用自签名证书的可能性很高),则可以通过在上添加NSAppTransportSecurity
替代来解决此问题Info.plist
。您可以使用该nscurl
工具的--ats-diagnostics
选项对主机执行一系列测试,以查看可能需要哪些ATS替代。
通过本地网络使用自签名证书
如果您尝试连接到在本地主机上运行的服务器,并且使用的是自签名证书,则需要将以下内容添加到中Info.plist
。
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
</dict>
根据Apple文档,将设置NSAllowsLocalNetworking
为YES
允许加载本地资源,而无需为应用程序的其余部分禁用ATS。
自定义缓存和重定向处理
URLSession
允许使用URLSessionDataDelegate
和URLSessionTaskDelegate
方法自定义缓存和重定向行为。Alamofire将这些自定义点显示为CachedResponseHandler
和RedirectHandler
协议。
CachedResponseHandler
该CachedResponseHandler
协议允许控制将HTTP响应缓存到URLCache
与Session
发出请求关联的实例中。该协议有一个单一要求:
func dataTask(_ task: URLSessionDataTask,
willCacheResponse response: CachedURLResponse,
completion: @escaping (CachedURLResponse?) -> Void)
从方法签名中可以看出,此控件仅适用于Request
使用基础URLSessionDataTask
进行网络传输的,其中包括DataRequest
s和UploadRequest
s(因为URLSessionUploadTask
是的子类URLSessionDataTask
)。考虑将响应用于缓存的条件非常广泛,因此最好查看该URLSessionDataDelegate
方法的文档urlSession(_:dataTask:willCacheResponse:completionHandler:)
。一旦考虑将响应用于缓存,便可以进行多种有价值的操作:
- 通过返回防止完全缓存响应
nil
CachedURLResponse
。 - 修改
CachedURLResponse
的storagePolicy
改变,其中的缓存值应该住。 URLResponse
直接修改基础,添加或删除值。- 修改
Data
与响应关联的关联(如果有)。
Alamofire包含ResponseCacher
符合的类型CachedResponseHandler
,从而易于缓存,而不是缓存或修改响应。ResponseCacher
取一个Behavior
值来控制缓存行为。
public enum Behavior {
///将缓存的响应存储在缓存中。
case cache
///防止将缓存的响应存储在缓存中。
case doNotCache
///修改缓存的响应,然后再将其存储在缓存中。
case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)
}
ResponseCacher
可以同时使用a Session
和Request
基础,如上所述。
RedirectHandler
该RedirectHandler
协议允许控制特定Request
s 的重定向行为。它有一个要求:
func task(_ task: URLSessionTask,
willBeRedirectedTo request: URLRequest,
for response: HTTPURLResponse,
completion: @escaping (URLRequest?) -> Void)
此方法提供了修改重定向URLRequest
或通过nil
以完全禁用重定向的机会。Alamofire提供了Redirector
符合的类型RedirectHandler
,从而易于遵循,不遵循或修改重定向的请求。Redirector
采用一个Behavior
值来控制重定向行为。
public enum Behavior {
///按照响应中定义的重定向。
case follow
不要遵循响应中定义的重定向。
case doNotFollow
///修改响应中定义的重定向请求。
case modify((URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?)
}
Redirector
可以同时使用a Session
和Request
基础,如上所述。
使用EventMonitor
s
该EventMonitor
协议允许观察和检查大量内部Alamofire事件。其中包括Alamofire实施的所有URLSessionDelegate
,URLSessionTaskDelegate
和URLSessionDownloadDelegate
方法,以及大量内部Request
事件。除了这些事件(默认情况下是不起作用的空方法)以外,EventMonitor
协议还要求DispatchQueue
在其上调度所有事件以保持性能。此DispatchQueue
默认为.main
,而是专用串行队列被推荐用于任何自定义符合类型。
Logging
EventMonitor
协议的最大用途也许是实现相关事件的记录。一个简单的实现可能看起来像这样:
final class Logger: EventMonitor {
let queue = DispatchQueue(label: ...)
//恢复任何类型的Request时调用的事件。
func requestDidResume(_ request: Request) {
print("Resuming: \(request)")
}
//每当DataRequest解析响应时调用的事件。
func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {
debugPrint("Finished: \(response)")
}
}
Logger
可以使用Session
与上述相同的方式将这种类型添加到中:
let logger = Logger()
let session = Session(eventMonitors: [logger])
发出请求
作为框架,Alamofire有两个主要目标:
- 为了轻松实现对原型和工具的网络请求
- 充当应用程序网络的通用基础
它通过使用功能强大的抽象,提供有用的默认值以及包含常见任务的实现来实现这些目标。但是,一旦对Alamofire的使用超出了一些要求,就必须超越高级的默认实现,而转向为特定应用程序定制的行为。Alamofire提供URLConvertible
和URLRequestConvertible
协议来帮助进行这种自定义。
URLConvertible
URLConvertible
可以将采用协议的类型用于构造URL,然后将其用于内部构造URL请求。String
,URL
以及URLComponents
符合URLConvertible
在默认情况下,允许任何人将其交给url
参数的request
,upload
和download
方法:
let urlString = "https://httpbin.org/get"
AF.request(urlString)
let url = URL(string: urlString)!
AF.request(url)
let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true)!
AF.request(urlComponents)
鼓励以重要方式与Web应用程序交互的应用程序具有自定义类型,以URLConvertible
作为将特定于域的模型映射到服务器资源的便捷方式。
URLRequestConvertible
采用该URLRequestConvertible
协议的类型可用于构造URLRequest
。URLRequest
符合URLRequestConvertible
默认情况下,允许它被传递到request
,upload
和download
直接的方法。Alamofire URLRequestConvertible
用作流经请求管道的所有请求的基础。URLRequest
建议直接使用s在Alamofire提供URLRequest
的ParameterEncoder
s 之外自定义创建的推荐方法。
let url = URL(string: "https://httpbin.org/post")!
var urlRequest = URLRequest(url: url)
urlRequest.method = .post
let parameters = ["foo": "bar"]
do {
urlRequest.httpBody = try JSONEncoder().encode(parameters)
} catch {
// Handle error.
}
urlRequest.headers.add(.contentType("application/json"))
AF.request(urlRequest)
鼓励以重要方式与Web应用程序进行交互的应用程序具有自定义类型,URLRequestConvertible
以确保请求的端点的一致性。这样的方法可以用来消除服务器端的不一致性,并提供类型安全的路由,以及管理其他状态。
路由请求
随着应用程序规模的扩大,在构建网络堆栈时采用通用模式非常重要。该设计的重要部分是如何路由您的请求。Alamofire URLConvertible
和URLRequestConvertible
协议以及Router
设计模式可为您提供帮助。
“路由器”是一种定义“路由”或请求组成部分的类型。这些组件可以包括的组成部分,URLRequest
发出请求所需的参数以及各种按请求的Alamofire设置。一个简单的路由器可能看起来像这样:
enum Router: URLRequestConvertible {
case get, post
var baseURL: URL {
return URL(string: "https://httpbin.org")!
}
var method: HTTPMethod {
switch self {
case .get: return .get
case .post: return .post
}
}
var path: String {
switch self {
case .get: return "get"
case .post: return "post"
}
}
func asURLRequest() throws -> URLRequest {
let url = baseURL.appendingPathComponent(path)
var request = URLRequest(url: url)
request.method = method
return request
}
}
AF.request(Router.get)
更复杂的路由器可能包括请求的参数。使用Alamofire的ParameterEncoder
协议和随附的编码器,Encodable
可以将任何类型的参数用作参数:
enum Router: URLRequestConvertible {
case get([String: String]), post([String: String])
var baseURL: URL {
return URL(string: "https://httpbin.org")!
}
var method: HTTPMethod {
switch self {
case .get: return .get
case .post: return .post
}
}
var path: String {
switch self {
case .get: return "get"
case .post: return "post"
}
}
func asURLRequest() throws -> URLRequest {
let url = baseURL.appendingPathComponent(path)
var request = URLRequest(url: url)
request.method = method
switch self {
case let .get(parameters):
request = try URLEncodedFormParameterEncoder().encode(parameters, into: request)
case let .post(parameters):
request = try JSONParameterEncoder().encode(parameters, into: request)
}
return request
}
}
可以将路由器扩展到具有任意数量的可配置属性的任意数量的端点,但是一旦达到一定程度的复杂性,就应考虑将一台大型路由器分成较小的路由器以用于API的各个部分。
响应处理
Alamofire通过各种response
方法和ResponseSerializer
协议提供响应处理。
处理响应而不进行序列化
双方DataRequest
并DownloadRequest
提供方法,使响应处理,而不调用任何ResponseSerializer
可言。这对于DownloadRequest
可能无法将大文件加载到内存的来说是最重要的。
// DataRequest
func response(queue: DispatchQueue = .main, completionHandler: @escaping (AFDataResponse<Data?>) -> Void) -> Self
// DownloadRequest
func response(queue: DispatchQueue = .main, completionHandler: @escaping (AFDownloadResponse<URL?>) -> Void) -> Self
与所有响应处理程序一样,所有序列化工作(在本例中为无)在内部队列上执行,并且完成处理程序在queue
传递给方法时调用。这意味着main
默认情况下不必分派回队列。但是,如果在完成处理程序中要执行任何重要的工作,则建议将自定义队列传递给响应方法,并main
在必要时在处理程序本身中分派回去。
ResponseSerializer
该ResponseSerializer
协议由DataResponseSerializerProtocol
和DownloadResponseSerializerProtocol
协议组成。合并后的版本ResponseSerializer
如下所示:
public protocol ResponseSerializer: DataResponseSerializerProtocol & DownloadResponseSerializerProtocol {
///要创建的序列化对象的类型。
associatedtype SerializedObject
/// DataPreprocessor用于准备传入的Data进行序列化。
var dataPreprocessor: DataPreprocessor { get }
/// “ HTTPMethod”认为适合使用空响应主体。
var emptyRequestMethods: Set<HTTPMethod> { get }
/// HTTP响应代码,认为空响应主体是合适的。
var emptyResponseCodes: Set<Int> { get }
func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> SerializedObject
func serializeDownload(request: URLRequest?,
response: HTTPURLResponse?,
fileURL: URL?,
error: Error?) throws -> SerializedObject
}
默认情况下,该serializeDownload
方法是通过Data
从磁盘读取下载并调用serialize
来实现的。因此,可能更适合实现自定义处理用于使用大型下载DownloadRequest
的response(queue:completionHandler:)
上述方法。
ResponseSerializer
提供了dataPreprocessor
,emptyResponseMethods
和的各种默认实现,并且emptyResponseCodes
可以按照一致的类型进行自定义,例如ResponseSerializer
Alamofire附带的各种。
所有ResponseSerializer
用法都通过DataRequest
和上的方法进行DownloadRequest
:
// DataRequest
func response<Serializer: DataResponseSerializerProtocol>(
queue: DispatchQueue = .main,
responseSerializer: Serializer,
completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void) -> Self
// DownloadRequest
func response<Serializer: DownloadResponseSerializerProtocol>(
queue: DispatchQueue = .main,
responseSerializer: Serializer,
completionHandler: @escaping (AFDownloadResponse<Serializer.SerializedObject>) -> Void) -> Self
Alamofire包括一些常见的响应处理程序,包括:
responseData(queue:completionHandler)
:验证并预处理该响应Data
使用DataResponseSerializer
。responseString(queue:encoding:completionHandler:)
:使用提供的将响应解析Data
为。String``String.Encoding
responseJSON(queue:options:completionHandler)
:解析响应Data
使用JSONSerialization
使用所提供的JSONSerialization.ReadingOptions
。不建议使用此方法,仅是为了与现有Alamofire用法兼容。而是responseDecodable
应使用。responseDecodable(of:queue:decoder:completionHandler:)
:使用提供的将响应解析Data
为提供的或推断的Decodable
类型DataDecoder
。使用JSONDecoder
默认。JSON和通用响应解析的推荐方法。
DataResponseSerializer
呼叫responseData(queue:completionHandler:)
上DataRequest
或DownloadRequest
使用DataResponseSerializer
该验证Data
(除非允许没有空的反应已被适当返回emptyResponseMethods
和emptyResponseCodes
),并把该Data
经过dataPreprocessor
。此响应处理程序对于自定义Data
处理很有用,但通常不是必需的。
StringResponseSerializer
呼叫responseString(queue:encoding:completionHandler)
上DataRequest
或DownloadRequest
使用StringResponseSerializer
该验证Data
(除非允许没有空的反应已被适当返回emptyResponseMethods
和emptyResponseCodes
),并把该Data
经过dataPreprocessor
。经预处理Data
然后被用来初始化一个String
使用String.Encoding
从解析HTTPURLResponse
。
JSONResponseSerializer
呼叫responseJSON(queue:options:completionHandler)
上DataRequest
或DownloadRequest
使用JSONResponseSerializer
该验证Data
(除非允许没有空的反应已被适当返回emptyResponseMethods
和emptyResponseCodes
),并把该Data
经过dataPreprocessor
。Data
然后JSONSerialization.jsonObject(with:options:)
使用提供的选项传递经过预处理的内容。不再建议使用此序列化程序。相反,使用DecodableResponseSerializer
提供更好的Swift体验。
DecodableResponseSerializer
呼叫responseDecodable(of:queue:decoder:completionHandler)
上DataRequest
或DownloadRequest
使用DecodableResponseSerializer
该验证Data
(除非允许没有空的反应已被适当返回emptyResponseMethods
和emptyResponseCodes
),并把该Data
经过dataPreprocessor
。Data
然后将预处理的内容传递给提供的类型,DataDecoder
然后解析为提供的或推断的Decodable
类型。
自定义响应处理程序
除了ResponseSerializer
Alamofire随附的flexible 之外,还有其他自定义响应处理的方法。
响应变换
使用现有的ResponseSerializer
然后转换输出是定制响应处理程序的最简单方法之一。双方DataResponse
并DownloadResponse
有map
,tryMap
,mapError
,和tryMapError
方法,同时保留与响应相关的元数据,可以将响应。例如,Decodable
可以使用来从响应中提取属性map
,同时还保留任何先前的解析错误。
AF.request(...).responseDecodable(of: SomeType.self) { response in
let propertyResponse = response.map { $0.someProperty }
debugPrint(propertyResponse)
}
引发错误的转换也可以与一起使用tryMap
,也许可以执行验证:
AF.request(..).responseDecodable(of: SomeType.self) { response in
let propertyResponse = response.tryMap { try $0.someProperty.validated() }
debugPrint(propertyResponse)
}
创建自定义响应序列化器
当Alamofire提供ResponseSerializer
的或响应转换不够灵活,或者自定义量很大时,创建一个ResponseSerializer
是封装该逻辑的好方法。集成自定义项通常有两个部分ResponseSerializer
:创建符合类型并扩展相关Request
类型以使其易于使用。例如,如果服务器返回经过特殊编码的String
,也许用逗号分隔的值,则ResponseSerializer
这种格式的格式可能如下所示:
struct CommaDelimitedSerializer: ResponseSerializer {
func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> [String] {
//调用现有的StringResponseSerializer以自动获得许多行为。
let string = try StringResponseSerializer().serialize(request: request,
response: response,
data: data,
error: error)
return Array(string.split(separator: ","))
}
}
请注意,方法SerializedObject
associatedtype
的返回类型可以满足要求serialize
。在更复杂的序列化程序中,此返回类型本身可以是通用的,从而允许对通用类型进行序列化,如所示DecodableResponseSerializer
。
为了使它CommaDelimitedSerializer
更有用,可以添加其他行为,例如允许通过将空HTTP方法和响应代码传递给底层来自定义空HTTP方法和响应代码StringResponseSerializer
。
流响应处理程序
DataStreamRequest
使用其自身唯一的响应处理程序类型来处理Data
作为流一部分的传入。除了提供的处理程序之外,还可以通过使用DataStreamSerializer
协议来执行自定义序列化。
public protocol DataStreamSerializer {
///从序列化的Data产生的类型。
associatedtype SerializedObject
///将传入的“数据”序列化为“ SerializedObject”值。
///
///-参数数据:要序列化的数据。
///
///-引发:序列化过程中产生的任何错误。
func serialize(_ data: Data) throws -> SerializedObject
}
任何自定义DataStreamSerializer
可被用于处理流Data
通过使用responseStream
方法:
AF.streamRequest(...).responseStream(using: CustomSerializer()) { stream in
// Process stream.
}
Alamofire包含DecodableStreamSerializer
,DataStreamSerializer
可以解析Decodable
传入的类型Data
。可以使用DataDecoder
实例和a 对其进行自定义,DataPreprocessor
并通过以下responseStreamDecodable
方法使用:
AF.streamRequest(...).responseDecodable(of: DecodableType.self) { stream in
// Process stream.
}
或者直接在前面提到的streamResponse
方法中使用它:
AF.streamRequest(...).responseStream(using: DecodableStreamSerializer<DecodableType>(decoder: JSONDecoder())) { stream in
// Process stream.
}
将Alamofire与Combine结合使用
在支持Combine框架的系统上,Alamofire提供了使用自定义Publisher
类型发布响应的功能。这些发布者的工作方式非常类似于Alamofire的响应处理程序。它们被链接到请求上,并且像响应处理程序一样,应该紧随其他API之后validate()
。例如:
AF.request(...).publishDecodable(type: DecodableType.self)
此代码产生一个DataResponsePublisher<DecodableType>
值,该值将发布一个DataResponse<DecodableType, AFError>
值。像所有Alamofire一样Publisher
,它DataResponsePublisher
是完全惰性的,这意味着仅resume
当下游Subscriber
对值提出要求时,才添加响应处理程序和请求。它仅提供一个值,无法重试。
要在使用Alamofire时正确处理重试
Publisher
,请使用Alamofire的内置重试机制,如上所述。
此外,DataResponsePublisher
还提供了将支出DataResponse<Success, Failure>
转换为Result<Success, Failure>
值或Success
带Failure
错误的值的功能。例如:
let publisher = AF.request(...).publishDecodable(type: DecodableType.self)
let resultPublisher = publisher.result()//提供一个AnyPublisher<Result<DecodableType, AFError>, Never>。
let valuePublisher = publisher.value()//提供一个AnyPublisher <DecodableType, AFError>。
与任何一样Publisher
,DataResponsePublisher
可以与各种Combine API一起使用,允许Alamofire首次支持简单的同时请求。
//所有可取消的Combine API用法都必须存储其令牌以维护订阅。
var tokens: Set<AnyCancellable> = []
...
let first = AF.request(...).publishDecodable(type: First.self)
let second = AF.request(...).publishDecodable(type: Second.self)
let both = Publishers.CombineLatest(first, second)
both.sink { first, second in // DataResponse<First, AFError>, DataResponse<Second, AFError>
debugPrint(first)
debugPrint(second)
}
.store(in: &tokens)
顺序请求也是可能的:
//所有可取消的Combine API用法都必须存储其令牌以维护订阅。
var tokens: Set<AnyCancellable> = []
...
AF.request(...)
.publishDecodable(type: First.self)
.value()
.flatMap {
AF.request(...) // 使用 第一个 值 ,以 创建 第二个请求。
.publishDecodable(type: Second.self)
}
.sink { second in // DataResponse<Second, AFError>
debugPrint(second)
}
.store(in: &tokens)
订阅后,此转换链将发出第一个请求,然后创建第二个发布者,并在第二个请求完成时完成。
与所有Combine用法一样,必须注意确保通过维护
AnyCancellable
像这样的函数返回的令牌的生命周期来尽早取消订阅sink
。如果过早取消请求,则响应的错误将设置为AFError.explicitlyCancelled
。
DownloadResponsePublisher
Alamofire还提供Publisher
了DownloadRequest
S, DownloadResponsePublisher
。其行为和功能与相同DataResponsePublisher
。
像大多数DownloadRequest
的响应处理程序一样,从磁盘DownloadResponsePublisher
读取Data
以执行序列化,如果读取大量,可能会影响系统性能Data
。建议您publishUnserialized()
只接收URL?
文件下载到的文件,然后对大文件执行自己的磁盘读取操作。
DataStreamPublisher
DataStreamPublisher
是Publisher
为DataStreamRequest
秒。DataStreamRequest
和Alamofire的其他Publisher
s 一样,它本身DataStreamPublisher
可以返回Data
从网络接收到的序列化的多个值以及最终完成事件。有关DataStreamRequest
工作原理的更多信息,请参见我们的详细使用文档。
网络可达性
在NetworkReachabilityManager
对主机和地址蜂窝和WiFi网络接口的可达性变化监听。
let manager = NetworkReachabilityManager(host: "www.apple.com")
manager?.startListening { status in
print("Network Status Changed: \(status)")
}
请务必记住保留
manager
上述示例中的,否则不会报告任何状态更改。另外,请勿在host
字符串中包含方案,否则可达性将无法正常运行。
使用网络可达性确定下一步操作时,需要记住一些重要的事情。
- 不要使用可达性来确定是否应该发送网络请求。
- 您应该总是发送它。
- 恢复可达性后,请使用该事件重试失败的网络请求。
- 即使网络请求仍然可能失败,这也是重试它们的好时机。
- 网络可达性状态对于确定网络请求为何失败的原因可能很有用。
- 如果网络请求失败,则告诉用户该网络请求由于脱机而失败比处理诸如“请求超时”之类的技术性错误更为有用。
或者,使用RequestRetrier
诸如内置的RetryPolicy
,而不是可达性更新来重试因网络故障而失败的请求,这可能会更简单,更可靠。默认情况下,RetryPolicy
将在各种错误条件下重试幂等请求,包括脱机网络连接。