Alamofire3.+

重點來源:翻譯自官方github

添加依賴

  1. CocoaPods

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

Alamofire 4.0使用

ps 寫該文時官方最新文檔還沒有出珍坊,so...

Alamofire 4.0是Alamofire的最新主要版本,它是一個用于iOS肺樟、tvOS亡呵、macOS和watchOS的HTTP網(wǎng)絡庫馋辈,使用Swift編寫覆获。作為一個主要的版本,遵循語義版本約定贸弥,4.0引入了api破壞的更改窟坐。

提供此指南是為了使用Alamofire 3簡化現(xiàn)有應用程序的轉換。x到最新的api,以及說明設計和結構的新功能和更新狸涌。

環(huán)境支持

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

4.0優(yōu)點

  • 完整的Swift 3兼容性:包括完全采用新的API設計指南。
  • 新錯誤系統(tǒng):使用新的AFError類型來遵守SE-0112中提出的新模式
  • 新的RequestAdapter協(xié)議:允許在實例化請求之前檢查和調整每個URLRequest最岗,允許輕松修改授權頭等屬性帕胆。
  • 新的RequestRetrier協(xié)議:允許在必要時檢查和重試任何失敗的請求,允許圍繞一組請求構建自定義身份驗證解決方案(OAuth1般渡、OAuth2懒豹、xAuth、Basic Auth等)驯用。
  • 新的參數(shù)編碼協(xié)議:替換參數(shù)編碼枚舉脸秽,允許更容易的擴展和定制,并在失敗時拋出錯誤蝴乔,而不是返回元組记餐。
  • 新的請求類型:包括DataRequest、DownloadRequest薇正、UploadRequest和StreamRequest片酝,它們實現(xiàn)每個請求類型的特定進程、驗證和序列化api以及行為挖腰。
  • 新進度api:包括同時支持Progress和Int64類型的downloadProgress和uploadProgress api雕沿,并調用一個默認為.main的指定調度隊列。
  • 增強的響應驗證:現(xiàn)在包括數(shù)據(jù)或temporaryURL和destinationURL猴仑,如果驗證失敗总处,允許內(nèi)聯(lián)閉包解析服務器數(shù)據(jù)以獲取錯誤消息因悲。
  • 新的下載目的地:通過禁用文件系統(tǒng)上的move操作、刪除前一個文件和創(chuàng)建中間目錄,允許您完全控制文件系統(tǒng)上的move操作稳强。
  • 新的響應類型:統(tǒng)一響應API簽名,并公開臨時url和downloadURL屬性墓塌,以便在較新的平臺上進行下載和所有新的任務指標羹铅。

新版本變化

Alamofire 4完全采用了所有新的Swift 3更改和約定,包括新的API設計指南坐漏。因此薄疚,Alamofire中的幾乎所有API都以某種方式進行了修改。我們不可能記錄每一個更改赊琳,因此我們將嘗試確定最常見的api以及它們是如何更改的街夭,以幫助您克服有時沒有多大幫助的編譯器錯誤。

命名空間變化

一些公共類已被移到全局命名空間中躏筏,以使它們更容易使用板丽,并使它們成為第一類類型。

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

創(chuàng)建請求

由于發(fā)出請求肯定是Alamofire中最常見的操作,下面是Alamofire 3的一些示例埃碱。x請求與它們在Alamofire 4中的新等價物進行比較猖辫。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

URLStringConvertible協(xié)議變化

URLConvertible的變化

命名修改

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

修改原因:

Alamofire中一個非常常見的問題是,用戶忘記按百分比轉義URL字符串砚殿,Alamofire將崩潰啃憎。到目前為止,我們(Alamofire團隊)一直認為這就是Alamofire的設計方式似炎,您的url需要符合RFC 2396辛萍。這對于社區(qū)來說當然不理想,因為我們都希望Alamofire告訴我們羡藐,我們的URL是無效的贩毕,而不是崩潰。

新的URLConvertible協(xié)議仆嗦。Alamofire之前無法安全處理無效URL字符串的原因實際上是由于URLStringConvertible上缺乏安全性辉阶。Alamofire不可能知道如何智能地使無效的URL字符串有效。因此瘩扼,如果無法從URLConvertible創(chuàng)建URL睛藻,則為AFError。拋出invalidURL錯誤邢隧。

這個更改(以及其他許多更改)允許Alamofire安全地處理無效url并在響應處理程序中報告錯誤店印。

URLRequest一致性變化

URLRequest不再符合URLStringConvertible協(xié)議,現(xiàn)在是URLConvertible協(xié)議倒慧。在以前的Alamofire版本中按摘,這總是有點牽強,實際上沒有必要纫谅。它還很有可能在許多Alamofire api中引入歧義炫贤。由于這些原因,URLRequest不再符合URLStringConvertible(現(xiàn)在是URLConvertible)付秕。

下面的操作不再支持:

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

取而代之的是:

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

關于PR-1505點這里

新功能

1. Request Adapter

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

它允許在創(chuàng)建SessionManager之前對每個請求進行檢查和調整兰珍。使用適配器的一種非常特定的方式是將授權頭附加到特定類型的身份驗證后的請求。

class AccessTokenAdapter: RequestAdapter {
    private let accessToken: String

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

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

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

        return urlRequest
    }
}

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

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

如果在調整過程中發(fā)生錯誤询吴,應該拋出錯誤掠河,并將其提交到請求的響應處理程序中。
可以查看PR-1450更多內(nèi)容

2. Request Retrier(請求檢索)

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

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

它允許在執(zhí)行時遇到錯誤的請求重試猛计,如果指定了可選的延遲唠摹。

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

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

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

檢索器允許您在請求完成后檢查它,并運行所有驗證閉包以確定是否應該重試奉瘤。當同時使用RequestAdapter和RequestRetrier協(xié)議時勾拉,可以為OAuth1、OAuth2、Basic Auth甚至指數(shù)級后退重試策略創(chuàng)建憑據(jù)刷新系統(tǒng)藕赞〕芍猓可能性是無限的。有關此主題的更多信息和示例斧蜕,請參閱README双霍。
更多信息請查看 PR-1391PR-1450

3. Task Metrics

在iOS、tvOS 10和macOS 10.12中惩激,蘋果推出了新的URLSessionTaskMetrics api店煞。任務指標封裝了關于請求和響應執(zhí)行的一些奇妙的統(tǒng)計信息蟹演。這個API非常類似于Alamofire的時間軸风钻,但是提供了更多Alamofire無法計算的統(tǒng)計信息。我們對這些api感到非常興奮酒请,并在每種響應類型上公開了它們骡技,這意味著它們不容易使用。

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

需要注意的是羞反,這些api只在iOS布朦、tvOS 10+和macOS 10.12+上可用。因此昼窗,根據(jù)您的部署目標是趴,您可能需要使用以下內(nèi)部可用性檢查:

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

更多請查看 PR-1492

更新的功能

Alamofire 4包含許多新特性和對現(xiàn)有特性的增強。本節(jié)簡要概述這些特性并演示它們的用途澄惊。有關每一個的更多信息唆途,請參考鏈接的拉請求

Errors

Alamofire 4包含一個全新的錯誤系統(tǒng),它采用了 SE-0112 中提出的新模式掸驱。新錯誤系統(tǒng)的核心是AFError肛搬,這是一種由五種主要情況支持的新錯誤類型枚舉。

  • .invalidurl (url: URLConvertible)——當URLConvertible類型無法創(chuàng)建有效url時返回毕贼。
  • .parameterencodingfailed(reason:ParameterEncodingFailureReason) -當參數(shù)編碼對象在編碼過程中拋出錯誤時返回温赔。
  • .multipartencodingfailed(reason:MultipartEncodingFailureReason) -當多部分編碼過程中的某個步驟失敗時返回。
  • .responsevalidationfailed(reason:ResponseValidationFailureReason)——當validate()調用失敗時返回鬼癣。
  • .responseserializationfailed(reason:ResponseSerializationFailureReason)——當響應序列化器在序列化過程中遇到錯誤時返回陶贼。

每個案例都包含一個特定的失敗原因,這是另一個嵌套枚舉待秃,其中有多個案例包含關于所發(fā)生錯誤的確切類型的額外信息骇窍。這最終意味著在Alamofire中更容易識別錯誤的來源和處理方法。

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

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

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

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

這種新設計允許您盡可能深入地研究錯誤锥余,以便找出繼續(xù)進行的最佳方法腹纳。它還使開發(fā)人員不必到處處理NSError類型。通過切換到Alamofire中的自定義錯誤類型,我們可以簡化結果和響應泛型類型嘲恍,只需要一個泛型參數(shù)足画。這簡化了響應序列化邏輯。
更多請查看 PR-1419

Parameter Encoding Protocol

到目前為止佃牛,參數(shù)編碼枚舉已經(jīng)為我們提供了兩年多的良好服務淹辞。然而,它有一些限制俘侠,我們想在Alamofire 4中解決象缀。

  • .url根據(jù)HTTP方法選擇目的地,所以它總是有點令人困惑在處理.url案例的行為時
  • .urlencodedinurl案例一直是一個令人頭疼的問題爷速。
  • .json和.propertylist編碼不能接受格式化或寫入選項央星。
    自定義編碼對于用戶來說有點難掌握。

由于這些原因惫东,我們決定在Alamofire 4中完全消除枚舉!現(xiàn)在莉给,ParameterEncoding是由三個具體的URLEncoding、JSONEncoding和PropertyList編碼結構支持的協(xié)議廉沮,它們具有一個新的參數(shù)類型別名颓遏,用于創(chuàng)建參數(shù)字典。

public typealias Parameters = [String: Any]

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

URL Encoding

新的URLEncoding結構包含一個目標枚舉滞时,支持三種類型的目標:

  • .methodDependent——將已編碼的查詢字符串結果應用于現(xiàn)有的查詢字符串叁幢,用于GET、HEAD和DELETE請求坪稽,并將set作為使用任何其他HTTP方法的請求的HTTP主體
  • .querystring—設置或追加已編碼的查詢字符串結果到現(xiàn)有查詢字符串曼玩。
  • .httpbody—將已編碼的查詢字符串結果設置為URL請求的HTTP體。

這些目的地使得控制將參數(shù)編碼到URLRequest上的位置變得容易得多刽漂。在參數(shù)編碼方面演训,創(chuàng)建請求仍然使用與以前相同的簽名,并且具有相同的默認行為贝咙。

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

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

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

JSON Encoding

新的JSONEncoding結構公開了定制JSON編寫選項的能力

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

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

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

Property List Encoding

新的PropertyListEncoding結構允許定制plist格式和write選項样悟。

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

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

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

Custom Encoding

創(chuàng)建自定義自定義參數(shù)編碼現(xiàn)在與實現(xiàn)協(xié)議一樣簡單。有關如何做到這一點的更多示例庭猩,請參閱README窟她。更多請查看 PR-1465

Request 子類

在 Alamofire 4中,request, download, upload 和 stream API不再返回 Request 對象蔼水。相反震糖,它們返回特定類型的 Request 的子類。原因:

  • Progress:progress方法的行為對于上傳請求來說是混亂的趴腋。
  • Response Serializers:是為數(shù)據(jù)和上傳請求而設計的吊说,而不是為下載或流請求而設計的论咏。

在高版本上,Alamofire 4現(xiàn)在有四個請求子類颁井,每個子類都支持自己的自定義鏈接api厅贪。這允許每個子類創(chuàng)建針對特定類型請求的擴展。

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

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

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

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

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

通過這種分割雅宾,Alamofire 4能夠為每種類型的請求創(chuàng)建定制的鏈接api养涮。這提供了各種各樣的可能性,但是讓我們花一點時間來關注一下這個變化在進度報告和下載目的地方面意味著什么眉抬。

Download and Upload Progress(上傳和下載進度)

數(shù)據(jù)贯吓、下載和上傳請求的進度報告系統(tǒng)已經(jīng)完全重新設計。每個請求類型都包含通過返回底層的進度實例在每次進度更新期間執(zhí)行閉包的進度api蜀变。閉包將在默認為main的指定隊列上調用悄谐。

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

下載指定文件

Alamofire 3.x,成功的下載請求總是將臨時文件移動到目的地閉包提供的最終目的地URL昏苏。雖然這是一個很好的方便尊沸,但它有幾個限制:

  • 強制性 -強制提供一個目標閉包來移動文件威沫,即使不想移動
  • 限制性 -在移動文件之前贤惯,無法調整文件系統(tǒng)。

這些限制導致了Alamofire 4中的一些增強棒掠。第一個是目標閉包的可選性》豕梗現(xiàn)在,默認情況下烟很,目標閉包為nil颈墅,這意味著文件不會移動到文件系統(tǒng)的任何位置,并且會返回臨時URL雾袱。

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

下載選項

另一個主要的變化是在目標閉包中添加了下載選項恤筛,允許對移動操作進行更多的文件系統(tǒng)控制。為此芹橡,創(chuàng)建了DownloadOptions類型并將其添加到DownloadFileDestination閉包中毒坛。

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

目前支持的可選項:

  • .createintermediatedirectory—如果指定,則為目標URL創(chuàng)建中間目錄林说。
  • .removepreviousfile -如果指定煎殷,從目標URL中刪除前一個文件。
let destination: DownloadRequest.DownloadFileDestination = { _, _ in 
    return (fileURL, [.removePreviousFile, .createIntermediateDirectories]) 
}

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

如果在文件系統(tǒng)操作期間發(fā)生錯誤腿箩,DownloadResponse上的錯誤類型為URLError豪直。

Response Validation(響應確認)

在Alamofire 4中有幾個改進響應驗證系統(tǒng)的機會領域。這些領域包括:

  • 將底層數(shù)據(jù)公開給驗證閉包珠移。
  • 不同請求子類類型之間的自定義驗證允許為下載請求公開temporaryURL和destinationURL弓乙。

通過創(chuàng)建請求子類末融,驗證閉包類型別名和請求api能夠針對每個請求類型進行定制。

Data Request

在DataRequest(通過UploadRequest繼承)上公開的驗證閉包現(xiàn)在如下所示:

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

通過公開 Data? 屬性暇韧,您不再需要根據(jù)請求編寫擴展來訪問它』耍現(xiàn)在你可以這樣做:

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

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

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

Download Request

DownloadRequest上的驗證閉包非常類似于DataRequest API,但更適合于下載锨咙。

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

temporaryURL和destinationURL參數(shù)現(xiàn)在允許您直接在內(nèi)聯(lián)閉包中訪問服務器返回的數(shù)據(jù)语卤。這允許您在確定需要創(chuàng)建自定義錯誤時檢查文件中的數(shù)據(jù)。

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

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

通過將底層服務器數(shù)據(jù)直接暴露給內(nèi)聯(lián)閉包酪刀,可以在驗證閉包中解析這些響應中嵌入的錯誤消息粹舵,以創(chuàng)建包括服務器錯誤消息在內(nèi)的自定義錯誤。如果負載與響應序列化器閉包中使用的模式相同骂倘,則可以調用響應序列化器來解析錯誤消息眼滤,而不是復制邏輯。有關如何做到這一點的示例历涝,請參閱README诅需。

Response Serializers(響應序列化器)

Alamofire 3.x中的響應序列化系統(tǒng)有幾個非常嚴重的限制:

  • 響應序列化api可以應用于下載和流請求,但會導致未定義的行為荧库。
  • 響應API返回4個參數(shù)堰塌,而不是封裝響應類型。

在Alamofire 3.x中這個系統(tǒng)有一些非常強大的限制分衫。因此场刑,在Alamofire 4中,請求類型首先被分解為子類蚪战,這為為特定類型的請求創(chuàng)建定制的響應序列化器和api提供了機會牵现。在深入討論響應序列化器之前,我們應該先瀏覽一下新的響應類型邀桑。

Default Data Response

DefaultDataResponse表示未序列化的服務器響應瞎疼。沒有發(fā)生Alamofire處理,它只是從SessionDelegate api收集所有響應信息壁畸,并以一個簡單的結構返回贼急。

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

這是您將從 DataRequest.response API返回的響應類型

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

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

泛型DataResponse類型與Alamofire 3中的泛型響應相同。但是重構后包含了新的metrics屬性瓤摧。

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

您仍然可以訪問DataRequest和UploadRequest類型上與以前相同的響應序列化api竿裂。

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

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

由于下載與數(shù)據(jù)和上傳請求的工作方式不同,Alamofire 4包含定制的下載響應類型照弥,以適應其行為腻异。DefaultDownloadResponse類型表示DownloadRequest的非序列化服務器響應,DownloadRequest將所有SessionDelegate信息收集到一個簡單的結構中这揣。

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

在使用新的DownloadRequest.response API 時悔常,將返回DefaultDownloadResponse類型影斑。

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

新的通用DownloadResponse類型類似于通用DataResponse類型,但包含為下載請求定制的信息机打。DownloadResponse類型在DownloadRequest類型上公開的四個新api之一時返回矫户。這些新api與DataRequest api匹配,并通過從底層臨時URL或目標URL加載數(shù)據(jù)提供相同的功能残邀。

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

這些新的響應序列化api使將請求下載到文件并在單個調用中序列化響應變得容易得多皆辽。

自定義響應序列化器

如果您已經(jīng)創(chuàng)建了自己的自定義響應序列化器,您可能希望跨數(shù)據(jù)和下載請求擴展支持芥挣,就像我們對Alamofire響應序列化器所做的那樣驱闷。如果您決定這樣做,那么仔細看看Alamofire是如何通過將響應序列化器實現(xiàn)移動到請求來共享這兩種請求類型之間的響應序列化器實現(xiàn)的空免。這使我們能夠耗盡邏輯以避免類型之間的重復空另。
查看更多: PR-1457

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蹋砚,隨后出現(xiàn)的幾起案子扼菠,更是在濱河造成了極大的恐慌,老刑警劉巖坝咐,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件循榆,死亡現(xiàn)場離奇詭異,居然都是意外死亡畅厢,警方通過查閱死者的電腦和手機冯痢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門氮昧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來框杜,“玉大人,你說我怎么就攤上這事袖肥∵淙瑁” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵椎组,是天一觀的道長油狂。 經(jīng)常有香客問我,道長寸癌,這世上最難降的妖魔是什么专筷? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蒸苇,結果婚禮上磷蛹,老公的妹妹穿的比我還像新娘。我一直安慰自己溪烤,他們只是感情好味咳,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布庇勃。 她就那樣靜靜地躺著,像睡著了一般槽驶。 火紅的嫁衣襯著肌膚如雪责嚷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天掂铐,我揣著相機與錄音罕拂,去河邊找鬼。 笑死全陨,一個胖子當著我的面吹牛聂受,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播烤镐,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼蛋济,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炮叶?” 一聲冷哼從身側響起碗旅,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎镜悉,沒想到半個月后祟辟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡侣肄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年旧困,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稼锅。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡吼具,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出矩距,到底是詐尸還是另有隱情拗盒,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布锥债,位于F島的核電站陡蝇,受9級特大地震影響,放射性物質發(fā)生泄漏哮肚。R本人自食惡果不足惜登夫,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望允趟。 院中可真熱鬧恼策,春花似錦、人聲如沸拼窥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至总棵,卻和暖如春鳍寂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背情龄。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工迄汛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骤视。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓鞍爱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親专酗。 傳聞我的和親對象是個殘疾皇子睹逃,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容