Alamofire源碼解讀
AFNetworking的作者M(jìn)att Thompson 提出了一個(gè)新的類似AFNetworking的網(wǎng)絡(luò)基礎(chǔ)庫(kù)筋现,并且專門使用最新的Swift語(yǔ)言寫的珠移,名為 Alamofire.
對(duì)于使用OC的開(kāi)發(fā)者來(lái)說(shuō)一定十分熟悉AFNetworking這個(gè)框架韩脏,因?yàn)楝F(xiàn)在我們的app只要是有關(guān)于網(wǎng)絡(luò)訪問(wèn)的部分大部分都會(huì)通過(guò)這個(gè)框架來(lái)進(jìn)行網(wǎng)絡(luò)的訪問(wèn)。
而Alamofire 是 Swift 語(yǔ)言的 HTTP 網(wǎng)絡(luò)開(kāi)發(fā)工具包跨跨,功能強(qiáng)大蔑舞,支持各種 HTTP Method入偷、JSON、文件上傳眉枕、文件下載和多種認(rèn)證方法恶复。
所以當(dāng)我們采用Swift進(jìn)行開(kāi)發(fā)的時(shí)候,我們就有了更多的選擇,是采用OC的AFNetworking也好,還是采用Swift的Alamofire也行...
相信作為開(kāi)發(fā)者,大家都在其他地方或多或少的讀過(guò)關(guān)于AFNetworking的解析,有的甚至也自己做過(guò)AFNetworking的解析,
那么為什么還要再做Alamofire的解析呢?
原因有以下幾點(diǎn),
- 新的開(kāi)發(fā)語(yǔ)言,新的開(kāi)發(fā)思路
- 學(xué)習(xí)Swift規(guī)范開(kāi)發(fā)的一個(gè)很好的教程
- 網(wǎng)絡(luò)流程的學(xué)習(xí),請(qǐng)求怜森、響應(yīng)、解析谤牡、等等...
- AFAFNetworking和Alamofire之間的對(duì)比
OK 進(jìn)入正題.
首先作為一個(gè)新的網(wǎng)絡(luò)庫(kù),Alamofire同時(shí)作為AFnetworking的Swift版本,甚至是作為同一個(gè)作者的開(kāi)源庫(kù),無(wú)論從哪個(gè)角度來(lái)說(shuō),Alamofire都有很多和AFnetworking相似的地方...
如何對(duì)比,我們首先把Alamofire加上項(xiàng)目中看看
網(wǎng)上很容易找到方法,直接加入項(xiàng)目,或者是采用CocoaPods進(jìn)行安裝.
這里我也簡(jiǎn)單的展示一下
第一種方式
我們下載這個(gè)庫(kù)文件
https://github.com/Alamofire/Alamofire
我們創(chuàng)建一個(gè)項(xiàng)目并把a(bǔ)lamofire拖到整個(gè)項(xiàng)目根目錄中
打開(kāi)工程后選擇Alamofire.xcodeproj 拖入工程中
選擇自己的工程.xcodeproj->Build Phases->Link Binary With Libraries添加Alamofire.framework
之后可能需要在info.plist中添加(如果你的網(wǎng)路請(qǐng)求地址是http的話)
在需要的網(wǎng)絡(luò)請(qǐng)求的地方類似這樣使用即可
這樣手動(dòng)導(dǎo)入框架就已經(jīng)完成了
當(dāng)然作為開(kāi)發(fā)者,大部分都會(huì)使用CocoaPods的方式,這里我就不啰嗦了,大家自行百度使用CocoaPods的方式即可
OK,我們這里先不看使用的方式,我們開(kāi)始解讀源碼,我們打開(kāi)Alamofire我們可以看到所有的文件
如果使用的是CocoaPods的話也可以看到相同的
總共有17個(gè)文件
- Alamofire-->基本接口的封裝
- AFError-->錯(cuò)誤的封裝
- Notifications-->通知的封裝
- ParameterEncoding-->參數(shù)編碼的封裝
- Request-->請(qǐng)求的封裝 (核心)
- Response-->服務(wù)器返回?cái)?shù)據(jù)的封裝
- Result-->請(qǐng)求結(jié)果的封裝
- SessionDelegate-->會(huì)話代理的封裝
- SessionManager-->會(huì)話管理的封裝 (核心)
- TaskDelegate-->任務(wù)代理的封裝
- DispatchQueue+Alamofire-->GCD的封裝
- MultipartFormData-->多表單數(shù)據(jù)的封裝
- NetworkReachabilityManager-->網(wǎng)絡(luò)狀態(tài)管理的封裝
- ResponseSerialization-->響應(yīng)序列化管理的封裝
- ServerTrustPolicy-->安全策略的封裝
- Timeline-->時(shí)間軸的封裝
- Validation-->服務(wù)器響應(yīng)的驗(yàn)證
我會(huì)按照網(wǎng)絡(luò)請(qǐng)求的整個(gè)過(guò)程來(lái)逐一解析
OK,作為一個(gè)網(wǎng)絡(luò)庫(kù),第一件我們會(huì)干的是什么事情呢?我會(huì)創(chuàng)造一個(gè)請(qǐng)求,
這里面包括,請(qǐng)求的地址,參數(shù),策略等等,然后由一個(gè)會(huì)話管理發(fā)起請(qǐng)求.并接受相應(yīng),
所以我們就先看這個(gè)ParameterEncoding
為什么呢?我們都知道,我們發(fā)起一個(gè)請(qǐng)求,會(huì)有這樣的一些設(shè)置,設(shè)置請(qǐng)求方式:get\post\put\delete...然后我們會(huì)設(shè)置請(qǐng)求協(xié)議:http\ftp...,接著設(shè)置域名,設(shè)置主機(jī)地址,路徑,端口號(hào),參數(shù)....
這樣才能夠組成一次完整的請(qǐng)求.否則我們連請(qǐng)求地址都不對(duì),還談什么請(qǐng)求呢?
ParameterEncoding部分
/// HTTP method definitions.
///
/// See https://tools.ietf.org/html/rfc7231#section-4.3
public enum HTTPMethod: String {
case options = "OPTIONS"
case get = "GET"
case head = "HEAD"
case post = "POST"
case put = "PUT"
case patch = "PATCH"
case delete = "DELETE"
case trace = "TRACE"
case connect = "CONNECT"
}
我們?cè)赑arameterEncoding中可以看到,這是一個(gè)http請(qǐng)求類型的枚舉.
在Swift中,枚舉跟OC有了很大的區(qū)別,使用的是case的方式.
public protocol ParameterEncoding {
/// Creates a URL request by encoding parameters and applying them onto an existing request.
///
/// - parameter urlRequest: The request to have parameters applied.
/// - parameter parameters: The parameters to apply.
///
/// - throws: An `AFError.parameterEncodingFailed` error if encoding fails.
///
/// - returns: The encoded request.
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest
}
關(guān)于ParameterEncoding協(xié)議
這個(gè)協(xié)議中只有一個(gè)函數(shù)副硅,該函數(shù)需要兩個(gè)參數(shù):
urlRequest 該參數(shù)需要實(shí)現(xiàn)URLRequestConvertible協(xié)議,實(shí)現(xiàn)URLRequestConvertible協(xié)議的對(duì)象能夠轉(zhuǎn)換成URLRequest
parameters 參數(shù)拓哟,其類型為Parameters想许,也就是字典:public typealias Parameters = [String: Any]
該函數(shù)返回值類型為URLRequest。通過(guò)觀察這個(gè)函數(shù)断序,我們就明白了這個(gè)函數(shù)的目的就是把參數(shù)綁定到urlRequest之中流纹,至于返回的urlRequest是不是之前的urlRequest,這個(gè)不一定违诗,另一個(gè)比較重要的是該函數(shù)會(huì)拋出異常漱凝,因此在本篇后邊的解讀中會(huì)說(shuō)明該異常的來(lái)源。
我們已經(jīng)知道了URLEncoding就是和URL相關(guān)的編碼诸迟。當(dāng)把參數(shù)編碼到httpBody中這種情況是不受限制的茸炒,而直接編碼到URL中就會(huì)受限制,只有當(dāng)HTTPMethod為GET, HEAD and DELETE時(shí)才直接編碼到URL中阵苇。
public enum Destination {
case methodDependent, queryString, httpBody
}
methodDependent 根據(jù)HTTPMethod自動(dòng)判斷采取哪種編碼方式
queryString 拼接到URL中
httpBody 拼接到httpBody中
// MARK: Properties
/// Returns a default `URLEncoding` instance.
public static var `default`: URLEncoding { return URLEncoding() }
/// Returns a `URLEncoding` instance with a `.methodDependent` destination.
public static var methodDependent: URLEncoding { return URLEncoding() }
/// Returns a `URLEncoding` instance with a `.queryString` destination.
public static var queryString: URLEncoding { return URLEncoding(destination: .queryString) }
/// Returns a `URLEncoding` instance with an `.httpBody` destination.
public static var httpBody: URLEncoding { return URLEncoding(destination: .httpBody) }
/// The destination defining where the encoded query string is to be applied to the URL request.
public let destination: Destination
// MARK: Initialization
/// Creates a `URLEncoding` instance using the specified destination.
///
/// - parameter destination: The destination defining where the encoded query string is to be applied.
///
/// - returns: The new `URLEncoding` instance.
public init(destination: Destination = .methodDependent) {
self.destination = destination
從這個(gè)地方我們可以看出Alamofire的URLEncoding提供了默認(rèn)的初始化選擇的Destination是methodDependent壁公,除了default這個(gè)單利外,又增加了其他的三個(gè)
這里采用了類屬性類創(chuàng)建單例
public static var `default`: URLEncoding { return URLEncoding() }
接下來(lái)是encode的方法
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()//獲取UrlRequest
guard let parameters = parameters else { return urlRequest }//如果參數(shù)為nil就直接返回urlRequest
if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {// 參數(shù)編碼到url
guard let url = urlRequest.url else {
throw AFError.parameterEncodingFailed(reason: .missingURL)
}
//分解url
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
//把原有的url中的query百分比編碼后在拼接上編碼后的參數(shù)
let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
urlComponents.percentEncodedQuery = percentEncodedQuery
urlRequest.url = urlComponents.url
}
} else {// 參數(shù)編碼到httpBody
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {// 設(shè)置Content-Type
urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
}
return urlRequest
}
所以以上是url的組成,只有完整組成了url并且定義了請(qǐng)求方式,請(qǐng)求類型....才能夠正確的獲取到請(qǐng)求和響應(yīng).
上面的是參數(shù)的組成,但是在這之前參數(shù)是如何對(duì)應(yīng),轉(zhuǎn)碼并且轉(zhuǎn)化成字符串的呢?
我們可以看到上面有用到一個(gè)query的方法,我們學(xué)習(xí)一下
private func query(_ parameters: [String: Any]) -> String {
var components: [(String, String)] = []
for key in parameters.keys.sorted(by: <) {
let value = parameters[key]!
components += queryComponents(fromKey: key, value: value)
}
return components.map { "\($0)=\($1)" }.joined(separator: "&")
}
看到了我們很熟悉的拼接字符串
上邊函數(shù)的整體思路是:
寫一個(gè)數(shù)組绅项,這個(gè)數(shù)組中存放的是元組數(shù)據(jù)紊册,元組中存放的是key和字符串類型的value
遍歷參數(shù),對(duì)參數(shù)做進(jìn)一步的處理快耿,然后拼接到數(shù)組中
進(jìn)一步處理數(shù)組內(nèi)部的元組數(shù)據(jù)囊陡,把元組內(nèi)部的數(shù)據(jù)用=號(hào)拼接,然后用符號(hào)&把數(shù)組拼接成字符串
這里面用到了
public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
var components: [(String, String)] = []
if let dictionary = value as? [String: Any] {
for (nestedKey, value) in dictionary {
components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
}
} else if let array = value as? [Any] {
for value in array {
components += queryComponents(fromKey: "\(key)[]", value: value)
}
} else if let value = value as? NSNumber {
if value.isBool {
components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
} else {
components.append((escape(key), escape("\(value)")))
}
} else if let bool = value as? Bool {
components.append((escape(key), escape((bool ? "1" : "0"))))
} else {
components.append((escape(key), escape("\(value)")))
}
return components
}
我們可以看到value是Any的類型,所以在這個(gè)方法里面是做類型的轉(zhuǎn)換同時(shí)做轉(zhuǎn)碼,我們?cè)倏纯崔D(zhuǎn)碼方法
public func escape(_ string: String) -> String {
let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowedCharacterSet = CharacterSet.urlQueryAllowed
allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
var escaped = ""
//==========================================================================================================
//
// Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few
// hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no
// longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more
// info, please refer to:
//
// - https://github.com/Alamofire/Alamofire/issues/206
//
//==========================================================================================================
if #available(iOS 8.3, *) {
escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
} else {
let batchSize = 50
var index = string.startIndex
while index != string.endIndex {
let startIndex = index
let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex
let range = startIndex..<endIndex
let substring = string.substring(with: range)
escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? substring
index = endIndex
}
}
return escaped
}
不同于網(wǎng)上流行的轉(zhuǎn)碼方式,但是大致上的思路仍然是一樣的,而且我可以跟你說(shuō),這個(gè)方法很優(yōu)秀,網(wǎng)上流傳的轉(zhuǎn)碼的方法有的時(shí)候仍然有局限性,但是這個(gè)不會(huì),最起碼我至今沒(méi)有發(fā)現(xiàn),所以無(wú)論是我用不用Alamofire,我都會(huì)把這個(gè)轉(zhuǎn)碼的方式給單獨(dú)拿出來(lái),在我需要的時(shí)候使用,屢試不爽...
到這里掀亥,URLEncoding的全部?jī)?nèi)容就分析完畢了撞反,我們把不同的功能劃分成不同的函數(shù),這種做法最大的好處就是我們可以使用單獨(dú)的函數(shù)做獨(dú)立的事情搪花。我完全可以使用escape這個(gè)函數(shù)轉(zhuǎn)義任何字符串遏片。
繼續(xù)向下看則是JSONEncoding
JSONEncoding的主要作用是把參數(shù)以JSON的形式編碼到request之中,當(dāng)然是通過(guò)request的httpBody進(jìn)行賦值的撮竿。JSONEncoding提供了兩種處理函數(shù)吮便,一種是對(duì)普通的字典參數(shù)進(jìn)行編碼,另一種是對(duì)JSONObject進(jìn)行編碼倚聚,處理這兩種情況的函數(shù)基本上是相同的
public struct JSONEncoding: ParameterEncoding {
// MARK: Properties
/// Returns a `JSONEncoding` instance with default writing options.
public static var `default`: JSONEncoding { return JSONEncoding() }
/// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options.
public static var prettyPrinted: JSONEncoding { return JSONEncoding(options: .prettyPrinted) }
/// The options for writing the parameters as JSON data.
public let options: JSONSerialization.WritingOptions
// MARK: Initialization
/// Creates a `JSONEncoding` instance using the specified options.
///
/// - parameter options: The options for writing the parameters as JSON data.
///
/// - returns: The new `JSONEncoding` instance.
public init(options: JSONSerialization.WritingOptions = []) {
self.options = options
}
這里邊值得注意的是JSONSerialization.WritingOptions,也就是JSON序列化的寫入方式线衫。WritingOptions是一個(gè)結(jié)構(gòu)體,系統(tǒng)提供了一個(gè)選項(xiàng):prettyPrinted,意思是更好的打印效果
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
guard let parameters = parameters else { return urlRequest }
do {
let data = try JSONSerialization.data(withJSONObject: parameters, options: options)
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = data
} catch {
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
}
return urlRequest
}
public func encode(_ urlRequest: URLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
guard let jsonObject = jsonObject else { return urlRequest }
do {
let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options)
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = data
} catch {
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
}
return urlRequest
}
至于encode方法,第一個(gè)函數(shù)實(shí)現(xiàn)了ParameterEncoding協(xié)議惑折,第二個(gè)參數(shù)作為擴(kuò)展授账,函數(shù)中最核心的內(nèi)容是把參數(shù)變成Data類型枯跑,然后給httpBody賦值,需要注意的是異常處理白热。
至于接下來(lái)的PropertyListEncoding,基本上和JSONEncoding差不多是一樣的,這里就不介紹了
Request部分
有了參數(shù)和地址,那當(dāng)然的就是發(fā)起請(qǐng)求,自然而然的就到了request部分
enum RequestTask {
case data(TaskConvertible?, URLSessionTask?)
case download(TaskConvertible?, URLSessionTask?)
case upload(TaskConvertible?, URLSessionTask?)
case stream(TaskConvertible?, URLSessionTask?)
}
這是一個(gè)task的枚舉,分別表示各種請(qǐng)求的方式,我們可以熟悉的看到系統(tǒng)的類URLSessionTask,所以說(shuō)Alamofire也是基于URLSession來(lái)寫的網(wǎng)絡(luò)框架
/// The underlying task.
open var task: URLSessionTask? { return delegate.task }
/// The session belonging to the underlying task.
open let session: URLSession
/// The request sent or to be sent to the server.
open var request: URLRequest? { return task?.originalRequest }
/// The response received from the server, if any.
open var response: HTTPURLResponse? { return task?.response as? HTTPURLResponse }
/// The number of times the request has been retried.
open internal(set) var retryCount: UInt = 0
let originalTask: TaskConvertible?
var startTime: CFAbsoluteTime?
var endTime: CFAbsoluteTime?
var validations: [() -> Void] = []
private var taskDelegate: TaskDelegate
private var taskDelegateLock = NSLock()
// MARK: Lifecycle
init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
self.session = session
switch requestTask {
case .data(let originalTask, let task):
taskDelegate = DataTaskDelegate(task: task)
self.originalTask = originalTask
case .download(let originalTask, let task):
taskDelegate = DownloadTaskDelegate(task: task)
self.originalTask = originalTask
case .upload(let originalTask, let task):
taskDelegate = UploadTaskDelegate(task: task)
self.originalTask = originalTask
case .stream(let originalTask, let task):
taskDelegate = TaskDelegate(task: task)
self.originalTask = originalTask
}
delegate.error = error
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
}
這里是request的初始化和一些設(shè)置的默認(rèn)屬性...
這部分是auth
// MARK: Authentication
/// Associates an HTTP Basic credential with the request.
///
/// - parameter user: The user.
/// - parameter password: The password.
/// - parameter persistence: The URL credential persistence. `.ForSession` by default.
///
/// - returns: The request.
@discardableResult
open func authenticate(
user: String,
password: String,
persistence: URLCredential.Persistence = .forSession)
-> Self
{
let credential = URLCredential(user: user, password: password, persistence: persistence)
return authenticate(usingCredential: credential)
}
/// Associates a specified credential with the request.
///
/// - parameter credential: The credential.
///
/// - returns: The request.
@discardableResult
open func authenticate(usingCredential credential: URLCredential) -> Self {
delegate.credential = credential
return self
}
/// Returns a base64 encoded basic authentication credential as an authorization header tuple.
///
/// - parameter user: The user.
/// - parameter password: The password.
///
/// - returns: A tuple with Authorization header and credential value if encoding succeeds, `nil` otherwise.
open static func authorizationHeader(user: String, password: String) -> (key: String, value: String)? {
guard let data = "\(user):\(password)".data(using: .utf8) else { return nil }
let credential = data.base64EncodedString(options: [])
return (key: "Authorization", value: "Basic \(credential)")
}
接下來(lái)并沒(méi)有什么特殊的方式
類似于NSURLSession 分別有對(duì)task的恢復(fù),暫停和取消
/// Resumes the request.
open func resume() {
guard let task = task else { delegate.queue.isSuspended = false ; return }
if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }
task.resume()
NotificationCenter.default.post(
name: Notification.Name.Task.DidResume,
object: self,
userInfo: [Notification.Key.Task: task]
)
}
/// Suspends the request.
open func suspend() {
guard let task = task else { return }
task.suspend()
NotificationCenter.default.post(
name: Notification.Name.Task.DidSuspend,
object: self,
userInfo: [Notification.Key.Task: task]
)
}
/// Cancels the request.
open func cancel() {
guard let task = task else { return }
task.cancel()
NotificationCenter.default.post(
name: Notification.Name.Task.DidCancel,
object: self,
userInfo: [Notification.Key.Task: task]
)
}
對(duì)應(yīng)的實(shí)現(xiàn)會(huì)發(fā)送對(duì)應(yīng)的通知而已
接下來(lái)是DataReuqest,作為請(qǐng)求的一種方式,繼承自request,是用來(lái)管理一個(gè)潛在的URLSessionDataTask.
// MARK: Helper Types
struct Requestable: TaskConvertible {
let urlRequest: URLRequest
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
do {
let urlRequest = try self.urlRequest.adapt(using: adapter)
return queue.sync { session.dataTask(with: urlRequest) }
} catch {
throw AdaptError(error: error)
}
}
}
// MARK: Properties
/// The request sent or to be sent to the server.
open override var request: URLRequest? {
if let request = super.request { return request }
if let requestable = originalTask as? Requestable { return requestable.urlRequest }
return nil
}
/// The progress of fetching the response data from the server for the request.
open var progress: Progress { return dataDelegate.progress }
var dataDelegate: DataTaskDelegate { return delegate as! DataTaskDelegate }
// MARK: Stream
/// Sets a closure to be called periodically during the lifecycle of the request as data is read from the server.
///
/// This closure returns the bytes most recently received from the server, not including data from previous calls.
/// If this closure is set, data will only be available within this closure, and will not be saved elsewhere. It is
/// also important to note that the server data in any `Response` object will be `nil`.
///
/// - parameter closure: The code to be executed periodically during the lifecycle of the request.
///
/// - returns: The request.
@discardableResult
open func stream(closure: ((Data) -> Void)? = nil) -> Self {
dataDelegate.dataStream = closure
return self
}
// MARK: Progress
/// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
///
/// - parameter queue: The dispatch queue to execute the closure on.
/// - parameter closure: The code to be executed periodically as data is read from the server.
///
/// - returns: The request.
@discardableResult
open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
dataDelegate.progressHandler = (closure, queue)
return self
}
源碼并不是很復(fù)雜,主要分成幾個(gè)小的部分.請(qǐng)求,獲取請(qǐng)求urlRequest,通過(guò)Requestable判斷能夠發(fā)起請(qǐng)求,可以的話則發(fā)起請(qǐng)求,之后測(cè)試請(qǐng)求的過(guò)程中的季度,代理等,以及請(qǐng)求返回的數(shù)據(jù)
DownloadRequest稍微復(fù)雜一點(diǎn),因?yàn)槭窍螺d,所以無(wú)可避免的數(shù)據(jù)量會(huì)大一些,從而需要對(duì)暫土仓恢復(fù)等做個(gè)稍微復(fù)雜的處理
public struct DownloadOptions: OptionSet {
/// Returns the raw bitmask value of the option and satisfies the `RawRepresentable` protocol.
public let rawValue: UInt
/// A `DownloadOptions` flag that creates intermediate directories for the destination URL if specified.
public static let createIntermediateDirectories = DownloadOptions(rawValue: 1 << 0)
/// A `DownloadOptions` flag that removes a previous file from the destination URL if specified.
public static let removePreviousFile = DownloadOptions(rawValue: 1 << 1)
/// Creates a `DownloadFileDestinationOptions` instance with the specified raw value.
///
/// - parameter rawValue: The raw bitmask value for the option.
///
/// - returns: A new log level instance.
public init(rawValue: UInt) {
self.rawValue = rawValue
}
}
下載方式的選擇,創(chuàng)建中間目錄,移除之前的文件等等,
相較于Requestable來(lái)說(shuō)Downloadable會(huì)稍微復(fù)雜點(diǎn),是否可以下載的話有對(duì)url的判斷,同樣還有對(duì)數(shù)據(jù)的判斷,
enum Downloadable: TaskConvertible {
case request(URLRequest)
case resumeData(Data)
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
do {
let task: URLSessionTask
switch self {
case let .request(urlRequest):
let urlRequest = try urlRequest.adapt(using: adapter)
task = queue.sync { session.downloadTask(with: urlRequest) }
case let .resumeData(resumeData):
task = queue.sync { session.downloadTask(withResumeData: resumeData) }
}
return task
} catch {
throw AdaptError(error: error)
}
}
}
/// The request sent or to be sent to the server.
open override var request: URLRequest? {
if let request = super.request { return request }
if let downloadable = originalTask as? Downloadable, case let .request(urlRequest) = downloadable {
return urlRequest
}
return nil
}
/// The resume data of the underlying download task if available after a failure.
open var resumeData: Data? { return downloadDelegate.resumeData }
/// The progress of downloading the response data from the server for the request.
open var progress: Progress { return downloadDelegate.progress }
var downloadDelegate: DownloadTaskDelegate { return delegate as! DownloadTaskDelegate }
// MARK: State
/// Cancels the request.
open override func cancel() {
downloadDelegate.downloadTask.cancel { self.downloadDelegate.resumeData = $0 }
NotificationCenter.default.post(
name: Notification.Name.Task.DidCancel,
object: self,
userInfo: [Notification.Key.Task: task as Any]
)
}
// MARK: Progress
/// Sets a closure to be called periodically during the lifecycle of the `Request` as data is read from the server.
///
/// - parameter queue: The dispatch queue to execute the closure on.
/// - parameter closure: The code to be executed periodically as data is read from the server.
///
/// - returns: The request.
@discardableResult
open func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
downloadDelegate.progressHandler = (closure, queue)
return self
}
// MARK: Destination
/// Creates a download file destination closure which uses the default file manager to move the temporary file to a
/// file URL in the first available directory with the specified search path directory and search path domain mask.
///
/// - parameter directory: The search path directory. `.DocumentDirectory` by default.
/// - parameter domain: The search path domain mask. `.UserDomainMask` by default.
///
/// - returns: A download file destination closure.
open class func suggestedDownloadDestination(
for directory: FileManager.SearchPathDirectory = .documentDirectory,
in domain: FileManager.SearchPathDomainMask = .userDomainMask)
-> DownloadFileDestination
{
return { temporaryURL, response in
let directoryURLs = FileManager.default.urls(for: directory, in: domain)
if !directoryURLs.isEmpty {
return (directoryURLs[0].appendingPathComponent(response.suggestedFilename!), [])
}
return (temporaryURL, [])
}
}
請(qǐng)求的方式一樣,當(dāng)然多了幾個(gè)內(nèi)容,下載失敗之后的可恢復(fù)數(shù)據(jù),建議下載的目的地等.但是大致上都是一樣的,至于后面的UploadRequest和StreamRequest也都是大致一樣的內(nèi)容而已.相較于只是細(xì)節(jié)上的一些優(yōu)化.
TaskDelegate部分
因?yàn)橛辛藃equest那么相對(duì)應(yīng)的有了task對(duì)象來(lái)管理這個(gè)request,同樣的,就產(chǎn)生了各種狀態(tài)或者進(jìn)度.,那么TaskDelegate就是task的各種協(xié)議
這塊相對(duì)來(lái)說(shuō)就很簡(jiǎn)單了主要的部分是TaskDelegate部分,其包含的主要內(nèi)容是
// MARK: Properties
/// The serial operation queue used to execute all operations after the task completes.
open let queue: OperationQueue
/// The data returned by the server.
public var data: Data? { return nil }
/// The error generated throughout the lifecyle of the task.
public var error: Error?
var task: URLSessionTask? {
didSet { reset() }
}
var initialResponseTime: CFAbsoluteTime?
var credential: URLCredential?
var metrics: AnyObject? // URLSessionTaskMetrics
// MARK: Lifecycle
init(task: URLSessionTask?) {
self.task = task
self.queue = {
let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
operationQueue.isSuspended = true
operationQueue.qualityOfService = .utility
return operationQueue
}()
}
func reset() {
error = nil
initialResponseTime = nil
}
// MARK: URLSessionTaskDelegate
var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)?
有數(shù)據(jù),有各種回調(diào)的閉包,有錯(cuò)誤...
此外就是幾個(gè)協(xié)議方法
queue: OperationQueue 很明顯這是一個(gè)隊(duì)列,隊(duì)列中可以添加很多operation屋确。隊(duì)列是可以被暫停的纳击,通過(guò)把isSuspended設(shè)置為true就可以讓隊(duì)列中的所有operation暫停,直到isSuspended設(shè)置為false后攻臀,operation才會(huì)開(kāi)始執(zhí)行焕数。在Alamofire中,放入該隊(duì)列的operation有一下幾種情況:
注意:該隊(duì)列會(huì)在任務(wù)完成之后把isSuspended設(shè)置為false
一個(gè)Request的endTime刨啸,在任務(wù)完成后調(diào)用堡赔,就可以為Request設(shè)置請(qǐng)求結(jié)束時(shí)間
response處理,Alamofire中的響應(yīng)回調(diào)是鏈?zhǔn)降纳枇砭褪前堰@些回調(diào)函數(shù)通過(guò)operation添加到隊(duì)列中善已,因此也保證了回調(diào)函數(shù)的訪問(wèn)順序是正確的
上傳數(shù)據(jù)成功后,刪除臨時(shí)文件离例,這個(gè)后續(xù)的文章會(huì)解釋的
data: Data? 表示服務(wù)器返回的Data换团,這個(gè)可能為空
error: Error?表示該代理生命周期內(nèi)有可能出現(xiàn)的錯(cuò)誤,這一點(diǎn)很重要宫蛆,我們?cè)趯懸粋€(gè)代理或者manager的時(shí)候艘包,可以添加這么一個(gè)錯(cuò)誤屬性,專門抓取生命周期內(nèi)的錯(cuò)誤洒扎。
task: URLSessionTask? 表示一個(gè)task辑甜,對(duì)于本代理而言衰絮,task是很重要的一個(gè)屬性袍冷。
initialResponseTime: CFAbsoluteTime? 當(dāng)task是URLSessionDataTask時(shí),表示接收到數(shù)據(jù)的時(shí)間猫牡;當(dāng)task是URLSessionDownloadTask時(shí)胡诗,表示開(kāi)始寫數(shù)據(jù)的時(shí)間;當(dāng)task是URLSessionUploadTask時(shí)淌友,表示上傳數(shù)據(jù)的時(shí)間煌恢;
credential: URLCredential? 表示證書,如果給該代理設(shè)置了這個(gè)屬性震庭,在它里邊的證書驗(yàn)證方法中會(huì)備用到
metrics: AnyObject? apple提供了一個(gè)統(tǒng)計(jì)task信息的類URLSessionTaskMetrics,可以統(tǒng)計(jì)跟task相關(guān)的一些信息瑰抵,包括和task相關(guān)的所有事務(wù),task的開(kāi)始和結(jié)束時(shí)間器联,task的重定向的次數(shù)二汛。
@objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)
func urlSession(
_ session: URLSession,
task: URLSessionTask,
willPerformHTTPRedirection response: HTTPURLResponse,
newRequest request: URLRequest,
completionHandler: @escaping (URLRequest?) -> Void)
{
var redirectRequest: URLRequest? = request
if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
}
completionHandler(redirectRequest)
}
這個(gè)函數(shù)處理的問(wèn)題是請(qǐng)求重定向問(wèn)題
@objc(URLSession:task:didReceiveChallenge:completionHandler:)
func urlSession(
_ session: URLSession,
task: URLSessionTask,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
{
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
var credential: URLCredential?
if let taskDidReceiveChallenge = taskDidReceiveChallenge {
(disposition, credential) = taskDidReceiveChallenge(session, task, challenge)
} else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
let host = challenge.protectionSpace.host
if
let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
let serverTrust = challenge.protectionSpace.serverTrust
{
if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
disposition = .useCredential
credential = URLCredential(trust: serverTrust)
} else {
disposition = .cancelAuthenticationChallenge
}
}
} else {
if challenge.previousFailureCount > 0 {
disposition = .rejectProtectionSpace
} else {
credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
if credential != nil {
disposition = .useCredential
}
}
}
completionHandler(disposition, credential)
}
改函數(shù)用于處理驗(yàn)證相關(guān)的事務(wù)
如果服務(wù)器需要驗(yàn)證客戶端的婿崭,我們只需要給TaskDelegate的 var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?賦值就行了。
這里有一個(gè)很重要的問(wèn)題肴颊,HTTPS并不會(huì)觸發(fā)上邊的回調(diào)函數(shù)氓栈,原因就是NSURLSession內(nèi)部有一個(gè)根證書,內(nèi)部會(huì)跟服務(wù)器的證書進(jìn)行驗(yàn)證婿着,如果服務(wù)器的證書是證書機(jī)構(gòu)頒發(fā)的話授瘦,就可以順利通過(guò)驗(yàn)證,否則會(huì)報(bào)錯(cuò)竟宋。
另一個(gè)很重要的問(wèn)題是提完,上邊的方法在何種情況下觸發(fā),按照apple的說(shuō)法URL Session Programming Guide,當(dāng)服務(wù)器響應(yīng)頭中包含WWW-Authenticate或者使用 proxy authentication TLS trust validation時(shí)丘侠,上邊的方法就會(huì)被觸發(fā)氯葬,其他情況下都不會(huì)觸發(fā)。
Alamofire是雙向驗(yàn)證的
雙向驗(yàn)證的過(guò)程:
當(dāng)服務(wù)器返回的challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust時(shí)婉陷,服務(wù)器提供了一個(gè)服務(wù)器信任的證書帚称,但這個(gè)證書也僅僅是服務(wù)器自己信任的,攻擊者完全可以提供一個(gè)證書騙過(guò)客戶端秽澳,基于這個(gè)問(wèn)題闯睹,客戶端需要驗(yàn)證服務(wù)端的證書
Alamofire中通過(guò)ServerTrustPolicy.swift這個(gè)類來(lái)驗(yàn)證證書,大概過(guò)程就是拿本地的證書跟服務(wù)端返回的證書進(jìn)行對(duì)比担神,如果客戶端存在相同的證書就表示通過(guò)楼吃,這個(gè)過(guò)程我會(huì)在ServerTrustPolicy.swift那篇文章中給出詳細(xì)的解答
因此,客戶端和服務(wù)端要建立SSL只需要兩步就行了:
服務(wù)端返回WWW-Authenticate響應(yīng)頭妄讯,并返回自己信任證書
客戶端驗(yàn)證證書孩锡,然后用證書中的公鑰把數(shù)據(jù)加密后發(fā)送給服務(wù)端
@objc(URLSession:task:needNewBodyStream:)
func urlSession(
_ session: URLSession,
task: URLSessionTask,
needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
{
var bodyStream: InputStream?
if let taskNeedNewBodyStream = taskNeedNewBodyStream {
bodyStream = taskNeedNewBodyStream(session, task)
}
completionHandler(bodyStream)
}
當(dāng)給task的Request提供一個(gè)body stream時(shí)才會(huì)調(diào)用,我們不需要關(guān)心這個(gè)方法亥贸,即使我們通過(guò)fileURL或者NSData上傳數(shù)據(jù)時(shí)躬窜,該函數(shù)也不會(huì)被調(diào)用,使用場(chǎng)景很少。
@objc(URLSession:task:didCompleteWithError:)
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let taskDidCompleteWithError = taskDidCompleteWithError {
taskDidCompleteWithError(session, task, error)
} else {
if let error = error {
if self.error == nil { self.error = error }
if
let downloadDelegate = self as? DownloadTaskDelegate,
let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
{
downloadDelegate.resumeData = resumeData
}
}
queue.isSuspended = false
}
}
該函數(shù)在請(qǐng)求完成后被調(diào)用,值得注意的是error不為nil的情況飞傀,除了給自身的error屬性賦值外,針對(duì)下載任務(wù)做了特殊處理默垄,就是把當(dāng)前已經(jīng)下載的數(shù)據(jù)保存在downloadDelegate.resumeData中,有點(diǎn)像斷點(diǎn)下載甚纲。
接下來(lái)的DataTaskDelegate繼承自TaskDelegate,實(shí)現(xiàn)了URLSessionDataDelegate協(xié)議
var dataTask: URLSessionDataTask { return task as! URLSessionDataTask }
override var data: Data? {
if dataStream != nil {
return nil
} else {
return mutableData
}
}
var progress: Progress
var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)?
var dataStream: ((_ data: Data) -> Void)?
private var totalBytesReceived: Int64 = 0
private var mutableData: Data
private var expectedContentLength: Int64?
dataTask: URLSessionDataTask DataTaskDelegate管理URLSessionDataTask
data: Data? 同樣是返回Data口锭,但這里有一點(diǎn)不同,如果定義了dataStream方法的話介杆,這個(gè)data返回為nil
progress: Progress 進(jìn)度
progressHandler 這不是函數(shù)鹃操,是一個(gè)元組况既,什么時(shí)候調(diào)用,在下邊的方法中給出說(shuō)明
dataStream 自定義的數(shù)據(jù)處理函數(shù)
totalBytesReceived 已經(jīng)接受的數(shù)據(jù)
mutableData 保存數(shù)據(jù)的容器
expectedContentLength 需要接受的數(shù)據(jù)的總大小
有四個(gè)函數(shù)
var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?
第一個(gè)
func urlSession(
_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
{
var disposition: URLSession.ResponseDisposition = .allow
expectedContentLength = response.expectedContentLength
if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
disposition = dataTaskDidReceiveResponse(session, dataTask, response)
}
completionHandler(disposition)
}
當(dāng)收到服務(wù)端的響應(yīng)后组民,該方法被觸發(fā)棒仍。在這個(gè)函數(shù)中,我們能夠獲取到和數(shù)據(jù)相關(guān)的一些參數(shù)臭胜,大家可以想象成響應(yīng)頭莫其。Alamofire中給出了一個(gè)函數(shù)dataTaskDidReceiveResponse,我們可以利用這個(gè)函數(shù)控制是不是要繼續(xù)獲取數(shù)據(jù)耸三,默認(rèn)是.allow乱陡。
第二個(gè)
func urlSession(
_ session: URLSession,
dataTask: URLSessionDataTask,
didBecome downloadTask: URLSessionDownloadTask)
{
dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask)
}
當(dāng)我們把URLSession.ResponseDisposition設(shè)置成becomeDownload就會(huì)觸發(fā)上邊的第二個(gè)函數(shù),在函數(shù)中會(huì)提供一個(gè)新的downloadTask仪壮。這就給我們一個(gè)把dataTask轉(zhuǎn)換成downloadTask的機(jī)會(huì)
第三個(gè)
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
if let dataTaskDidReceiveData = dataTaskDidReceiveData {
dataTaskDidReceiveData(session, dataTask, data)
} else {
if let dataStream = dataStream {
dataStream(data)
} else {
mutableData.append(data)
}
let bytesReceived = Int64(data.count)
totalBytesReceived += bytesReceived
let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
progress.totalUnitCount = totalBytesExpected
progress.completedUnitCount = totalBytesReceived
if let progressHandler = progressHandler {
progressHandler.queue.async { progressHandler.closure(self.progress) }
}
}
}
Alamofire把數(shù)據(jù)放入對(duì)象中的過(guò)程,也是下載的核心方法..
第四個(gè)
func urlSession(
_ session: URLSession,
dataTask: URLSessionDataTask,
willCacheResponse proposedResponse: CachedURLResponse,
completionHandler: @escaping (CachedURLResponse?) -> Void)
{
var cachedResponse: CachedURLResponse? = proposedResponse
if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse)
}
completionHandler(cachedResponse)
}
該函數(shù)用于處理是否需要緩存響應(yīng)憨颠,Alamofire默認(rèn)是緩存這些response的,但是每次發(fā)請(qǐng)求积锅,它不會(huì)再緩存中讀取爽彤。
至于后續(xù)的DownloadTaskDelegate...基本都是差不多的實(shí)現(xiàn),不通的類型處理的思路稍微不同...
Response部分
首先是默認(rèn)的DataResponse
DefaultDataResponse
public struct DefaultDataResponse {
/// The URL request sent to the server.
public let request: URLRequest?
/// The server's response to the URL request.
public let response: HTTPURLResponse?
/// The data returned by the server.
public let data: Data?
/// The error encountered while executing or validating the request.
public let error: Error?
/// The timeline of the complete lifecycle of the request.
public let timeline: Timeline
var _metrics: AnyObject?
/// Creates a `DefaultDataResponse` instance from the specified parameters.
///
/// - Parameters:
/// - request: The URL request sent to the server.
/// - response: The server's response to the URL request.
/// - data: The data returned by the server.
/// - error: The error encountered while executing or validating the request.
/// - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.
/// - metrics: The task metrics containing the request / response statistics. `nil` by default.
public init(
request: URLRequest?,
response: HTTPURLResponse?,
data: Data?,
error: Error?,
timeline: Timeline = Timeline(),
metrics: AnyObject? = nil)
{
self.request = request
self.response = response
self.data = data
self.error = error
self.timeline = timeline
}
}
簡(jiǎn)單的理解
request:URLRequest,本次訪問(wèn)數(shù)據(jù)的urlRequest,包含了請(qǐng)求的地址,方式...
response:服務(wù)器的響應(yīng),有返回的狀態(tài)碼,頭部信息等
data:服務(wù)器返回的數(shù)據(jù),可能為空
error:錯(cuò)誤的類型
timeline:請(qǐng)求的完整生命周期的時(shí)間
通過(guò)一個(gè)默認(rèn)的響應(yīng)結(jié)果可以看到大致其包含的信息有狀態(tài)和數(shù)據(jù)(錯(cuò)誤)等,
public struct DataResponse<Value> {
/// The URL request sent to the server.
public let request: URLRequest?
/// The server's response to the URL request.
public let response: HTTPURLResponse?
/// The data returned by the server.
public let data: Data?
/// The result of response serialization.
public let result: Result<Value>
/// The timeline of the complete lifecycle of the request.
public let timeline: Timeline
/// Returns the associated value of the result if it is a success, `nil` otherwise.
public var value: Value? { return result.value }
/// Returns the associated error value if the result if it is a failure, `nil` otherwise.
public var error: Error? { return result.error }
var _metrics: AnyObject?
/// Creates a `DataResponse` instance with the specified parameters derived from response serialization.
///
/// - parameter request: The URL request sent to the server.
/// - parameter response: The server's response to the URL request.
/// - parameter data: The data returned by the server.
/// - parameter result: The result of response serialization.
/// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.
///
/// - returns: The new `DataResponse` instance.
public init(
request: URLRequest?,
response: HTTPURLResponse?,
data: Data?,
result: Result<Value>,
timeline: Timeline = Timeline())
{
self.request = request
self.response = response
self.data = data
self.result = result
self.timeline = timeline
}
}
dataResponse部分,基本都是一樣的內(nèi)容,需要看的是其擴(kuò)展的部分
/// Evaluates the specified closure when the result of this `DataResponse` is a success, passing the unwrapped
/// result value as a parameter.
///
/// Use the `map` method with a closure that does not throw. For example:
///
/// let possibleData: DataResponse<Data> = ...
/// let possibleInt = possibleData.map { $0.count }
///
/// - parameter transform: A closure that takes the success value of the instance's result.
///
/// - returns: A `DataResponse` whose result wraps the value returned by the given closure. If this instance's
/// result is a failure, returns a response wrapping the same failure.
public func map<T>(_ transform: (Value) -> T) -> DataResponse<T> {
var response = DataResponse<T>(
request: request,
response: self.response,
data: data,
result: result.map(transform),
timeline: timeline
)
response._metrics = _metrics
return response
}
/// Evaluates the given closure when the result of this `DataResponse` is a success, passing the unwrapped result
/// value as a parameter.
///
/// Use the `flatMap` method with a closure that may throw an error. For example:
///
/// let possibleData: DataResponse<Data> = ...
/// let possibleObject = possibleData.flatMap {
/// try JSONSerialization.jsonObject(with: $0)
/// }
///
/// - parameter transform: A closure that takes the success value of the instance's result.
///
/// - returns: A success or failure `DataResponse` depending on the result of the given closure. If this instance's
/// result is a failure, returns the same failure.
public func flatMap<T>(_ transform: (Value) throws -> T) -> DataResponse<T> {
var response = DataResponse<T>(
request: request,
response: self.response,
data: data,
result: result.flatMap(transform),
timeline: timeline
)
response._metrics = _metrics
return response
}
}
主要的是兩個(gè)過(guò)濾及序列化的方式
再接下來(lái)的DownloadResponse..也差不多是一樣的....
ResponseSerialization部分
有了服務(wù)器的返回?cái)?shù)據(jù)那么肯定就有解析數(shù)據(jù)的,
至于是什么樣的序列化方式...
public protocol DataResponseSerializerProtocol {
/// The type of serialized object to be created by this `DataResponseSerializerType`.
associatedtype SerializedObject
/// A closure used by response handlers that takes a request, response, data and error and returns a result.
var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<SerializedObject> { get }
}
DataResponseSerializer協(xié)議,返回的是一個(gè)SerializedObject的對(duì)象
首先是DataResponseSerializer
public struct DataResponseSerializer<Value>: DataResponseSerializerProtocol {
/// The type of serialized object to be created by this `DataResponseSerializer`.
public typealias SerializedObject = Value
/// A closure used by response handlers that takes a request, response, data and error and returns a result.
public var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>
/// Initializes the `ResponseSerializer` instance with the given serialize response closure.
///
/// - parameter serializeResponse: The closure used to serialize the response.
///
/// - returns: The new generic response serializer instance.
public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<Value>) {
self.serializeResponse = serializeResponse
}
}
沒(méi)有序列化的,直接返回HTTPResponse
/// The type in which all download response serializers must conform to in order to serialize a response.
public protocol DownloadResponseSerializerProtocol {
/// The type of serialized object to be created by this `DownloadResponseSerializerType`.
associatedtype SerializedObject
/// A closure used by response handlers that takes a request, response, url and error and returns a result.
var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<SerializedObject> { get }
}
// MARK: -
/// A generic `DownloadResponseSerializerType` used to serialize a request, response, and data into a serialized object.
public struct DownloadResponseSerializer<Value>: DownloadResponseSerializerProtocol {
/// The type of serialized object to be created by this `DownloadResponseSerializer`.
public typealias SerializedObject = Value
/// A closure used by response handlers that takes a request, response, url and error and returns a result.
public var serializeResponse: (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>
/// Initializes the `ResponseSerializer` instance with the given serialize response closure.
///
/// - parameter serializeResponse: The closure used to serialize the response.
///
/// - returns: The new generic response serializer instance.
public init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, URL?, Error?) -> Result<Value>) {
self.serializeResponse = serializeResponse
}
}
跟DataResponseSerializer類似
/// Adds a handler to be called once the request has finished.
///
/// - parameter queue: The queue on which the completion handler is dispatched.
/// - parameter completionHandler: The code to be executed once the request has finished.
///
/// - returns: The request.
@discardableResult
public func response(queue: DispatchQueue? = nil, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self {
delegate.queue.addOperation {
(queue ?? DispatchQueue.main).async {
var dataResponse = DefaultDataResponse(
request: self.request,
response: self.response,
data: self.delegate.data,
error: self.delegate.error,
timeline: self.timeline
)
dataResponse.add(self.delegate.metrics)
completionHandler(dataResponse)
}
}
return self
}
/// Adds a handler to be called once the request has finished.
///
/// - parameter queue: The queue on which the completion handler is dispatched.
/// - parameter responseSerializer: The response serializer responsible for serializing the request, response,
/// and data.
/// - parameter completionHandler: The code to be executed once the request has finished.
///
/// - returns: The request.
@discardableResult
public func response<T: DataResponseSerializerProtocol>(
queue: DispatchQueue? = nil,
responseSerializer: T,
completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
-> Self
{
delegate.queue.addOperation {
let result = responseSerializer.serializeResponse(
self.request,
self.response,
self.delegate.data,
self.delegate.error
)
var dataResponse = DataResponse<T.SerializedObject>(
request: self.request,
response: self.response,
data: self.delegate.data,
result: result,
timeline: self.timeline
)
dataResponse.add(self.delegate.metrics)
(queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }
}
return self
}
}
兩個(gè)方式大致是差不多的,都是給請(qǐng)求添加一個(gè)func,實(shí)現(xiàn)請(qǐng)求完成之后的回調(diào),
第一個(gè)使用的是默認(rèn)的DefaultDataResponse類型的結(jié)果,第二個(gè)則是自定義的實(shí)現(xiàn)DataResponseSerializerProtocol的結(jié)果,
結(jié)果序列化成Data的
// MARK: - Data
extension Request {
/// Returns a result data type that contains the response data as-is.
///
/// - parameter response: The response from the server.
/// - parameter data: The data returned from the server.
/// - parameter error: The error already encountered if it exists.
///
/// - returns: The result data type.
public static func serializeResponseData(response: HTTPURLResponse?, data: Data?, error: Error?) -> Result<Data> {
guard error == nil else { return .failure(error!) }
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(Data()) }
guard let validData = data else {
return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
}
return .success(validData)
}
}
extension DataRequest {
/// Creates a response serializer that returns the associated data as-is.
///
/// - returns: A data response serializer.
public static func dataResponseSerializer() -> DataResponseSerializer<Data> {
return DataResponseSerializer { _, response, data, error in
return Request.serializeResponseData(response: response, data: data, error: error)
}
}
/// Adds a handler to be called once the request has finished.
///
/// - parameter completionHandler: The code to be executed once the request has finished.
///
/// - returns: The request.
@discardableResult
public func responseData(
queue: DispatchQueue? = nil,
completionHandler: @escaping (DataResponse<Data>) -> Void)
-> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.dataResponseSerializer(),
completionHandler: completionHandler
)
}
}
結(jié)果序列化成字符串
// MARK: - String
extension Request {
/// Returns a result string type initialized from the response data with the specified string encoding.
///
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
/// response, falling back to the default HTTP default character set, ISO-8859-1.
/// - parameter response: The response from the server.
/// - parameter data: The data returned from the server.
/// - parameter error: The error already encountered if it exists.
///
/// - returns: The result data type.
public static func serializeResponseString(
encoding: String.Encoding?,
response: HTTPURLResponse?,
data: Data?,
error: Error?)
-> Result<String>
{
guard error == nil else { return .failure(error!) }
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success("") }
guard let validData = data else {
return .failure(AFError.responseSerializationFailed(reason: .inputDataNil))
}
var convertedEncoding = encoding
if let encodingName = response?.textEncodingName as CFString!, convertedEncoding == nil {
convertedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(
CFStringConvertIANACharSetNameToEncoding(encodingName))
)
}
let actualEncoding = convertedEncoding ?? String.Encoding.isoLatin1
if let string = String(data: validData, encoding: actualEncoding) {
return .success(string)
} else {
return .failure(AFError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)))
}
}
}
extension DataRequest {
/// Creates a response serializer that returns a result string type initialized from the response data with
/// the specified string encoding.
///
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
/// response, falling back to the default HTTP default character set, ISO-8859-1.
///
/// - returns: A string response serializer.
public static func stringResponseSerializer(encoding: String.Encoding? = nil) -> DataResponseSerializer<String> {
return DataResponseSerializer { _, response, data, error in
return Request.serializeResponseString(encoding: encoding, response: response, data: data, error: error)
}
}
/// Adds a handler to be called once the request has finished.
///
/// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the
/// server response, falling back to the default HTTP default character set,
/// ISO-8859-1.
/// - parameter completionHandler: A closure to be executed once the request has finished.
///
/// - returns: The request.
@discardableResult
public func responseString(
queue: DispatchQueue? = nil,
encoding: String.Encoding? = nil,
completionHandler: @escaping (DataResponse<String>) -> Void)
-> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.stringResponseSerializer(encoding: encoding),
completionHandler: completionHandler
)
}
}
結(jié)果序列化成JSON
// MARK: - JSON
extension Request {
/// Returns a JSON object contained in a result type constructed from the response data using `JSONSerialization`
/// with the specified reading options.
///
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
/// - parameter response: The response from the server.
/// - parameter data: The data returned from the server.
/// - parameter error: The error already encountered if it exists.
///
/// - returns: The result data type.
public static func serializeResponseJSON(
options: JSONSerialization.ReadingOptions,
response: HTTPURLResponse?,
data: Data?,
error: Error?)
-> Result<Any>
{
guard error == nil else { return .failure(error!) }
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
guard let validData = data, validData.count > 0 else {
return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
}
do {
let json = try JSONSerialization.jsonObject(with: validData, options: options)
return .success(json)
} catch {
return .failure(AFError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)))
}
}
}
extension DataRequest {
/// Creates a response serializer that returns a JSON object result type constructed from the response data using
/// `JSONSerialization` with the specified reading options.
///
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
///
/// - returns: A JSON object response serializer.
public static func jsonResponseSerializer(
options: JSONSerialization.ReadingOptions = .allowFragments)
-> DataResponseSerializer<Any>
{
return DataResponseSerializer { _, response, data, error in
return Request.serializeResponseJSON(options: options, response: response, data: data, error: error)
}
}
/// Adds a handler to be called once the request has finished.
///
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
/// - parameter completionHandler: A closure to be executed once the request has finished.
///
/// - returns: The request.
@discardableResult
public func responseJSON(
queue: DispatchQueue? = nil,
options: JSONSerialization.ReadingOptions = .allowFragments,
completionHandler: @escaping (DataResponse<Any>) -> Void)
-> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.jsonResponseSerializer(options: options),
completionHandler: completionHandler
)
}
}
結(jié)果序列化成Any
// MARK: - Property List
extension Request {
/// Returns a plist object contained in a result type constructed from the response data using
/// `PropertyListSerialization` with the specified reading options.
///
/// - parameter options: The property list reading options. Defaults to `[]`.
/// - parameter response: The response from the server.
/// - parameter data: The data returned from the server.
/// - parameter error: The error already encountered if it exists.
///
/// - returns: The result data type.
public static func serializeResponsePropertyList(
options: PropertyListSerialization.ReadOptions,
response: HTTPURLResponse?,
data: Data?,
error: Error?)
-> Result<Any>
{
guard error == nil else { return .failure(error!) }
if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }
guard let validData = data, validData.count > 0 else {
return .failure(AFError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
}
do {
let plist = try PropertyListSerialization.propertyList(from: validData, options: options, format: nil)
return .success(plist)
} catch {
return .failure(AFError.responseSerializationFailed(reason: .propertyListSerializationFailed(error: error)))
}
}
}
extension DataRequest {
/// Creates a response serializer that returns an object constructed from the response data using
/// `PropertyListSerialization` with the specified reading options.
///
/// - parameter options: The property list reading options. Defaults to `[]`.
///
/// - returns: A property list object response serializer.
public static func propertyListResponseSerializer(
options: PropertyListSerialization.ReadOptions = [])
-> DataResponseSerializer<Any>
{
return DataResponseSerializer { _, response, data, error in
return Request.serializeResponsePropertyList(options: options, response: response, data: data, error: error)
}
}
/// Adds a handler to be called once the request has finished.
///
/// - parameter options: The property list reading options. Defaults to `[]`.
/// - parameter completionHandler: A closure to be executed once the request has finished.
///
/// - returns: The request.
@discardableResult
public func responsePropertyList(
queue: DispatchQueue? = nil,
options: PropertyListSerialization.ReadOptions = [],
completionHandler: @escaping (DataResponse<Any>) -> Void)
-> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.propertyListResponseSerializer(options: options),
completionHandler: completionHandler
)
}
}
Result部分
結(jié)果已經(jīng)序列化或者自定成想要的格式了,那么對(duì)應(yīng)的一個(gè)請(qǐng)求就完成了本次相應(yīng)的完整的請(qǐng)求,那么對(duì)應(yīng)的回調(diào)結(jié)果就應(yīng)該返回序列化之后的結(jié)果
響應(yīng)有成功,有失敗,相對(duì)應(yīng)的就有了兩種不同的result
public enum Result<Value> {
case success(Value)
case failure(Error)
/// Returns `true` if the result is a success, `false` otherwise.
public var isSuccess: Bool {
switch self {
case .success:
return true
case .failure:
return false
}
}
/// Returns `true` if the result is a failure, `false` otherwise.
public var isFailure: Bool {
return !isSuccess
}
/// Returns the associated value if the result is a success, `nil` otherwise.
public var value: Value? {
switch self {
case .success(let value):
return value
case .failure:
return nil
}
}
/// Returns the associated error value if the result is a failure, `nil` otherwise.
public var error: Error? {
switch self {
case .success:
return nil
case .failure(let error):
return error
}
}
}
跟上面說(shuō)的一樣,首先是枚舉,成功或者失敗,另外這里用了泛型
泛型的寫法是類似這樣的:<T>,在<和>之間聲明一種類型缚陷,這個(gè)T知識(shí)象征性的适篙,在賦值的時(shí)候,可以是任何類型
我們從上面講到的responseJSON中可以看到
@discardableResult
public func responseJSON(
queue: DispatchQueue? = nil,
options: JSONSerialization.ReadingOptions = .allowFragments,
completionHandler: @escaping (DataResponse<Any>) -> Void)
-> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.jsonResponseSerializer(options: options),
completionHandler: completionHandler
)
}
上邊的這個(gè)函數(shù)的主要目的是把請(qǐng)求成功后的結(jié)果序列化為JSON,completionHandler函數(shù)的參數(shù)類型為DataResponse<Any>箫爷,其中的Any就會(huì)傳遞給Result嚷节,也就是Result<Any>。
那么問(wèn)題來(lái)了虎锚,不是把數(shù)據(jù)解析成JSON了嗎硫痰?為什么要返回Any類型呢?json本質(zhì)上很類似于JavaScript中的對(duì)象和數(shù)組窜护。JSONSerialization.jsonObject返回的類型是Any效斑,這是因?yàn)榻馕龊蟮臄?shù)據(jù)有可能是數(shù)組,也有可能是字典柄慰。當(dāng)然如果不是這兩種格式的數(shù)據(jù)鳍悠,使用JSONSerialization.jsonObject解析會(huì)拋出異常税娜。
另外,result添加了一些屬性方便應(yīng)用使用...總起來(lái)說(shuō)坐搔,Result是一個(gè)比較簡(jiǎn)單的封裝。
差不多一次網(wǎng)絡(luò)請(qǐng)求的整個(gè)過(guò)程都已經(jīng)解析完畢了,那么我們肯定不能這個(gè)松散的,我們需要一個(gè)會(huì)話管理來(lái)控制每一個(gè)請(qǐng)求,控制并發(fā),控制進(jìn)度等.
SessionManager部分
大的來(lái)說(shuō),這是一個(gè)整合,把整個(gè)請(qǐng)求過(guò)程封裝成API的方式方便我們調(diào)用,同時(shí)作為一個(gè)會(huì)話管理,它也負(fù)責(zé)著并發(fā)等復(fù)雜處理
open static let defaultHTTPHeaders: HTTPHeaders = {
// Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3
let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"
// Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5
let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
let quality = 1.0 - (Double(index) * 0.1)
return "\(languageCode);q=\(quality)"
}.joined(separator: ", ")
// User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3
// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0`
let userAgent: String = {
if let info = Bundle.main.infoDictionary {
let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown"
let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown"
let osNameVersion: String = {
let version = ProcessInfo.processInfo.operatingSystemVersion
let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
let osName: String = {
#if os(iOS)
return "iOS"
#elseif os(watchOS)
return "watchOS"
#elseif os(tvOS)
return "tvOS"
#elseif os(macOS)
return "OS X"
#elseif os(Linux)
return "Linux"
#else
return "Unknown"
#endif
}()
return "\(osName) \(versionString)"
}()
let alamofireVersion: String = {
guard
let afInfo = Bundle(for: SessionManager.self).infoDictionary,
let build = afInfo["CFBundleShortVersionString"]
else { return "Unknown" }
return "Alamofire/\(build)"
}()
return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"
}
return "Alamofire"
}()
return [
"Accept-Encoding": acceptEncoding,
"Accept-Language": acceptLanguage,
"User-Agent": userAgent
]
}()
上面的內(nèi)容是一個(gè)默認(rèn)的defaultHTTPHeaders
open static let `default`: SessionManager = {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return SessionManager(configuration: configuration)
}()
返回一個(gè)默認(rèn)的會(huì)話管理單例
@discardableResult
open func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
var originalRequest: URLRequest?
do {
originalRequest = try URLRequest(url: url, method: method, headers: headers)
let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
return request(encodedURLRequest)
} catch {
return request(originalRequest, failedWith: error)
}
}
請(qǐng)求函數(shù),傳入url,參數(shù),請(qǐng)求方式等.調(diào)用請(qǐng)求,并返回請(qǐng)求DataRequest,
/// Creates a `DataRequest` to retrieve the contents of a URL based on the specified `urlRequest`.
///
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
///
/// - parameter urlRequest: The URL request.
///
/// - returns: The created `DataRequest`.
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
var originalRequest: URLRequest?
do {
originalRequest = try urlRequest.asURLRequest()
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
let request = DataRequest(session: session, requestTask: .data(originalTask, task))
delegate[task] = request
if startRequestsImmediately { request.resume() }
return request
} catch {
return request(originalRequest, failedWith: error)
}
}
根據(jù)request 添加響應(yīng)的delegate,隊(duì)列,并resume發(fā)起請(qǐng)求,同時(shí)返回DataRequest
private func request(_ urlRequest: URLRequest?, failedWith error: Error) -> DataRequest {
var requestTask: Request.RequestTask = .data(nil, nil)
if let urlRequest = urlRequest {
let originalTask = DataRequest.Requestable(urlRequest: urlRequest)
requestTask = .data(originalTask, nil)
}
let underlyingError = error.underlyingAdaptError ?? error
let request = DataRequest(session: session, requestTask: requestTask, error: underlyingError)
if let retrier = retrier, error is AdaptError {
allowRetrier(retrier, toRetry: request, with: underlyingError)
} else {
if startRequestsImmediately { request.resume() }
}
return request
}
同樣的請(qǐng)求,是在alamofire嘗試編碼失敗之后直接發(fā)起請(qǐng)求
// MARK: - Download Request
// MARK: URL Request
/// Creates a `DownloadRequest` to retrieve the contents the specified `url`, `method`, `parameters`, `encoding`,
/// `headers` and save them to the `destination`.
///
/// If `destination` is not specified, the contents will remain in the temporary location determined by the
/// underlying URL session.
///
/// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
///
/// - parameter url: The URL.
/// - parameter method: The HTTP method. `.get` by default.
/// - parameter parameters: The parameters. `nil` by default.
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
/// - parameter headers: The HTTP headers. `nil` by default.
/// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
///
/// - returns: The created `DownloadRequest`.
@discardableResult
open func download(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
to destination: DownloadRequest.DownloadFileDestination? = nil)
-> DownloadRequest
{
do {
let urlRequest = try URLRequest(url: url, method: method, headers: headers)
let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
return download(encodedURLRequest, to: destination)
} catch {
return download(nil, to: destination, failedWith: error)
}
}
download,類似于data,通過(guò)地址,參數(shù),方式等創(chuàng)建一個(gè)DownloadRequest
@discardableResult
open func download(
_ urlRequest: URLRequestConvertible,
to destination: DownloadRequest.DownloadFileDestination? = nil)
-> DownloadRequest
{
do {
let urlRequest = try urlRequest.asURLRequest()
return download(.request(urlRequest), to: destination)
} catch {
return download(nil, to: destination, failedWith: error)
}
}
發(fā)起下載請(qǐng)求
@discardableResult
open func download(
resumingWith resumeData: Data,
to destination: DownloadRequest.DownloadFileDestination? = nil)
-> DownloadRequest
{
return download(.resumeData(resumeData), to: destination)
}
恢復(fù)下載,從某data開(kāi)始
// MARK: Private - Download Implementation
private func download(
_ downloadable: DownloadRequest.Downloadable,
to destination: DownloadRequest.DownloadFileDestination?)
-> DownloadRequest
{
do {
let task = try downloadable.task(session: session, adapter: adapter, queue: queue)
let download = DownloadRequest(session: session, requestTask: .download(downloadable, task))
download.downloadDelegate.destination = destination
delegate[task] = download
if startRequestsImmediately { download.resume() }
return download
} catch {
return download(downloadable, to: destination, failedWith: error)
}
}
下載的實(shí)現(xiàn)調(diào)用
后面的update..也差不多都是這樣的方式,
總的來(lái)說(shuō)通過(guò)SessionManager提供的api我們可以很簡(jiǎn)單的發(fā)起一次請(qǐng)求,
再通過(guò)ResponseSerialization中對(duì)request的extension,我們可以調(diào)用如responseString\responseData等方法獲取到結(jié)果返回的回調(diào).
這樣一次完整的網(wǎng)絡(luò)請(qǐng)求基本就完成了,基礎(chǔ)的話,我們可以不用管每次請(qǐng)求,響應(yīng),管理等各種狀態(tài)的協(xié)議,我們基本不需要再去實(shí)現(xiàn),當(dāng)然有特殊需求的時(shí)候,我們只要在合適的地方實(shí)現(xiàn)協(xié)議即可
在我們使用時(shí)
我們僅需要如此即可
Alamofire.request("https://www.baidu.com").responseString { (response) in
print(response.result.value ?? "")
}
當(dāng)然我們也可以針對(duì)自己應(yīng)用中的環(huán)境在加一層封裝
例如我有一個(gè)項(xiàng)目
static let shared = XLXNetManager()
...
fileprivate class func get(url:URLConvertible, parameters : Parameters, success : @escaping (_ value : String)->(), failure : @escaping (_ error : Error)->()) {
Alamofire
.request(url, method: .get, parameters: appendAutoParametersDictionary(parameters), encoding: URLEncoding.default, headers: nil)
.responseString { (response) in
switch response.result {
case .success(let value):
success(value)
case .failure(let error):
XLXLog("error:\(error)")
failure(error)
}
}
}
fileprivate class func post(url:URLConvertible, parameters : Parameters, success : @escaping (_ value : String)->(), failure : @escaping (_ error : Error)->()) {
Alamofire
.request(url, method: .post, parameters: appendAutoParametersDictionary(parameters), encoding: URLEncoding.default, headers: nil)
.responseString { (response) in
switch response.result {
case .success(let value):
success(value)
case .failure(let error):
XLXLog("error:\(error)")
failure(error)
}
}
}
class func get_userHelp(success : @escaping (_ value : String)->(), failure : @escaping (_ error : Error)->()) {
get(url: userHelpUrl, parameters: [:], success: success, failure: failure)
}
這樣我在使用的時(shí)候
XLXNetManager.get_userHelp(success: {(ressult) in
//利用如HandyJSON等三方或者系統(tǒng),進(jìn)行模型轉(zhuǎn)換....
XLXLog(ressult)
}) { (error) in
XLXLog(error)
}
完全的類似AFNetworking時(shí)候的使用方式....
當(dāng)然,這個(gè)是自由的.任你發(fā)揮
未完待續(xù),接下來(lái)還會(huì)簡(jiǎn)單介紹其他非關(guān)鍵的類..