即將離開簡書温眉,請到掘金繼續(xù)關注我凌蔬。謝謝!
本文掘金鏈接
為什么離開
此文章是對 Alamofire Advanced Usage 的翻譯,有需要的可以去看原文译暂。
另外此文章的內容也保存到了我的 GitHub 倉庫崎脉,建議去 GitHub 閱讀,以獲得更好的閱讀體驗。如果覺得對你有用的赃春,可以順手給個 Star衷戈。謝謝!
- Alamofire 5 的使用 - 高級用法
Alamofire 5 的使用 - 高級用法
這篇文章介紹的是 Alamofire 框架的高級用法,如果之前沒有看過基本用法的,可以先去看看 Alamofire 5 的使用 - 基本用法
Alamofire 是建立在 URLSession
和 URL 加載系統(tǒng)之上的捂寿。為了充分利用這個框架,建議您熟悉底層網絡的概念和功能粪小。
建議閱讀:
- URL Loading System Programming Guide
URLSession
Class ReferenceURLCache
Class ReferenceURLAuthenticationChallenge
Class Reference
Session
Alamofire 的 Session
在職責上大致等同于它維護的 URLSession
實例:它提供 API 來生成各種 Request
子類,這些子類封裝了不同的 URLSessionTask
子類,以及封裝應用于實例生成的所有 Request
的各種配置腌闯。
Session
提供了一個 default
單例實例,并且 AF
實際上就是 Session.default
。因此嘲玫,以下兩個語句是等效的:
AF.request("https://httpbin.org/get")
let session = Session.default
session.request("https://httpbin.org/get")
創(chuàng)建自定義的 Session
實例
大多數(shù)應用程序將需要以各種方式自定義其 Session
實例的行為穷蛹。實現(xiàn)這一點的最簡單方法是使用以下便利初始化器鬼雀,并將結果存儲在整個應用程序中使用的單個實例中。
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
創(chuàng)建 Session
要自定義底層 URLSession
的行為崩侠,可以提供自定義的 URLSessionConfiguration
實例。建議從 URLSessionConfiguration.af.default
實例開始,因為它添加了 Alamofire 提供的默認 Accept-Encoding
、Accept-Language
和User-Agent
headers,但是可以使用任何 URLSessionConfiguration
荆忍。
let configuration = URLSessionConfiguration.af.default
configuration.allowsCellularAccess = false
let session = Session(configuration: configuration)
URLSessionConfiguration
不是設置Authorization
或Content-Type
headers 的建議位置腹泌。相反,可以使用提供的headers
APIs专甩、ParameterEncoder
或RequestAdapter
將它們添加到Request
中贡未。
正如蘋果在其文檔中所述嫩挤,在實例被添加到
URLSession
(或者,在 Alamofire 的情況下邑遏,用于初始化Session
)之后對URLSessionConfiguration
屬性進行修改沒有效果。
SessionDelegate
SessionDelegate
實例封裝了對各種 URLSessionDelegate
和相關協(xié)議回調的所有處理。SessionDelegate
還充當 Alamofire 生成的每個 Request
的 SessionStateDelegate
彬碱,允許 Request
從創(chuàng)建它們的 Session
實例間接導入狀態(tài)灵奖。SessionDelegate
可以使用特定的 FileManager
實例進行自定義,該實例將用于任何磁盤訪問,例如訪問要通過 UploadRequest
上傳的文件或通過 DownloadRequest
下載的文件谭贪。
let delelgate = SessionDelegate(fileManager: .default)
startRequestsImmediately
默認情況下洞渔,Session
將在添加至少一個響應 handler 后立即對 Request
調用 resume()
堤瘤。將 startRequestsImmediately
設置為 false
需要手動調用所有請求的 resume()
方法。
let session = Session(startRequestsImmediately: false)
Session
的 DispatchQueue
默認情況下环葵,Session
實例對所有異步工作使用單個 DispatchQueue
地梨。這包括 URLSession
的 delegate
OperationQueue
的 underlyingQueue
洁闰,用于所有 URLRequest
創(chuàng)建、所有響應序列化工作以及所有內部 Session
和 Request
狀態(tài)的改變腰素。如果性能分析顯示瓶頸在于 URLRequest
的創(chuàng)建或響應序列化献起,則可以為 Session
的每個工作區(qū)域提供單獨的 DispatchQueue
捌显。
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
協(xié)議(RequestAdapter & RequestRetrier
)提供了重要而強大的請求自適應和重試功能。它可以在 Session
和 Request
層級使用。有關 RequestInterceptor
和 Alamofire 包含的各種實現(xiàn)(如 RetryPolicy
)的更多詳細信息,請參見下文。
let policy = RetryPolicy()
let session = Session(interceptor: policy)
添加 ServerTrustManager
Alamofire 的 ServerTrustManager
類封裝了域名和遵循 ServerTrustEvaluating
協(xié)議的類型實例之間的映射山孔,這提供了定制 Session
處理 TLS 安全性的能力勒庄。這包括使用證書和公鑰固定以及證書吊銷檢查。有關更多信息坛吁,請參閱有關 ServerTrustManager
和 ServerTrustEvaluating
的部分。初始化 ServerTrustManger
非常簡單,只需提供域名與要執(zhí)行的計算類型之間的映射即可:
let manager = ServerTrustManager(evaluators: ["httpbin.org": PinnedCertificatesTrustEvaluator()])
let session = Session(serverTrustManager: manager)
有關評估服務器信任的詳細信息,請參閱下面的詳細文檔。
添加 RedirectHandler
Alamofire 的 RedirectHandler
協(xié)議定制了 HTTP 重定向響應的處理。它可以在 Session
和 Request
層級使用。Alamofire 包含了遵循 RedirectHandler
協(xié)議的 Redirector
類型,并提供對重定向的簡單控制性誉。有關重定向處理程序的詳細信息煌往,請參閱下面的詳細文檔。
let redirector = Redirector(behavior: .follow)
let session = Session(redirectHandler: redirector)
添加 CachedResponseHandler
Alamofire 的 CachedResponseHandler
協(xié)議定制了響應的緩存曾棕,可以在 Session
和 Request
層級使用。Alamofire 包含 ResponseCacher
類型菜循,它遵循 CachedResponseHandler
協(xié)議并提供對響應緩存的簡單控制翘地。有關詳細信息,請參閱下面的詳細文檔衙耕。
let cacher = ResponseCacher(behavior: .cache)
let session = Session(cachedResponseHandler: cacher)
添加 EventMonitor
Alamofire 的 EventMonitor
協(xié)議提供了對 Alamofire 內部事件的強大洞察力昧穿。它可以用來提供日志和其他基于事件的特性。Session
在初始化時接受遵循 EventMonitor
協(xié)議的實例的數(shù)組橙喘。
let monitor = ClosureEventMonitor()
monitor.requestDidCompleteTaskWithError = { (request, task, error) in
debugPrint(request)
}
let session = Session(eventMonitors: [monitor])
從 URLSession
創(chuàng)建實例
除了前面提到的便利初始化器之外时鸵,還可以直接從 URLSession
初始化 Session
。但是厅瞎,在使用這個初始化器時需要記住幾個要求饰潜,因此建議使用便利初始化器。其中包括:
- Alamofire 不支持為在后臺使用而配置的
URLSession
和簸。初始化Session
時彭雾,這將導致運行時錯誤。 - 必須創(chuàng)建
SessionDelegate
實例并將其作為URLSession
的delegate
锁保,以及傳遞給Session
的初始化器薯酝。 - 必須將自定義
OperationQueue
作為URLSession
的delegateQueue
。此隊列必須是串行隊列爽柒,它必須具有備用DispatchQueue
吴菠,并且必須將該DispatchQueue
作為其rootQueue
傳遞給Session
。
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 執(zhí)行的每個請求都由特定的類浩村、DataRequest
做葵、UploadRequest
和 DownloadRequest
封裝。這些類中的每一個都封裝了每種類型請求所特有的功能穴亏,但是 DataRequest
和 DownloadRequest
繼承自一個公共的父類 Request
(UploadRequest
繼承自 DataRequest
)蜂挪。Request
實例從不直接創(chuàng)建,而是通過各種 request
方法之一從會話 Session
中自動生成嗓化。
請求管道
一旦使用 Request
子類的初始參數(shù)或 URLRequestConvertible
創(chuàng)建了它棠涮,它就會通過組成 Alamofire 請求管道的一系列步驟進行傳遞。對于成功的請求刺覆,這些請求包括:
- 初始參數(shù)(如 HTTP 方法严肪、headers 和參數(shù))被封裝到內部
URLRequestConvertible
值中。如果直接傳遞URLRequestConvertible
值谦屑,則使用該值時將保持不變驳糯。 - 對
URLRequestConvertible
值調用asURLRequest()
,創(chuàng)建第一個URLRequest
值氢橙。此值將傳遞給Request
并存儲在requests
中酝枢。 - 如果有任何
Session
或Request
RequestAdapter
或RequestInterceptor
,則使用先前創(chuàng)建的URLRequest
調用它們悍手。然后將調整后的URLRequest
傳遞給Request
并存儲在requests
中帘睦。 -
Session
調用Request
創(chuàng)建的URLSessionTask
袍患,以基于URLRequest
執(zhí)行網絡請求。 - 完成
URLSessionTask
并收集URLSessionTaskMetrics
后竣付,Request
將執(zhí)行其Validator
诡延。 - 請求執(zhí)行已附加的任何響應 handlers,如
responseDecodable
古胆。
在這些步驟中的任何一個肆良,都可以通過創(chuàng)建或接收的 Error
值來表示失敗,然后將錯誤值傳遞給關聯(lián)的 Request
逸绎。例如惹恃,除了步驟 1 和 4 之外,上面的所有其他步驟都可以創(chuàng)建一個Error
桶良,然后傳遞給響應 handlers 或可供重試座舍。下面是一些可以或不能在整個請求管道中失敗的示例。
- 參數(shù)封裝不能失敗陨帆。
- 調用
asURLRequest()
時曲秉,任何URLRequestConvertible
值都可能創(chuàng)建錯誤。這允許初始驗證各種URLRequest
屬性或參數(shù)編碼失敗疲牵。 -
RequestAdapter
在自適應過程中可能會失敗承二,可能是由于缺少授權 token。 -
URLSessionTask
創(chuàng)建不能失敗纲爸。 -
URLSessionTask
可能由于各種原因帶有錯誤地完成亥鸠,包括網絡可用性和取消。這些Error
值將傳遞回給Request
识啦。 - 響應 handlers 可以產生任何錯誤负蚊,通常是由于無效響應或其他分析錯誤。
一旦將錯誤傳遞給 Request
颓哮,Request
將嘗試運行與 Session
或 Request
關聯(lián)的任何 RequestRetrier
家妆。如果任何 RequestRetrier
選擇重試該 Request
,則將再次運行完整的管道冕茅。RequestRetrier
也會產生 Error
伤极,但這些錯誤不會觸發(fā)重試。
Request
盡管 Request
不封裝任何特定類型的請求姨伤,但它包含 Alamofire 執(zhí)行的所有請求所共有的狀態(tài)和功能哨坪。這包括:
狀態(tài)
所有 Request
類型都包含狀態(tài)的概念,表示 Request
生命周期中的主要事件乍楚。
public enum State {
case initialized
case resumed
case suspended
case cancelled
case finished
}
請求在創(chuàng)建后以 .initialized
狀態(tài)啟動当编。通過調用適當?shù)纳芷诜椒ǎ梢話炱鹜较⒒謴秃腿∠?Request
凌箕。
-
resume()
恢復或啟動請求的網絡流量拧篮。如果startRequestsImmediately
為true
,則在將響應 handlers 添加到Request
后自動調用此函數(shù)牵舱。 -
suspend()
掛起或暫停請求及其網絡流量。此狀態(tài)下的Request
可以繼續(xù)缺虐,但只有DownloadRequest
才能繼續(xù)傳輸數(shù)據芜壁。其他Request
將重新開始。 -
cancel()
取消請求高氮。一旦進入此狀態(tài)慧妄,就無法恢復或掛起Request
。調用cancel()
時剪芍,將使用AFError.explicitlyCancelled
實例設置請求的error
屬性塞淹。如果一個Request
被恢復并且在以后沒有被取消,那么它將在所有響應驗證器和響應序列化器運行之后到達.finished
狀態(tài)罪裹。但是饱普,如果在請求達到.finished
狀態(tài)后將其他響應序列化器添加到該請求,則它將轉換回.resumed
狀態(tài)并再次執(zhí)行網絡請求状共。
進度
為了跟蹤請求的進度套耕,Request
提供了 uploadProgress
和 downloadProgress
屬性以及基于閉包的 uploadProgress
和 downloadProgress
方法。與所有基于閉包的 Request
APIs 一樣峡继,進度 APIs 可以與其他方法鏈接到 Request
之外冯袍。與其他基于閉包的 APIs 一樣,它們應該在添加任何響應 handlers(如 responseDecodable
)之前添加到請求中碾牌。
AF.request(...)
.uploadProgress { progress in
print(progress)
}
.downloadProgress { progress in
print(progress)
}
.responseDecodable(of: SomeType.self) { response in
debugPrint(response)
}
重要的是康愤,并不是所有的 Request
子類都能夠準確地報告它們的進度,或者可能有其他依賴項來報告它們的進度舶吗。
- 對于上傳進度征冷,可以通過以下方式確定進度:
- 通過作為上傳 body 提供給
UploadRequest
的Data
對象的長度。 - 通過作為
UploadRequest
的上傳 body 提供的磁盤上文件的長度裤翩。 - 通過根據請求的
Content-Length
header 的值(如果已手動設置)资盅。
- 通過作為上傳 body 提供給
- 對于下載進度,只有一個要求:
- 服務器響應必須包含
Content-Length
header踊赠。不幸的是呵扛,URLSession
對進度報告可能還有其他未記錄的要求,這妨礙了準確的進度報告筐带。
- 服務器響應必須包含
處理回調
Alamofire 的 RedirectHandler
協(xié)議提供了對 Request
的重定向處理的控制和定制今穿。除了每個 Session
RedirectHandler
之外,每個 Request
都可以被賦予屬于自己的 RedirectHandler
伦籍,并且這個 handler 將重寫 Session
提供的任何 RedirectHandler
蓝晒。
let redirector = Redirector(behavior: .follow)
AF.request(...)
.redirect(using: redirector)
.responseDecodable(of: SomeType.self) { response in
debugPrint(response)
}
注意:一個
Request
只能設置一個RedirectHandler
腮出。嘗試設置多個將導致運行時異常。
自定義緩存
Alamofire 的 CachedResponseHandler
協(xié)議提供了對響應緩存的控制和定制芝薇。除了每個 Session
的 CachedResponseHandlers
之外胚嘲,每個 Request
都可以被賦予屬于自己的 CachedResponseHandler
,并且這個 handler 將重寫 Session
提供的任何 CachedResponseHandler
洛二。
let cacher = Cacher(behavior: .cache)
AF.request(...)
.cacheResponse(using: cacher)
.responseDecodable(of: SomeType.self) { response in
debugPrint(response)
}
注意:一個
Request
只能設置一個CachedResponseHandler
馋劈。嘗試設置多個將導致運行時異常。
Credentials
為了利用 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
header机断,那么應該直接提供它楷拳,或者作為請求的一部分,或者通過一個RequestInterceptor
毫缆。
此外唯竹,添加 URLCredential
也同樣簡單:
let credential = URLCredential(...)
AF.request(...)
.authenticate(using: credential)
.responseDecodable(of: SomeType.self) { response in
debugPrint(response)
}
Request
的 URLRequest
由 Request
發(fā)出的每個網絡請求最終封裝在由傳遞給 Session
請求方法之一的各種參數(shù)創(chuàng)建的 URLRequest
值中。Request
將在其 requests
數(shù)組屬性中保留這些 URLRequest
的副本苦丁。這些值既包括從傳遞的參數(shù)創(chuàng)建的初始 URLRequest
浸颓,也包括由 RequestInterceptors
創(chuàng)建的任何 URLRequest
。但是旺拉,該數(shù)組不包括代表 Request
發(fā)出的 URLSessionTask
執(zhí)行的 URLRequest
产上。要檢查這些值,tasks
屬性允許訪問 Request
執(zhí)行的所有 URLSessionTask
蛾狗。
URLSessionTask
在許多方面晋涣,各種 Request
子類充當 URLSessionTask
的包裝器,提供與特定類型任務交互的特定 API沉桌。這些任務通過 tasks
數(shù)組屬性在 Request
實例上可見谢鹊。這包括為 Request
創(chuàng)建的初始任務,以及作為重試過程的一部分創(chuàng)建的任何后續(xù)任務留凭,每次重試一個任務佃扼。
響應
請求完成后,每個 Request
可能都有一個可用的 HTTPURLResponse
值蔼夜。此值僅在請求未被取消且沒有發(fā)出網絡請求失敗時可用兼耀。此外,如果重試請求,則只有最后一個響應可用瘤运∏舷迹可以從 tasks 屬性中的 URLSessionTasks
獲得中間的響應。
URLSessionTaskMetrics
Alamofire 為 Request
執(zhí)行的每個 URLSessionTask
收集 URLSessionTaskMetrics
值拯坟。這些值存儲在 metrics
屬性但金,每個值對應于同一索引中的 tasks
中的 URLSessionTask
。
URLSessionTaskMetrics
也可從 Alamofire 的各種響應類型中訪問郁季,如 DataResponse
傲绣。例如:
AF.request(...)
.responseDecodable(of: SomeType.self) { response in {
print(response.metrics)
}
DataRequest
DataRequest
是 Request
的一個子類,它封裝了 URLSessionDataTask
巩踏,將服務器響應下載到存儲在內存中的 Data
中。因此续搀,必須認識到塞琼,超大下載量可能會對系統(tǒng)性能產生不利影響。對于這些類型的下載禁舷,建議使用 DownloadRequest
將數(shù)據保存到磁盤彪杉。
其他狀態(tài)
除了 Request
提供的屬性之外,DataRequest
還有一些屬性牵咙。其中包括 data
(這是服務器響應的累積 Data
)和 convertible
(這是創(chuàng)建 DataRequest
時使用的 URLRequestConvertible
)派近,其中包含創(chuàng)建實例的原始參數(shù)。
驗證
默認情況下洁桌,DataRequest
不驗證響應渴丸。相反,必須向其中添加對 validate()
的調用另凌,以驗證各種屬性是否有效鳖昌。
public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> Result<Void, Error>
默認情況下囊咏,添加 validate()
確保響應狀態(tài)代碼在 200..<300
范圍內,并且響應的 Content-Type
與請求的 Accept
匹配。通過傳遞 Validation
閉包可以進一步定制驗證:
AF.request(...)
.validate { request, response, data in
...
}
UploadRequest
UploadRequest
是 DataRequest
的一個子類俐银,它封裝 URLSessionUploadTask
、將 Data
惭载、磁盤上的文件或 InputStream
上傳到遠程服務器恋拷。
其他狀態(tài)
除了 DataRequest
提供的屬性外,UploadRequest
還有一些屬性王污。其中包括一個 FileManager
實例罢吃,用于在上傳文件時自定義對磁盤的訪問,以及 upload
玉掸,upload
封裝了用于描述請求的 URLRequestConvertible
值和確定要執(zhí)行的上傳類型的 Uploadable
值刃麸。
DownloadRequest
DownloadRequest
是 Request
的一個具體子類,它封裝了 URLSessionDownloadTask
司浪,將響應數(shù)據下載到磁盤泊业。
其他狀態(tài)
DownloadRequest
除了由 Request
提供的屬性外把沼,還有一些屬性。其中包括取消 DownloadRequest
時生成的數(shù)據 resumeData
(可用于以后繼續(xù)下載)和 fileURL
(下載完成后下載文件對應的 URL)吁伺。
取消
除了支持 Request
提供的 cancel()
方法外饮睬,DownloadRequest
還包括 cancel(producingResumeData shouldProduceResumeData: Bool)
,如果可能的話篮奄,可以選擇在取消時設置 resumeData
屬性捆愁,以及 cancel(byProducingResumeData completionHandler: @escaping (_ data: Data?) -> Void)
,它將生成的恢復數(shù)據提供給傳遞進來的閉包窟却。
AF.download(...)
.cancel { resumeData in
...
}
驗證
DownloadRequest
支持的驗證版本與 DataRequest
和 UploadRequest
略有不同昼丑,因為它的數(shù)據被下載到磁盤上。
public typealias Validation = (_ request: URLRequest?, _ response: HTTPURLResponse, _ fileURL: URL?)
必須使用提供的 fileURL
訪問下載的 Data
夸赫,而不是直接訪問下載的 Data菩帝。否則,DownloadRequest
的驗證器的功能與 DataRequest
的相同茬腿。
使用 RequestInterceptor
調整和重試請求
Alamofire 的 RequestInterceptor
協(xié)議(由 RequestAdapter
和 RequestRetrier
協(xié)議組成)支持強大的每個 Session
和每個 Request
功能呼奢。其中包括身份驗證系統(tǒng),在該系統(tǒng)中切平,向每個 Request
添加一個常用的 headers握础,并在授權過期時重試 Request
。此外悴品,Alamofire 還包含一個內置的 RetryPolicy
類型禀综,當由于各種常見的網絡錯誤而導致請求失敗時,可以輕松重試他匪。
RequestAdapter
Alamofire 的 RequestAdapter
協(xié)議允許在通過網絡發(fā)出之前檢查和修改 Session
執(zhí)行的每個 URLRequest
菇存。適配器的一個非常常見的用途,是在特定類型身份驗證后面將 Authorization
header 添加請求邦蜜。
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void)
它的參數(shù)包括:
-
urlRequest
:最初從用于創(chuàng)建請求的參數(shù)或URLRequestConvertible
值創(chuàng)建的urlRequest
依鸥。 -
session
:創(chuàng)建調用適配器的Request
的Session
。 -
completion
: 一個必須調用的悼沈、用來表示適配器已完成的異步 completion handler贱迟。它的異步特性使RequestAdapter
能夠在請求通過網絡發(fā)送之前從網絡或磁盤訪問異步資源。提供給completion
閉包的Result
可以返回帶有修改后的URLRequest
的.success
值絮供,或者返回帶有關聯(lián)錯誤的.failure
值衣吠,然后將使用該值使請求失敗。例如壤靶,添加Authorization
header 需要修改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(bearer: accessToken))
completion(.success(urlRequest))
}
RequestRetrier
Alamofire 的 RequestRetrier
協(xié)議允許重試在執(zhí)行時遇到錯誤的請求。這包括在 Alamofire 的請求管道的任何階段產生的錯誤忧换。
RequestRetrier
協(xié)議只有一個方法:
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void)
它的參數(shù)包括:
-
request
: 遇到錯誤的Request
恬惯。 -
session
: 管理Request
的Session
。 -
error
: 觸發(fā)重試的Error
亚茬,通常是一個AFError
酪耳。 -
completion
: 必須調用的異步 completion handler,以表示Request
是否需要重試刹缝。調用的時候必須傳入RetryResult
碗暗。
RetryResult
類型表示在 RequestRetrier
中實現(xiàn)的任何邏輯的結果。定義為:
/// Outcome of determination whether retry is necessary.
public enum RetryResult {
/// Retry should be attempted immediately.
case retry
/// Retry should be attempted after the associated `TimeInterval`.
case retryWithDelay(TimeInterval)
/// Do not retry.
case doNotRetry
/// Do not retry due to the associated `Error`.
case doNotRetryWithError(Error)
}
例如梢夯,如果請求是等冪的言疗,Alamofire 的 RetryPolicy
類型將自動重試由于某種網絡錯誤而失敗的請求。
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)
}
}
安全
在與服務器和 web 服務通信時使用安全的 HTTPS 連接是保護敏感數(shù)據的重要步驟颂砸。默認情況下洲守,Alamofire 接收與 URLSession
相同的自動 TLS 證書和證書鏈驗證。雖然這保證了證書鏈的有效性沾凄,但并不能防止中間人(MITM)攻擊或其他潛在的漏洞。為了減輕 MITM 攻擊知允,處理敏感客戶數(shù)據或財務信息的應用程序應使用 Alamofire 的 ServerTrustEvaluating
協(xié)議提供的證書或公鑰固定撒蟀。
使用 ServerTrustManager
和 ServerTrustEvaluating
評估服務器信任
ServerTrustEvaluting
ServerTrustEvaluting
協(xié)議提供了執(zhí)行任何類型服務器信任評估的方法。它只有一個方法:
func evaluate(_ trust: SecTrust, forHost host: String) throws
此方法提供從底層 URLSession
接收的 SecTrust
值和主機 String
温鸽,并提供執(zhí)行各種評估的機會保屯。
Alamofire 包括許多不同類型的信任評估器,為評估過程提供可組合的控制:
-
DefaultTrustEvaluator
:使用默認服務器信任評估涤垫,同時允許您控制是否驗證質詢提供的主機姑尺。 -
RevocationTrustEvaluator
:檢查接收到的證書的狀態(tài)以確保它沒有被吊銷。這通常不會在每個請求上執(zhí)行蝠猬,因為它需要網絡請求開銷切蟋。 -
PinnedCertificatesTrustEvaluator
: 使用提供的證書驗證服務器信任。如果某個固定證書與某個服務器證書匹配榆芦,則認為服務器信任有效柄粹。此評估器還可以接受自簽名證書。 -
PublicKeysTrustEvaluator
: 使用提供的公鑰驗證服務器信任匆绣。如果某個固定公鑰與某個服務器證書公鑰匹配驻右,則認為服務器信任有效。 -
CompositeTrustEvaluator
: 評估一個ServerTrustEvaluating
值數(shù)組崎淳,只有在所有數(shù)組中值都成功時才成功堪夭。此類型可用于組合,例如,RevocationTrustEvaluator
和PinnedCertificatesTrustEvaluator
森爽。 -
DisabledEvaluator
:此評估器應僅在調試方案中使用恨豁,因為它禁用所有求值,而這些求值又將始終認為任何服務器信任都是有效的拗秘。此評估器不應在生產環(huán)境中使用圣絮!
ServerTrustManager
ServerTrustManager
負責存儲 ServerTrustEvaluating
值到特定主機的內部映射。這允許 Alamofire 使用不同的評估器評估每個主機雕旨。
let evaluators: [String: ServerTrustEvaluating] = [
// 默認情況下扮匠,包含在 app bundle 的證書會自動固定。
"cert.example.com": PinnedCertificatesTrustEvalutor(),
// 默認情況下凡涩,包含在 app bundle 的來自證書的公鑰會被自動使用棒搜。
"keys.example.com": PublicKeysTrustEvalutor(),
]
let manager = ServerTrustManager(evaluators: serverTrustPolicies)
此 ServerTrustManager
將具有以下行為:
-
cert.example.com
將始終在啟用默認和主機驗證的情況下使用證書固定,因此需要滿足以下條件才能允許 TLS 握手成功:- 證書鏈必須有效活箕。
- 證書鏈必須包含一個固定證書力麸。
- 質詢主機必須與證書鏈的葉證書中的主機匹配。
-
keys.example.com
將始終在啟用默認和主機驗證的情況下使用公鑰固定育韩,因此需要滿足以下條件才能允許 TLS 握手成功:- 證書鏈必須有效克蚂。
- 證書鏈必須包含一個固定的公鑰。
- 質詢主機必須與證書鏈中的證書中的主機匹配筋讨。
- 對其他主機的請求將產生一個錯誤埃叭,因為服務器信任管理器要求默認評估所有主機。
子類化 ServerTrustPolicyManager
如果發(fā)現(xiàn)自己需要更靈活的服務器信任策略匹配行為(例如通配符域名)悉罕,那么子類化 ServerTrustManager
赤屋,并用自己的自定義實現(xiàn)重寫 serverTrustEvaluator(forHost:)
方法。
final class CustomServerTrustPolicyManager: ServerTrustPolicyManager {
override func serverTrustEvaluator(forHost host: String) -> ServerTrustEvaluating? {
var policy: ServerTrustPolicy?
// Implement your custom domain matching behavior...
return policy
}
}
應用傳輸安全 (App Transport Security)
在 iOS 9 中添加了 App Transport Security(ATS)壁袄,使用帶有多個 ServerTrustEvaluating
對象的自定義 ServerTrustManager
可能不會有任何效果类早。如果您持續(xù)看到 CFNetwork SSLHandshake failed (-9806)
錯誤,則可能遇到了此問題嗜逻。蘋果的 ATS 系統(tǒng)會覆蓋整個質詢系統(tǒng)涩僻,除非您在應用程序的 plist 中配置 ATS 設置以禁用足夠多的 ATS 設置,以允許您的應用程序評估服務器信任栈顷。如果遇到此問題(自簽名證書的概率很高)令哟,可以通過將 NSAppTransportSecurity
設置添加到 Info.plist
來解決此問題。您可以使用 nscurl
工具的 --ats-diagnostics
選項對主機執(zhí)行一系列測試妨蛹,以查看可能需要哪些 ATS 重寫屏富。
在本地網絡中使用自簽名證書
如果嘗試連接到本地主機上運行的服務器,并且使用自簽名證書蛙卤,則需要將以下內容添加到 Info.plist
中狠半。
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
</dict>
根據蘋果文檔噩死,將 NSAllowsLocalNetworking
設置為 YES
允許加載本地資源,而不必為應用程序的其余部分禁用 ATS神年。
自定義緩存和重定向處理
URLSession
允許使用 URLSessionDataDelegate
和 URLSessionTaskDelegate
方法自定義緩存和重定向行為已维。Alamofire 將這些定制點呈現(xiàn)為 CachedResponseHandler
和 RedirectHandler
協(xié)議。
CachedResponseHandler
CachedResponseHandler
協(xié)議允許控制將 HTTP 響應緩存到與發(fā)出請求的 Session
相關聯(lián)的 URLCache
實例中已日。該協(xié)議只有一個方法:
func dataTask(_ task: URLSessionDataTask,
willCacheResponse response: CachedURLResponse,
completion: @escaping (CachedURLResponse?) -> Void)
從方法簽名中可以看出垛耳,此控制僅適用于使用底層 URLSessionDataTask
進行網絡傳輸?shù)?Request
,這些請求包括 DataRequest
和 UploadRequest
(因為 URLSessionUploadTask
是 URLSessionDataTask
的一個子類)飘千√孟剩考慮響應進行緩存的條件非常廣泛,因此最好查看 URLSessionDataDelegate
方法 urlSession(_:dataTask:willCacheResponse:completionHandler:)
的文檔护奈。一旦考慮將響應用于緩存缔莲,就可以進行各種有價值的操作:
- 通過返回
nil
CachedURLResponse
來防止完全緩存響應。 - 修改
CachedURLResponse
的storagePolicy
霉旗,以更改緩存值的存放位置痴奏。 - 直接修改底層
URLResponse
,添加或刪除值厌秒。 - 修改與響應關聯(lián)的
Data
(如果有)读拆。
Alamofire 包含遵循 CachedResponseHandler
協(xié)議的 ResponseCacher
類型,使緩存(或者不緩存)或修改響應變得容易鸵闪。ResponseCacher
接受一個 Behavior
值來控制緩存行為建椰。
public enum Behavior {
/// Stores the cached response in the cache.
case cache
/// Prevents the cached response from being stored in the cache.
case doNotCache
/// Modifies the cached response before storing it in the cache.
case modify((URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)
}
ResponseCacher
可以在 Session
和 Request
的基礎上使用,如上所述岛马。
RedirectHandler
RedirectHandler
協(xié)議允許控制特定 Request
的重定向行為。它只有一個方法:
func task(_ task: URLSessionTask,
willBeRedirectedTo request: URLRequest,
for response: HTTPURLResponse,
completion: @escaping (URLRequest?) -> Void)
此方法提供了修改重定向的 URLRequest
或傳遞 nil
以完全禁用重定向的機會屠列。Alamofire 提供了遵循 RedirectHandler
協(xié)議的 Redirector
類型啦逆,使其易于 follow、not follow 或修改重定向請求笛洛。Redirector
接受一個 Behavior
值來控制重定向行為夏志。
public enum Behavior {
/// Follow the redirect as defined in the response.
case follow
/// Do not follow the redirect defined in the response.
case doNotFollow
/// Modify the redirect request defined in the response.
case modify((URLSessionTask, URLRequest, HTTPURLResponse) -> URLRequest?)
}
使用 EventMonitor
EventMonitor
協(xié)議允許觀察和檢查大量內部 Alamofire 事件。這些事件包括由 Alamofire 實現(xiàn)的所有 URLSessionDelegate
苛让、URLSessionTaskDelegate
和 URLSessionDownloadDelegate
方法以及大量內部 Request
事件沟蔑。除了這些事件(默認情況下是不起作用的空方法)之外,EventMonitor
協(xié)議還需要一個 DispatchQueue
狱杰,在這個 DispatchQueue
上調度所有事件以保持性能瘦材。此 DispatchQueue
默認為 .main
,但對于任何自定義一致類型仿畸,建議使用專用串行隊列食棕。
Logging
也許 EventMonitor
協(xié)議的最大用途是實現(xiàn)相關事件的日志記錄朗和。一個簡單的實現(xiàn)可能如下所示:
final class Logger: EventMonitor {
let queue = DispatchQueue(label: ...)
// Event called when any type of Request is resumed.
func requestDidResume(_ request: Request) {
print("Resuming: \(request)")
}
// Event called whenever a DataRequest has parsed a response.
func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {
debugPrint("Finished: \(response)")
}
}
此 Logger
類型可以按上述方法添加到 Session
中:
let logger = Logger()
let session = Session(eventMonitors: [logger])
創(chuàng)建請求
作為一個框架,Alamofire 有兩個主要目標:
- 使原型和工具的網絡請求易于實現(xiàn)
- 作為 APP 網絡請求的通用基礎
它通過使用強大的抽象簿晓、提供有用的默認值和包含常見任務的實現(xiàn)來實現(xiàn)這些目標眶拉。然而,一旦 Alamofire 的使用超出了一些請求憔儿,就有必要超越高級的忆植、默認的實現(xiàn),進入為特定應用程序定制的行為谒臼。Alamofire 提供 URLConvertible
和 URLRequestConvertible
協(xié)議來幫助進行這種定制朝刊。
URLConvertible
可以使用遵循 URLConvertible
協(xié)議的類型來構造 URL,然后使用 URL 在內部構造 URL 請求屋休。默認情況下坞古,String
、URL
和 URLComponents
遵循了 URLConvertible
協(xié)議劫樟,允許將它們中的任何一個作為 URL 參數(shù)傳遞給 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
協(xié)議的類型可用于構造 URLRequest
奶陈。默認情況下,URLRequest
遵循 URLRequestConvertible
附较,允許將其直接傳遞到 request
吃粒、upload
和 download
方法中。Alamofire 使用 URLRevestExchange
作為請求管道中流動的所有請求的基礎拒课。直接使用 URLRequest
是在 Alamofire 提供的 ParamterEncoder
之外自定義 URLRequest
創(chuàng)建的推薦方法徐勃。
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
的自定義類型,以確保所請求端點的一致性早像。這種方法可以用來消除服務器端的不一致性僻肖,提供類型安全的路由,以及管理其他狀態(tài)卢鹦。
路由請求
隨著應用程序規(guī)模的增長臀脏,在構建網絡堆棧時采用通用模式非常重要。該設計的一個重要部分是如何路由您的請求冀自。Alamofire URLConvertible
和 URLRequestConvertible
協(xié)議以及 Router
設計模式都可以幫助您揉稚。
“router” 是定義“路由”或請求組件的類型。這些組件可以包括 URLRequest
的部分熬粗、發(fā)出請求所需的參數(shù)以及每個請求的各種 Alamofire 設置搀玖。一個簡單的 router 可能看起來像這樣:
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)
更復雜的 router 可以包括請求的參數(shù)。使用 Alamofire 的 ParameterEncoder
協(xié)議和包含的編碼器驻呐,任何 Encodable
類型都可以用作參數(shù):
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
}
}
Router 可以擴展到具有任意數(shù)量可配置屬性的任意數(shù)量的端點巷怜,但是一旦達到了一定的復雜程度葛超,就應該考慮將一個大的 router 分成較小的 router 作為 API 的一部分。
響應處理
Alamofire 通過各種 response
方法和 ResponseSerializer
協(xié)議提供響應處理延塑。
處理沒有序列化的響應
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
與所有響應 handlers 一樣,所有序列化工作(在本例中為“無”)都在內部隊列上執(zhí)行宋雏,并在傳遞給方法的 queue
上調用 completion handler芜飘。這意味著在默認情況下不需要將其分派回主隊列。但是磨总,如果要在 completion handler 中執(zhí)行任何重要的工作嗦明,建議將自定義隊列傳遞給響應方法,必要時在 handler 本身中將分派回主隊列蚪燕。
ResponseSerializer
ResponseSerializer
協(xié)議由 DataResponseSerializerProtocol
和 DownloadResponseSerializerProtocol
協(xié)議組成娶牌。ResponseSerializer
的組合版本如下:
public protocol ResponseSerializer: DataResponseSerializerProtocol & DownloadResponseSerializerProtocol {
/// The type of serialized object to be created.
associatedtype SerializedObject
/// `DataPreprocessor` used to prepare incoming `Data` for serialization.
var dataPreprocessor: DataPreprocessor { get }
/// `HTTPMethod`s for which empty response bodies are considered appropriate.
var emptyRequestMethods: Set<HTTPMethod> { get }
/// HTTP response codes for which empty response bodies are considered appropriate.
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
方法是通過從磁盤讀取下載的數(shù)據并調用 serialize
來實現(xiàn)的馆纳。因此诗良,使用上面提到的 DownloadRequest
的響應 response(queue:completionHandler:)
方法實現(xiàn)對大型下載的自定義處理可能更為合適。
ResponseSerializer
為 dataPreprocessor
鲁驶、emptyResponseMethods
和 emptyResponseCodes
提供了各種默認實現(xiàn)鉴裹,這些實現(xiàn)可以在自定義類型中進行定制,如 Alamofire 附帶的各種 ResponseSerializer
钥弯。
所有 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 包括幾個常見的響應 handlers径荔,包括:
-
responseData(queue:completionHandler)
:使用DataResponseSerializer
驗證和預處理響應Data
。 -
responseString(queue:encoding:completionHandler:)
:使用提供的String.Encoding
將響應Data
解析為String
脆霎。 -
responseJSON(queue:options:completionHandler)
:使用提供的JSONSerialization.ReadingOptions
使用JSONSerialization
解析響應Data
总处。不建議使用此方法,僅為與現(xiàn)有的 Alamofire 用法兼容而提供绪穆。相反,應該使用responseDecodable
虱岂。 -
responseDecodable(of:queue:decoder:completionHandler:)
:使用提供的DataDecoder
將響應Data
解析為提供的或推斷的Decodable
類型玖院。默認情況下使用JSONDecoder
。JSON 和泛型響應解析推薦用此方法第岖。
DataResponseSerializer
在 DataRequest
或 DownloadRequest
上調用 responseData(queue:completionHandler:)
使用 DataResponseSerializer
驗證 Data
是否已正確返回(除非 emptyResponseMethods
和 emptyResponseCodes
允許难菌,否則不允許空響應),并將該 Data
傳遞到 dataPreprocessor
蔑滓。此響應 handler 對于自定義 Data
處理非常有用郊酒,但通常不是必需的遇绞。
StringResponseSerializer
對 DataRequest
或 DownloadRequest
調用 responseString(queue:encoding:completionHa
使用 StringResponseSerializer
驗證 Data
是否已正確返回(除非 emptyResponseMethods
和 emptyResponseCodes
允許,否則不允許空響應)并將該 Data
傳遞到 dataPreprocessor
燎窘。然后摹闽,使用從 HTTPURLResponse
解析的 String.Encoding
處理 Data
,并初始化一個 String
褐健。
JSONResponseSerializer
在 DataRequest
或 DownloadRequest
上調用 responseJSON(queue:options:completionHandler)
使用 JSONResponseSerializer
驗證 Data
是否已正確返回(除非 emptyResponseMethods
和 emptyResponseCodes
允許付鹿,否則不允許空響應),并將該 Data
傳遞給 dataPreprocessor
蚜迅。然后舵匾,使用提供的選項把預處理的 Data
傳遞給 JSONSerialization.jsonObject(with:options:)
。不再推薦使用此序列化程序谁不。相反坐梯,使用 DecodableResponseSerializer
提供了更好的快速體驗。
DecodableResponseSerializer
在 DataRequest
或 DownloadRequest
上調用 responseDecodable(of:queue:decoder:completionHandler)
使用 DecodableResponseSerializer
來驗證 Data
是否已正確返回(除非 emptyResponseMethods
和 emptyResponseCodes
允許刹帕,否則不允許空響應)吵血,并將該 Data
傳遞給 dataPreprocessor
。然后轩拨,預處理的 Data
傳遞給提供的 DataDecoder
践瓷,并解析為提供的或推斷的 Decodable
類型。
自定義響應 Handlers
除了包含在 Alamofire 中的靈活的 ResponseSerializer
之外亡蓉,還有其他定制響應處理的方法晕翠。
響應轉換
使用現(xiàn)有的 ResponseSerializer
然后轉換輸出是定制響應 handler 的最簡單方法之一。DataResponse
和 DownloadResponse
都有 map
砍濒、tryMap
淋肾、mapError
和 tryMapError
方法,這些方法可以轉換響應爸邢,同時保留與響應相關聯(lián)的元數(shù)據樊卓。例如,可以使用 map
從可解碼響應中提取屬性杠河,同時還保留以前的任何解析錯誤碌尔。
AF.request(...).responseDecodable(of: SomeType.self) { response in
let propertyResponse = response.map { $0.someProperty }
debugPrint(propertyResponse)
}
引發(fā)錯誤的轉換也可以與 tryMap
一起使用,可能用于執(zhí)行驗證:
AF.request(..).responseDecodable(of: SomeType.self) { response in
let propertyResponse = response.tryMap { try $0.someProperty.validated() }
debugPrint(propertyResponse)
}
創(chuàng)建自定義響應序列化器
當 Alamofire 提供的 ResponseSerializer
或響應轉換不夠靈活券敌,或者定制量很大時唾戚,創(chuàng)建 ResponseSerializer
是封裝該邏輯的好方法。集成自定義 ResponseSerializer
通常有兩個部分:創(chuàng)建遵循協(xié)議的類型和擴展相關請求類型以方便使用待诅。例如叹坦,如果服務器返回了一個特殊編碼的 String
(可能是用逗號分隔的值),那么這種格式的 ResponseSerializer
可能如下所示:
struct CommaDelimitedSerializer: ResponseSerializer {
func serialize(
request: URLRequest?,
response: HTTPURLResponse?,
data: Data?,
error: Error?
) throws -> [String] {
// Call the existing StringResponseSerializer to get many behaviors automatically.
let string = try StringResponseSerializer().serialize(
request: request,
response: response,
data: data,
error: error
)
return Array(string.split(separator: ","))
}
}
請注意卑雁,serialize
方法的返回類型要滿足 SerializedObject
associatedtype
要求募书。在更復雜的序列化器中绪囱,此返回類型本身可以是泛型的,從而允許泛型類型的序列化莹捡,如 DecodableResponseSerializer
所示鬼吵。
為了使 CommaDelimitedSerializer
更有用,可以添加其他行為道盏,比如允許通過將空 HTTP 方法和響應代碼傳遞給底層的 StringResponseSerializer
來自定義它們而柑。
網絡可達性
NetworkReachabilityManager
監(jiān)聽移動網絡和 WiFi
網絡接口的主機和地址的可達性變化。
let manager = NetworkReachabilityManager(host: "www.apple.com")
manager?.startListening { status in
print("Network Status Changed: \(status)")
}
一定要記住保存
manager
荷逞,否則不會報告狀態(tài)更改媒咳。另外,不要將 scheme 包含在host
字符串中种远,否則可達性將無法正常工作涩澡。
當使用網絡可達性來確定下一步要做什么時,需要記住一些重要的事情坠敷。
-
不要使用可達性來確定是否應發(fā)送網絡請求妙同。
- 你應該總是把請求發(fā)出去。
- 恢復可訪問性后膝迎,使用事件重試失敗的網絡請求粥帚。
- 盡管網絡請求可能仍然失敗,但現(xiàn)在是重試請求的好時機限次。
- 網絡可達性狀態(tài)可用于確定網絡請求失敗的原因芒涡。
- 如果網絡請求失敗,則更有用的方法是告訴用戶網絡請求由于離線而失敗卖漫,而不是更技術性的錯誤费尽,例如“請求超時”
或者衩椒,使用 RequestRetrier
(如內置的 RetryPolicy
)可能會更簡單脸甘、更可靠,而不是使用可訪問性更新來重試因網絡故障而失敗的請求耗溜。默認情況下突委,RetryPolicy
將在各種錯誤條件下重試等冪請求柏卤,包括離線的網絡連接。