【4858.com】骨干用法,使用指南

By admin in 4858.com on 2019年4月14日

Alamofire是在URLSession和ULacrosseL加载系统的根基上写的。所以,为了越来越好地上学这些框架,提议先熟悉下列几个底层互联网协议栈:

【iOS开发】Alamofire框架的利用二 —— 高级用法

那边小说介绍的是Alamofire框架的高等级用法,假如此前没有看过主导用法的,能够先去看望【iOS开发】Alamofire框架的施用壹—— 基本用法

Alamofire是在URLSession和ULacrosseL加载系统的底子上写的。所以,为了更加好地上学那一个框架,提议先熟谙下列多少个底层网络协议栈:

  • URL Loading System Programming
    Guide >>
  • URLSession Class
    Reference >>
  • URLCache Class
    Reference >>
  • URLAuthenticationChallenge Class
    Reference >>

Alamofire 是一款 斯威夫特 写的 HTTP 网络请求库

Alamofire框架的应用1 —— 基本用法

对此利用Objective-C的开发者,一定尤其熟悉AFNetworking以此互联网框架。在苹果推出的Swift之后,AFNetworking的撰稿人专门用斯威夫特来编排七个类似AFNetworking的互联网框架,称为Alamofire。Alamofire地址
>>

自身分两篇小说介绍怎么样行使Alamofire框架。小说的始末根本是翻译Alamofire的readme。第3篇小说>>

  • URL Loading System Programming
    Guide >>
  • URLSession Class
    Reference >>
  • URLCache Class
    Reference >>
  • URLAuthenticationChallenge Class
    Reference >>

Session Manager

高级别的便利的格局,例如Alamofire.request,使用的是默许的Alamofire.SessionManager,并且那一个SessionManager是用默许URLSessionConfiguration配置的。

比如说,下边七个语句是等价的:

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

let sessionManager = Alamofire.SessionManager.default
sessionManager.request("https://httpbin.org/get")

咱俩得以本身创立后台会话和局促会话的session
manager,还可以够自定义暗中认可的对话配置来创立新的session
manager,例如修改暗中同意的header
httpAdditionalHeaderstimeoutIntervalForRequest

本篇内容为 Alamofire 官方 Readme
文件的翻译,如有翻译不妥贴的地点,您也能够给笔者付出P景逸SUV,该翻译github地址。本篇包涵如下内容:

功能

  • 链式请求/响应措施
  • U悍马H2L / JSON / plist参数编码
  • 上传文件/数据/流/ 多壹些表单数据
  • 使用请求下载文件或苏醒数据
  • 接纳U福睿斯L凭据实行身份注脚
  • HTTP响应的注解
  • 上传和下载速度闭包
  • cULANDL命令的出口
  • 动态适应和重试请求
  • TLS证书和Public Key Pinning
  • 网络可达性
  • 完善的单元和集成测试覆盖率

Session Manager

高级别的方便的措施,例如Alamofire.request,使用的是私下认可的Alamofire.SessionManager,并且那些SessionManager是用暗中认可URLSessionConfiguration配置的。

比如说,上面八个语句是等价的:

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

let sessionManager = Alamofire.SessionManager.default
sessionManager.request("https://httpbin.org/get")

咱俩得以协调创建后台会话和局促会话的session
manager,还足以自定义私下认可的对话配置来创立新的session
manager,例如修改暗中认可的header
httpAdditionalHeaderstimeoutIntervalForRequest

用暗中同意的对话配置创立一个Session Manager

let configuration = URLSessionConfiguration.default
let sessionManager = Alamofire.SessionManager(configuration: configuration)

Alamofire 是 斯威夫特 语言编写的 HTTP 网络库。

组件库

为了让Alamofire只顾于核心互连网的完毕,Alamofire生态系统还有此外五个库:

  • AlamofireImage:贰个图片库,包蕴图像响应系列化器、UIImageUIImageView的扩张、自定义图像滤镜、内部存储器中自行清除和依照优先级的图像下载系统。
  • AlamofireNetworkActivityIndicator:控制iOS应用的网络活动提醒器。包含可铺排的推迟计时器来支援缩减闪光和支撑URLSession实例。

用暗中认可的对话配置创设二个Session Manager

let configuration = URLSessionConfiguration.default
let sessionManager = Alamofire.SessionManager(configuration: configuration)

用后台会话配置创制2个Session Manager

let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background")
let sessionManager = Alamofire.SessionManager(configuration: configuration)
  • 特性
  • 组件
  • 条件需要
  • 移植指南
  • 安装
  • 用法
    • 简介 – 发起呼吁, 响应回调, 响应辨证, 响应缓存
      • HTTP – [HTTP 方法], 请求参数编码, HTTP Headers, 认证
      • 恢宏数码 – 下载数据到文件, 上传数据到服务器
      • 工具 – 目的总计, [cUCRUISERL 命令输出](#cU昂科威L 命令输出)
    • 高等用法
      • URL 会话 – 会话管理, 会话代理, 请求
      • 请求路由 – 请求路由, Adapting and Retrying Requests
      • 模型对象 – 自定义响应系列化器
      • 网络连接 – 安全性, 互连网可用性
  • Open Radars
  • FAQ
  • 致谢
  • 捐款
  • 开源商谈

须求的选择环境

  • iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 8.1+
  • Swift 3.0+

用后台会话配置创立三个Session Manager

let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background")
let sessionManager = Alamofire.SessionManager(configuration: configuration)

用默短暂会话配置创制一个Session Manager

let configuration = URLSessionConfiguration.ephemeral
let sessionManager = Alamofire.SessionManager(configuration: configuration)

安装方式

用默短暂会话配置创建3个Session Manager

let configuration = URLSessionConfiguration.ephemeral
let sessionManager = Alamofire.SessionManager(configuration: configuration)

修改会话配置

var defaultHeaders = Alamofire.SessionManager.defaultHTTPHeaders
defaultHeaders["DNT"] = "1 (Do Not Track Enabled)"

let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = defaultHeaders

let sessionManager = Alamofire.SessionManager(configuration: configuration)

留神:不引入在Authorization或者Content-Type
header使用。而应当采用Alamofire.requestAPI、URLRequestConvertibleParameterEncoding的headers参数。

  • [x] 链式请求 / 响应措施调用
  • [x] ULacrosseL / JSON / plist 请求参数编码
  • [x] 上传 File / Data / Stream / MultipartFormData
  • [x] 文件下载和断点续传
  • [x] UENCORELCredential 认证形式
  • [x] HTTP 响应表达
  • [x] 上传下载进程
  • [x] cUTiggoL 命令输出
  • [x] Dynamically Adapt and Retry Requests
  • [x] TLS Certificate and Public Key Pinning
  • [x] 网络可用性
  • [x] 测试单元和合并测试

CocoaPods

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!

target '项目名称' do
    pod 'Alamofire', '~> 4.4'
end

iOS版本和Alamofire本子能够自身遵照实况自行转移。CocoaPods是比较常用的第三方库管理工科具,别的办法就不详细说了。

修改会话配置

var defaultHeaders = Alamofire.SessionManager.defaultHTTPHeaders
defaultHeaders["DNT"] = "1 (Do Not Track Enabled)"

let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = defaultHeaders

let sessionManager = Alamofire.SessionManager(configuration: configuration)

在意:不引入在Authorization或者Content-Type
header使用。而应当利用Alamofire.requestAPI、URLRequestConvertibleParameterEncoding的headers参数。

对话代理

暗中同意意况下,一个SessionManager实例创制3个SessionDelegate目的来处理底层URLSession变动的比不上品种的代理回调。每一种代理方法的实现拍卖大规模的景况。然后,高级用户大概鉴于种种原因需求重写暗许功用。

为了让 Alamofire 集中于主旨互联网操作的落到实处,Alamofire Software Foundation
接纳组件的花样为 Alamofire 添加额外的职能。

哪些采用

对话代理

暗许情况下,1个SessionManager实例创设3个SessionDelegate目的来处理底层URLSession变化的例外档次的代理回调。种种代理方法的完成拍卖大规模的气象。然后,高级用户也许由于各个原因须要重写暗许效能。

重写闭包

率先种自定义SessionDelegate的主意是通过重写闭包。大家能够在每种闭包重写SessionDelegate
API对应的落到实处。上边是重写闭包的示范:

/// 重写URLSessionDelegate的`urlSession(_:didReceive:completionHandler:)`方法
open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?

/// 重写URLSessionDelegate的`urlSessionDidFinishEvents(forBackgroundURLSession:)`方法 
open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?

/// 重写URLSessionTaskDelegate的`urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`方法 
open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?

/// 重写URLSessionDataDelegate的`urlSession(_:dataTask:willCacheResponse:completionHandler:)`方法 
open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?

上边包车型大巴言传身教演示了怎么选用taskWillPerformHTTPRedirection来防止回调到其它apple.com域名。

let sessionManager = Alamofire.SessionManager(configuration: URLSessionConfiguration.default)
let delegate: Alamofire.SessionDelegate = sessionManager.delegate

delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
    var finalRequest = request

    if
        let originalRequest = task.originalRequest,
        let urlString = originalRequest.url?.urlString,
        urlString.contains("apple.com")
    {
        finalRequest = originalRequest
    }

    return finalRequest
}
  • AlamofireImage – 四个分包图表响应体系化, UIImageUIImageView
    的壮大,自定义图片过滤器,自动清理的内部存款和储蓄器缓存,基于优先级的图片下载的图片库。
  • AlamofireNetworkActivityIndicator –
    控制互连网活动提示器的显得。允许用户配置延迟时间来拖延活动提示器的来得,同时也支撑独立创制的
    URLSession 实例对象。

发请求

Alamofire.request("")

重写闭包

率先种自定义SessionDelegate的章程是因此重写闭包。大家能够在各种闭包重写SessionDelegate
API对应的落到实处。上边是重写闭包的示范:

/// 重写URLSessionDelegate的`urlSession(_:didReceive:completionHandler:)`方法
open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?

/// 重写URLSessionDelegate的`urlSessionDidFinishEvents(forBackgroundURLSession:)`方法 
open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?

/// 重写URLSessionTaskDelegate的`urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`方法 
open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?

/// 重写URLSessionDataDelegate的`urlSession(_:dataTask:willCacheResponse:completionHandler:)`方法 
open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?

下边包车型客车言传身教演示了怎么采用taskWillPerformHTTPRedirection来幸免回调到别的apple.com域名。

let sessionManager = Alamofire.SessionManager(configuration: URLSessionConfiguration.default)
let delegate: Alamofire.SessionDelegate = sessionManager.delegate

delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
    var finalRequest = request

    if
        let originalRequest = task.originalRequest,
        let urlString = originalRequest.url?.urlString,
        urlString.contains("apple.com")
    {
        finalRequest = originalRequest
    }

    return finalRequest
}

子类化

另叁个重写SessionDelegate的贯彻的格局是把它子类化。通过子类化,大家得以完全自定义他的一颦一笑,也许为这几个API创制八个代理并且依旧选用它的暗许完结。通过创制代理,我们可以跟踪日志事件、发公告、提供前后促成。上面那一个事例演示了怎么子类化SessionDelegate,并且有回调的时候打字与印刷新闻:

class LoggingSessionDelegate: SessionDelegate {
    override func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        willPerformHTTPRedirection response: HTTPURLResponse,
        newRequest request: URLRequest,
        completionHandler: @escaping (URLRequest?) -> Void)
    {
        print("URLSession will perform HTTP redirection to request: \(request)")

        super.urlSession(
            session,
            task: task,
            willPerformHTTPRedirection: response,
            newRequest: request,
            completionHandler: completionHandler
        )
    }
}

如上所述,无论是默许完毕依旧重写闭包,都应当提供必需的意义。子类化应该作为最终的挑叁拣4。

1呼百应处理

直白在伏乞前边用点语法链接响应处理:

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    print(response.request)  // 原始的URL请求
    print(response.response) // HTTP URL响应
    print(response.data)     // 服务器返回的数据
    print(response.result)   // 响应序列化结果,在这个闭包里,存储的是JSON数据

    if let JSON = response.result.value {
        print("JSON: \(JSON)")
    }
}

在地点的例证中,responseJSON
handler直接拼接到请求后边,当呼吁实现后被调用。这一个回调方式的闭包1旦接收响应后,就会处理那么些响应,并不会因为等待服务器的响应而造成堵塞执行。请求的结果仅在响应闭包的限定内可用。其余任何与服务器再次来到的响应只怕数额相关的操作,都无法不在这几个闭包内实施。

Alamofire暗中认可意况下富含三种不相同的响应handler:

// 响应 Handler - 未序列化的响应
func response(
    queue: DispatchQueue?,
    completionHandler: @escaping (DefaultDataResponse) -> Void)
    -> Self

// 响应数据 Handler - 序列化成数据类型
func responseData(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Data>) -> Void)
    -> Self

// 响应字符串 Handler - 序列化成字符串类型
func responseString(
    queue: DispatchQueue?,
    encoding: String.Encoding?,
    completionHandler: @escaping (DataResponse<String>) -> Void)
    -> Self

// 响应 JSON Handler - 序列化成Any类型
func responseJSON(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Any>) -> Void)
    -> Self

// 响应 PropertyList (plist) Handler - 序列化成Any类型
func responsePropertyList(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Any>) -> Void))
    -> Self

有着的响应handler都不会对响应实行验证。也正是说响应状态码在400..<499500..<599界定内,都不会触发错误。

子类化

另二个重写SessionDelegate的兑现的形式是把它子类化。通过子类化,大家可以完全自定义他的一坐一起,可能为那一个API创制1个代理并且如故使用它的暗中同意达成。通过创办代理,大家得以跟踪日志事件、发通报、提供前后促成。上面这些例子演示了怎么子类化SessionDelegate,并且有回调的时候打字与印刷信息:

class LoggingSessionDelegate: SessionDelegate {
    override func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        willPerformHTTPRedirection response: HTTPURLResponse,
        newRequest request: URLRequest,
        completionHandler: @escaping (URLRequest?) -> Void)
    {
        print("URLSession will perform HTTP redirection to request: \(request)")

        super.urlSession(
            session,
            task: task,
            willPerformHTTPRedirection: response,
            newRequest: request,
            completionHandler: completionHandler
        )
    }
}

如上所述,无论是私下认可完成照旧重写闭包,都应该提供要求的职能。子类化应该作为最后的选项。

请求

requestdownloaduploadstream艺术的结果是DataRequestDownloadRequestUploadRequestStreamRequest,并且有着请求都继承自Request。所有的Request并不是一向开立的,而是由session
manager创制的。

各类子类都有特定的法门,例如authenticatevalidateresponseJSONuploadProgress,都回来3个实例,以便方法链接(也便是用点语法一而再调用方法)。

伸手能够被中断、复苏和注销:

  • suspend():暂停底层的职责和调度队列
  • resume():恢复生机底层的职务和调度队列。借使manager的startRequestsImmediately不是true,那么必须调用resume()来开头请求。
  • cancel():撤除底层的职务,并发生2个error,error被传出任何已经注册的响应handlers。
  • iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 8.1+
  • Swift 3.0+
响应 Handler

response handler不处理其余相应数额。它只有是从UGL450L会话代理中转化音讯。

Alamofire.request("https://httpbin.org/get").response { response in
    print("Request: \(response.request)")
    print("Response: \(response.response)")
    print("Error: \(response.error)")

    if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
        print("Data: \(utf8Text)")
    }
}

相似情况下不提议利用那种未有响应体系化器的handler,而相应利用上面有特定系列化器的handler。

请求

requestdownloaduploadstream主意的结果是DataRequestDownloadRequestUploadRequestStreamRequest,并且有着请求都持续自Request。所有的Request并不是一向开立的,而是由session
manager成立的。

种种子类都有特定的主意,例如authenticatevalidateresponseJSONuploadProgress,都回去2个实例,以便方法链接(也便是用点语法两次三番调用方法)。

伸手可以被暂停、恢复生机和注销:

  • suspend():暂停底层的任务和调度队列
  • resume():恢复底层的职分和调度队列。假设manager的startRequestsImmediately不是true,那么必须调用resume()来开头请求。
  • cancel():废除底层的职分,并发生三个error,error被传播任何已经注册的响应handlers。

传递请求

乘机应用的不多增大,当我们树立网络栈的时候要选用通用的方式。在通用形式的安插性中,3个很重点的一对就是怎么传送请求。服从Router设计格局的URLConvertibleURLRequestConvertible合计得以扶持大家。

一呼百应数据 Handler

responseData使用responseDataSerializer(这一个指标把服务器的多少类别化成其余门类)来领取服务器重回的多寡。假若没有回到错误并且有数据重回,那么响应Result将会是.successvalueData类型。

Alamofire.request("https://httpbin.org/get").responseData { response in
    debugPrint("All Response Info: \(response)")

    if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) {
        print("Data: \(utf8Text)")
    }
}

传递请求

乘势应用的不多增大,当我们建立网络栈的时候要使用通用的方式。在通用方式的安顿中,贰个很要紧的片段便是什么传送请求。遵守Router设计格局的URLConvertibleURLRequestConvertible合计得以支持大家。

URLConvertible

遵循了URLConvertible商业事务的门类能够被用来创设UPRADOL,然后用来创造U奥迪Q5L请求。StringURLURLComponent暗许是依据URLConvertible协议的。它们都能够作为url参数传入requestuploaddownload方法:

let urlString = "https://httpbin.org/post"
Alamofire.request(urlString, method: .post)

let url = URL(string: urlString)!
Alamofire.request(url, method: .post)

let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true)!
Alamofire.request(urlComponents, method: .post)

以一种有意义的艺术和web应用程序竞相的选用,都鼓励使用自定义的遵照URLConvertible磋商的体系将一定领域模型映射到服务器能源,因为如此比较有利。

  • Alamofire 四.0 移植指南
  • Alamofire 叁.0 移植指南
  • Alamofire 2.0 移植指南
响应字符串 Handler

responseString
handler使用responseStringSerializer对象依照特定的编码格式把服务器重临的数目转换来String。假若未有回去错误并且服务器的多少成功地转移为String,那么响应Result将会是.successvalueString类型。

Alamofire.request("https://httpbin.org/get").responseString { response in
    print("Success: \(response.result.isSuccess)")
    print("Response String: \(response.result.value)")
}

比方未有点名编码格式,将会选用服务器的HTTPURLResponse点名的格式。借使服务器不恐怕确定编码格式,那么暗许使用.isoLatin1

URLConvertible

遵循了URLConvertible合计的体系可以被用来营造ULX570L,然后用来创制U福睿斯L请求。StringURLURLComponent暗许是遵照URLConvertible磋商的。它们都可以看成url参数字传送入requestuploaddownload方法:

let urlString = "https://httpbin.org/post"
Alamofire.request(urlString, method: .post)

let url = URL(string: urlString)!
Alamofire.request(url, method: .post)

let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true)!
Alamofire.request(urlComponents, method: .post)

以1种有意义的章程和web应用程序竞相的应用,都鼓励接纳自定义的依照URLConvertible协和式飞机的品种将一定领域模型映射到服务器财富,因为如此相比较方便。

品种安全传送
extension User: URLConvertible {
    static let baseURLString = "https://example.com"

    func asURL() throws -> URL {
        let urlString = User.baseURLString + "/users/\(username)/"
        return try urlString.asURL()
    }
}

let user = User(username: "mattt")
Alamofire.request(user) // https://example.com/users/mattt

Cocoapods

CocoaPods 是一款 Cocoa 项指标依赖库管理工科具。使用下边发号施令安装
cocoapods:

$ gem install cocoapods

编写翻译 Alamofire 四.0.0+ 要求 一.1.0 以上版本的 cocoapods

Podfile 中展开宣示来集成 Alamofire:

source 'https://github.com/CocoaPods/Specs.git'platform :ios, '10.0'use_frameworks!target '<Your Target Name>' do pod 'Alamofire', '~> 4.0'end

接下来运营下边包车型地铁授命进行设置:

$ pod install
响应 JSON Handler

responseJSON
handler使用responseJSONSerializer对象根据钦点的JSONSerialization.ReadingOptions把服务器重回的数码转换来Any花色。假诺未有回到错误并且服务器的多寡成功地更换为JSON对象,那么响应Result将会是.successvalueAny类型。

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    debugPrint(response)

    if let json = response.result.value {
        print("JSON: \(json)")
    }
}

富有JSON的种类化,都以利用JSONSerialization完成的。

花色安全传送
extension User: URLConvertible {
    static let baseURLString = "https://example.com"

    func asURL() throws -> URL {
        let urlString = User.baseURLString + "/users/\(username)/"
        return try urlString.asURL()
    }
}

let user = User(username: "mattt")
Alamofire.request(user) // https://example.com/users/mattt

URLRequestConvertible

遵循URLRequestConvertible合计的档次能够被用来创设UTucsonL请求。URLRequest【4858.com】骨干用法,使用指南。暗中认可服从了URLRequestConvertible,允许被直接传入requestuploaddownload(推荐用那种艺术为单个请求自定义请求头)。

let url = URL(string: "https://httpbin.org/post")!
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"

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

do {
    urlRequest.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])
} catch {
    // No-op
}

urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")

Alamofire.request(urlRequest)

以壹种有含义的措施和web应用程序竞相的利用,都鼓励选取自定义的依据URLRequestConvertible磋商的花色来担保请求端点的1致性。那种格局能够用来抽象服务器端的不一致性,并提供品类安全传送,以及管理身份验证凭据和其余情形。

Carthage

Carthage 是壹款编写翻译管理正视库的工具,可提供编写翻译好的 frameworks。

通过 Homebrew 使用一下命令安装 Carthage:

$ brew update$ brew install carthage

Cartfile 中进行宣示来集成 Alamofire:

github "Alamofire/Alamofire" ~> 4.0

运行 carthage update 命令举办编写翻译,并把编写翻译好的 Alamofire.framework
拖拽到你的 Xcode 项目中。

链式响应handler

响应handler能够链接在一道:

Alamofire.request("https://httpbin.org/get")
    .responseString { response in
        print("Response String: \(response.result.value)")
    }
    .responseJSON { response in
        print("Response JSON: \(response.result.value)")
    }

注意:在同一个请求中选择八个响应handler,供给服务器的数目会被系列化数次,每一回对应1个handler。

URLRequestConvertible

遵循URLRequestConvertible合计的档次能够被用来塑造UTucsonL请求。URLRequest暗许遵从了URLRequestConvertible,允许被直接传入requestuploaddownload(推荐用那种办法为单个请求自定义请求头)。

let url = URL(string: "https://httpbin.org/post")!
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"

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

do {
    urlRequest.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])
} catch {
    // No-op
}

urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")

Alamofire.request(urlRequest)

以壹种有意义的诀要和web应用程序相互的选拔,都鼓励利用自定义的遵照URLRequestConvertible协和式飞机的项目来担保请求端点的1致性。那种办法能够用来抽象服务器端的不1致性,并提供项目安全传送,以及管理身份验证凭据和此外景况。

API参数抽象
enum Router: URLRequestConvertible {
    case search(query: String, page: Int)

    static let baseURLString = "https://example.com"
    static let perPage = 50

    // MARK: URLRequestConvertible

    func asURLRequest() throws -> URLRequest {
        let result: (path: String, parameters: Parameters) = {
            switch self {
            case let .search(query, page) where page > 0:
                return ("/search", ["q": query, "offset": Router.perPage * page])
            case let .search(query, _):
                return ("/search", ["q": query])
            }
        }()

        let url = try Router.baseURLString.asURL()
        let urlRequest = URLRequest(url: url.appendingPathComponent(result.path))

        return try URLEncoding.default.encode(urlRequest, with: result.parameters)
    }
}

Alamofire.request(Router.search(query: "foo bar", page: 1)) // https://example.com/search?q=foo%20bar&offset=50

发起呼吁

import AlamofireAlamofire.request("https://httpbin.org/get")
响应handler队列

暗许情形下,响应handler是在主队列执行的。但是大家也足以自定义队列:

let utilityQueue = DispatchQueue.global(qos: .utility)

Alamofire.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
    print("Executing response handler on utility queue")
}
API参数抽象
enum Router: URLRequestConvertible {
    case search(query: String, page: Int)

    static let baseURLString = "https://example.com"
    static let perPage = 50

    // MARK: URLRequestConvertible

    func asURLRequest() throws -> URLRequest {
        let result: (path: String, parameters: Parameters) = {
            switch self {
            case let .search(query, page) where page > 0:
                return ("/search", ["q": query, "offset": Router.perPage * page])
            case let .search(query, _):
                return ("/search", ["q": query])
            }
        }()

        let url = try Router.baseURLString.asURL()
        let urlRequest = URLRequest(url: url.appendingPathComponent(result.path))

        return try URLEncoding.default.encode(urlRequest, with: result.parameters)
    }
}

Alamofire.request(Router.search(query: "foo bar", page: 1)) // https://example.com/search?q=foo%20bar&offset=50
CRUD和授权
import Alamofire

enum Router: URLRequestConvertible {
    case createUser(parameters: Parameters)
    case readUser(username: String)
    case updateUser(username: String, parameters: Parameters)
    case destroyUser(username: String)

    static let baseURLString = "https://example.com"

    var method: HTTPMethod {
        switch self {
        case .createUser:
            return .post
        case .readUser:
            return .get
        case .updateUser:
            return .put
        case .destroyUser:
            return .delete
        }
    }

    var path: String {
        switch self {
        case .createUser:
            return "/users"
        case .readUser(let username):
            return "/users/\(username)"
        case .updateUser(let username, _):
            return "/users/\(username)"
        case .destroyUser(let username):
            return "/users/\(username)"
        }
    }

    // MARK: URLRequestConvertible

    func asURLRequest() throws -> URLRequest {
        let url = try Router.baseURLString.asURL()

        var urlRequest = URLRequest(url: url.appendingPathComponent(path))
        urlRequest.httpMethod = method.rawValue

        switch self {
        case .createUser(let parameters):
            urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
        case .updateUser(_, let parameters):
            urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
        default:
            break
        }

        return urlRequest
    }
}

Alamofire.request(Router.readUser("mattt")) // GET https://example.com/users/mattt

一呼百应回调

拍卖请求的响应只需在 Request 前边加上处理响应的回调。

Alamofire.request("https://httpbin.org/get").responseJSON { response in print(response.request) // original URL request print(response.response) // HTTP URL response print(response.data) // server data print(response.result) // result of response serialization if let JSON = response.result.value { print("JSON: \ }}

地点的例证中,responseJSON 回调拼接在 Request
前边,壹旦互连网请求实现便会履行该
responseJSON。那里未有阻塞线程来等待响应,而是利用了闭包格局的回调来异步接受响应。请求的结果不得不在响应的闭包中进行处理。对响应或从服务器收到到的数额只能在响应闭包中处理。

Alamofire
中互连网请求是异步处理的。对异步编制程序相关的定义不太熟习的话会令人3只雾水,但有太多的说辞让大家接纳异步编制程序。

Alamofire 私下认可包括八种响应回调:

// Response Handler - Unserialized Responsefunc response( queue: DispatchQueue?, completionHandler: @escaping (DefaultDataResponse) -> Void) -> Self// Response Data Handler - Serialized into Datafunc responseData( queue: DispatchQueue?, completionHandler: @escaping (DataResponse<Data>) -> Void) -> Self// Response String Handler - Serialized into Stringfunc responseString( queue: DispatchQueue?, encoding: String.Encoding?, completionHandler: @escaping (DataResponse<String>) -> Void) -> Self// Response JSON Handler - Serialized into Anyfunc responseJSON( queue: DispatchQueue?, completionHandler: @escaping (DataResponse<Any>) -> Void) -> Self// Response PropertyList  Handler - Serialized into Anyfunc responsePropertyList( queue: DispatchQueue?, completionHandler: @escaping (DataResponse<Any>) -> Void)) -> Self

那几个回调都未曾对 HTTPURLResponse 进行认证。

比如,400..499500..599 之间的响应状态码不会自行触发
Error。Alamofire 选择 Response Validation 的链式方法来开始展览表达。

response 不对响应数据举行任何处理,直接把 U凯雷德L session
代理中的数据提交前边的流水生产线,与行使 cURL 执行请求效果一样。

Alamofire.request("https://httpbin.org/get").response { response in print("Request: \(response.request)") print("Response: \(response.response)") print("Error: \(response.error)") if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) { print("Data: \") }}

强烈建议使用其余响应类别化器将数据变成更易使用的 ResponseResult
类型

responseData 回调使用 responseDataSerializer
(该指标用于将从服务器收到的数目体系化为其余品类) 处理服务器端重返的
Data。假若未有不当就重返 Data,response 的 Result 会棉被服装置为
.success value 会被设置为 Data 类型。

Alamofire.request("https://httpbin.org/get").responseData { response in debugPrint("All Response Info: \") if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) { print("Data: \") }}

responseString 回调使用 responseStringSerializer
依照钦赐的编码格局将从服务器收到到的 Data 转换为 String
类型。假如没错误并且数据被成功种类化为 String,则 response 的
Result 会被安装为 .successvalue 会棉被服装置为 String 类型。

Alamofire.request("https://httpbin.org/get").responseString { response in print("Success: \(response.result.isSuccess)") print("Response String: \(response.result.value)")}

借使未钦命编码情势,Alamofire 会依照从服务器端接收到的
HTTPURLResponse
中的编码格局进行编码。假设响应中也未钦定编码格局,暗许使用
.isoLatin1 编码情势

responseJSON 使用 responseJSONSerializer 依据钦点的
JSONSerialization.ReadingOptions 将从服务器收到到的数据 Data 转换为
Any 类型。如若未有不当并且吸收接纳到的数额成功的队列化为 JSON 对象,则
response 的 Result 会棉被服装置为 .success 并且 value 会被安装为 Any
类型。

Alamofire.request("https://httpbin.org/get").responseJSON { response in debugPrint if let json = response.result.value { print("JSON: \ }}

JSON 的系列化由 Foundation 框架中的 JSONSerialization 接口实现。

壹呼百应回调也能够链式调用:

Alamofire.request("https://httpbin.org/get") .responseString { response in print("Response String: \(response.result.value)") } .responseJSON { response in print("Response JSON: \(response.result.value)") }

在意:对同3个呼吁使用八个响应回调须求服务器对数码举行反复类别化,每回种类化针对二个响应回调。

响应回调默许是在主派发队列中履行。不过能够为响应回调钦赐自定义的操作队列。

let utilityQueue = DispatchQueue.global(qos: .utility)Alamofire.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in print("Executing response handler on utility queue")}

响应表明

私下认可景况下,Alamofire把富有完结的请求当做是成功的乞求,无论响应的内容是何等。假若响应有3个不可能被接受的状态码恐怕MIME类型,在响应handler从前调用验证将会时有发生错误。

CRUD和授权
import Alamofire

enum Router: URLRequestConvertible {
    case createUser(parameters: Parameters)
    case readUser(username: String)
    case updateUser(username: String, parameters: Parameters)
    case destroyUser(username: String)

    static let baseURLString = "https://example.com"

    var method: HTTPMethod {
        switch self {
        case .createUser:
            return .post
        case .readUser:
            return .get
        case .updateUser:
            return .put
        case .destroyUser:
            return .delete
        }
    }

    var path: String {
        switch self {
        case .createUser:
            return "/users"
        case .readUser(let username):
            return "/users/\(username)"
        case .updateUser(let username, _):
            return "/users/\(username)"
        case .destroyUser(let username):
            return "/users/\(username)"
        }
    }

    // MARK: URLRequestConvertible

    func asURLRequest() throws -> URLRequest {
        let url = try Router.baseURLString.asURL()

        var urlRequest = URLRequest(url: url.appendingPathComponent(path))
        urlRequest.httpMethod = method.rawValue

        switch self {
        case .createUser(let parameters):
            urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
        case .updateUser(_, let parameters):
            urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
        default:
            break
        }

        return urlRequest
    }
}

Alamofire.request(Router.readUser("mattt")) // GET https://example.com/users/mattt

适配和重试请求

前些天的大多数Web服务,都供给身份注脚。今后可比宽泛的是OAuth。平日是内需3个access
token来授权使用或然用户,然后才方可选拔种种扶助的Web服务。成立这几个access
token是相比较麻烦的,当access
token晚点之后就相比费心了,大家需求再行创建二个新的。有成都百货上千线程安全难题要思索。

RequestAdapterRequestRetrier切磋能够让大家更便于地为一定的Web服务创制2个线程安全的验证系统。

响应表达

私下认可情状下 Alamofire
会忽略响应内容是还是不是正确,只要请求完毕就申明着成功。在响应回调调用此前调用
validata 时,若响应中有不当的网络状态码或错误的 MIME
格式的数量则会抛出荒谬。

Alamofire.request("https://httpbin.org/get") .validate(statusCode: 200..<300) .validate(contentType: ["application/json"]) .responseData { response in switch response.result { case .success: print("Validation Successful") case .failure(let error): print } }

机动验证会验证 200...299 之间的状态码并表明响应数据的 Content-Type
是还是不是和请求头的内定的 Accept 类型是还是不是相称。

Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in switch response.result { case .success: print("Validation Successful") case .failure(let error): print }}
手动验证
Alamofire.request("https://httpbin.org/get")
    .validate(statusCode: 200..<300)
    .validate(contentType: ["application/json"])
    .responseData { response in
    switch response.result {
    case .success:
        print("Validation Successful")
    case .failure(let error):
        print(error)
    }
}

适配和重试请求

近来的大多数Web服务,都须求身份认证。今后可比宽泛的是OAuth。平时是急需2个access
token来授权使用也许用户,然后才方可选拔种种援救的Web服务。创立那几个access
token是相比麻烦的,当access
token晚点过后就相比较费力了,大家供给再行成立3个新的。有众多线程安全题材要思考。

RequestAdapterRequestRetrier合计得以让咱们更便于地为特定的Web服务创造多少个线程安全的认证连串。

RequestAdapter

RequestAdapter协和式飞机允许每三个SessionManagerRequest在开创在此之前被检查和适配。三个不胜特别的行使适配器方法是,在三个一定的认证项目,把Authorization
header拼接到请求。

class AccessTokenAdapter: RequestAdapter {
    private let accessToken: String

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

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

        if let urlString = urlRequest.url?.absoluteString, 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")

壹呼百应缓存

壹呼百应的缓存操作由系统级框架 URLCache
完结。其同时提供了内部存款和储蓄器,硬盘两种缓存方式并且用户能够设置可缓存的深浅。

Alamofire 暗中认可会使用共享的 URLCache。查看 Session Manager
Configurations 实行自定义。

电动验证

活动验证在200…29九限量内的状态码;假使请求头中有钦定Accept,那么也会注脚响应头的与请求头Accept一样的Content-Type

Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in
    switch response.result {
    case .success:
        print("Validation Successful")
    case .failure(let error):
        print(error)
    }
}

RequestAdapter

RequestAdapter合计允许每二个SessionManagerRequest在开创以前被检查和适配。二个尤其尤其的行使适配器方法是,在一个一定的认证项目,把Authorization
header拼接到请求。

class AccessTokenAdapter: RequestAdapter {
    private let accessToken: String

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

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

        if let urlString = urlRequest.url?.absoluteString, 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")

RequestRetrier

RequestRetrier说道允许3个在实施进程中遭逢error的央求被重试。当二头使用RequestAdapterRequestRetrier情商时,我们能够为OAuth一、OAuth2、Basic
Auth(每一趟请求API都要提供用户名和密码)甚至是exponential
backoff重试策略创立资格复苏系统。上边包车型客车例子演示了什么促成一个OAuth贰access token的恢复流程。

免责注解:那不是二个健全的OAuth二消除方案。那可是是出现说法怎样把RequestAdapterRequestRetrier合计结合起来创立1个线程安全的过来系统。

重申:
不要把那个例子复制到实际的付出使用中,那无非是一个事例。每一个验证系统必须为各种特定的平台和验证项目重新定制。

class OAuth2Handler: RequestAdapter, RequestRetrier {
    private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?, _ refreshToken: String?) -> Void

    private let sessionManager: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders

        return SessionManager(configuration: configuration)
    }()

    private let lock = NSLock()

    private var clientID: String
    private var baseURLString: String
    private var accessToken: String
    private var refreshToken: String

    private var isRefreshing = false
    private var requestsToRetry: [RequestRetryCompletion] = []

    // MARK: - Initialization

    public init(clientID: String, baseURLString: String, accessToken: String, refreshToken: String) {
        self.clientID = clientID
        self.baseURLString = baseURLString
        self.accessToken = accessToken
        self.refreshToken = refreshToken
    }

    // MARK: - RequestAdapter

    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseURLString) {
            var urlRequest = urlRequest
            urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
            return urlRequest
        }

        return urlRequest
    }

    // MARK: - RequestRetrier

    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
        lock.lock() ; defer { lock.unlock() }

        if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 {
            requestsToRetry.append(completion)

            if !isRefreshing {
                refreshTokens { [weak self] succeeded, accessToken, refreshToken in
                    guard let strongSelf = self else { return }

                    strongSelf.lock.lock() ; defer { strongSelf.lock.unlock() }

                    if let accessToken = accessToken, let refreshToken = refreshToken {
                        strongSelf.accessToken = accessToken
                        strongSelf.refreshToken = refreshToken
                    }

                    strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) }
                    strongSelf.requestsToRetry.removeAll()
                }
            }
        } else {
            completion(false, 0.0)
        }
    }

    // MARK: - Private - Refresh Tokens

    private func refreshTokens(completion: @escaping RefreshCompletion) {
        guard !isRefreshing else { return }

        isRefreshing = true

        let urlString = "\(baseURLString)/oauth2/token"

        let parameters: [String: Any] = [
            "access_token": accessToken,
            "refresh_token": refreshToken,
            "client_id": clientID,
            "grant_type": "refresh_token"
        ]

        sessionManager.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default)
            .responseJSON { [weak self] response in
                guard let strongSelf = self else { return }

                if 
                    let json = response.result.value as? [String: Any], 
                    let accessToken = json["access_token"] as? String, 
                    let refreshToken = json["refresh_token"] as? String 
                {
                    completion(true, accessToken, refreshToken)
                } else {
                    completion(false, nil, nil)
                }

                strongSelf.isRefreshing = false
            }
    }
}

let baseURLString = "https://some.domain-behind-oauth2.com"

let oauthHandler = OAuth2Handler(
    clientID: "12345678",
    baseURLString: baseURLString,
    accessToken: "abcd1234",
    refreshToken: "ef56789a"
)

let sessionManager = SessionManager()
sessionManager.adapter = oauthHandler
sessionManager.retrier = oauthHandler

let urlString = "\(baseURLString)/some/endpoint"

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

一旦OAuth2HandlerSessionManager被选取与adapterretrier,他将会透过活动回复access
token来处理1个地下的access token
error,并且依据失利的依次来重试全数曲折的呼吁。(要是供给让他们根据成立的年月各类来推行,能够运用他们的task
identifier来排序)

下面那个事例只是检查了401响应码,不是出现说法怎么着检查三个不法的access
token error。在其实支出使用中,我们想要检查realmwww-authenticate
header响应,固然那取决于OAuth贰的落到实处。

再有一个要器重注意的是,那些申明系统可以在七个session
manager之间共享。例如,能够在同一个Web服务集聚使用defaultephemeral对话配置。上边那个事例能够在多少个session
manager间共享三个oauthHandler实例,来保管3个重操旧业流程。

HTTP 方法

HTTPMethod 枚举出了 ENVISIONFC 7231 §四.3 中定义的 HTTP 方法:

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"}

可以为 Alamofire.request 接口的 method 参数设置那么些值:

Alamofire.request("https://httpbin.org/get") // method defaults to `.get`Alamofire.request("https://httpbin.org/post", method: .post)Alamofire.request("https://httpbin.org/put", method: .put)Alamofire.request("https://httpbin.org/delete", method: .delete)

Alamofire.request 的 method 参数默许是 .get

1呼百应缓存

1呼百应缓存是应用系统的框架URLCache来处理的。它提供了内部存款和储蓄器和磁盘上的缓存,并同意大家决定内部存款和储蓄器和磁盘的轻重缓急。

暗中同意景况下,Alamofire采纳共享的URLCache。假设要自定义,上边会讲到。

RequestRetrier

RequestRetrier磋商允许2个在实践进程中遭逢error的乞请被重试。当一头利用RequestAdapterRequestRetrier说道时,大家能够为OAuth一、OAuth贰、Basic
Auth(每回请求API都要提供用户名和密码)甚至是exponential
backoff重试策略创建资格恢复生机系统。下边包车型地铁例子演示了怎么落实3个OAuth2access token的回涨流程。

豁免权利评释:那不是贰个健全的OAuth2化解方案。那只是是出现说法怎样把RequestAdapterRequestRetrier协和式飞机结合起来创造1个线程安全的还原系统。

重申:
不要把那一个事例复制到实际的付出使用中,那唯有是三个事例。各类验证系统必须为各类特定的平台和表明项目重新定制。

class OAuth2Handler: RequestAdapter, RequestRetrier {
    private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?, _ refreshToken: String?) -> Void

    private let sessionManager: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders

        return SessionManager(configuration: configuration)
    }()

    private let lock = NSLock()

    private var clientID: String
    private var baseURLString: String
    private var accessToken: String
    private var refreshToken: String

    private var isRefreshing = false
    private var requestsToRetry: [RequestRetryCompletion] = []

    // MARK: - Initialization

    public init(clientID: String, baseURLString: String, accessToken: String, refreshToken: String) {
        self.clientID = clientID
        self.baseURLString = baseURLString
        self.accessToken = accessToken
        self.refreshToken = refreshToken
    }

    // MARK: - RequestAdapter

    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseURLString) {
            var urlRequest = urlRequest
            urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
            return urlRequest
        }

        return urlRequest
    }

    // MARK: - RequestRetrier

    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
        lock.lock() ; defer { lock.unlock() }

        if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 {
            requestsToRetry.append(completion)

            if !isRefreshing {
                refreshTokens { [weak self] succeeded, accessToken, refreshToken in
                    guard let strongSelf = self else { return }

                    strongSelf.lock.lock() ; defer { strongSelf.lock.unlock() }

                    if let accessToken = accessToken, let refreshToken = refreshToken {
                        strongSelf.accessToken = accessToken
                        strongSelf.refreshToken = refreshToken
                    }

                    strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) }
                    strongSelf.requestsToRetry.removeAll()
                }
            }
        } else {
            completion(false, 0.0)
        }
    }

    // MARK: - Private - Refresh Tokens

    private func refreshTokens(completion: @escaping RefreshCompletion) {
        guard !isRefreshing else { return }

        isRefreshing = true

        let urlString = "\(baseURLString)/oauth2/token"

        let parameters: [String: Any] = [
            "access_token": accessToken,
            "refresh_token": refreshToken,
            "client_id": clientID,
            "grant_type": "refresh_token"
        ]

        sessionManager.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default)
            .responseJSON { [weak self] response in
                guard let strongSelf = self else { return }

                if 
                    let json = response.result.value as? [String: Any], 
                    let accessToken = json["access_token"] as? String, 
                    let refreshToken = json["refresh_token"] as? String 
                {
                    completion(true, accessToken, refreshToken)
                } else {
                    completion(false, nil, nil)
                }

                strongSelf.isRefreshing = false
            }
    }
}

let baseURLString = "https://some.domain-behind-oauth2.com"

let oauthHandler = OAuth2Handler(
    clientID: "12345678",
    baseURLString: baseURLString,
    accessToken: "abcd1234",
    refreshToken: "ef56789a"
)

let sessionManager = SessionManager()
sessionManager.adapter = oauthHandler
sessionManager.retrier = oauthHandler

let urlString = "\(baseURLString)/some/endpoint"

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

一旦OAuth2HandlerSessionManager被运用与adapterretrier,他将会通过自动回复access
token来拍卖一个不法的access token
error,并且遵照退步的顺序来重试全部曲折的乞请。(若是急需让他们依据创造的年华各种来施行,可以行使他们的task
identifier来排序)

地点这一个例子只是检查了401响应码,不是出现说法怎么着检查一个地下的access
token error。在事实上开发应用中,我们想要检查realmwww-authenticate
header响应,纵然那取决于OAuth二的落到实处。

还有二个要重视注意的是,那些申明系统能够在四个session
manager之间共享。例如,能够在同一个Web服务汇集使用defaultephemeral对话配置。上面这些事例能够在八个session
manager间共享3个oauthHandler实例,来治本二个恢复生机流程。

自定义响应类别化

Alamofire为data、strings、JSON和Property List提供了内置的响应体系化:

Alamofire.request(...).responseData { (resp: DataResponse<Data>) in ... }
Alamofire.request(...).responseString { (resp: DataResponse<String>) in ... }
Alamofire.request(...).responseJSON { (resp: DataResponse<Any>) in ... }
Alamofire.request(...).responsePropertyList { resp: DataResponse<Any>) in ... }

这几个响应包装了反类别化的值(Data, String, Any)可能error (network,
validation errors),以及元数据 (ULX570L Request, HTTP headers, status code,
metrics, …)。

我们可以有五个章程来自定义全体响应成分:

  • 1呼百应映射
  • 处理错误
  • 创制2个自定义的响应体系化器
  • 泛型响应对象连串化

呼吁参数编码

Alamofire 暗中同意提供了二种参数编码格局,蕴涵
URL,JSON,PropertyList。同时也支撑遵从了 ParameterEncoding
协议的编码情势。

URLEncoding 编码形式创立了 url
编码的询问字符串并将其拼接到存在的央浼字符串后或许设置为 UQashqaiL 请求的 HTTP
body。对于编码后的查询字符串,是一贯动用,拼接依然设置为 HTTP body
取决于编码的 DestinationDestination 包罗两种方法:

  • .methodDependent – 若请求格局是
    GET,HEAD,DELETE,则将编码的询问字符串与留存的询问字符串进行拼接,对于其余请求格局则设置为呼吁的
    HTTP body。
  • .queryString – 将编码的查询字符串与存在的询问字符串实行拼接。
  • .httpBody – 设置为呼吁的 HTTP body。

请求头中的 Content-Type 字段被设置为
application/x-www-form-urlencoded; charset=utf-8。URL 编码中并没有规定集合类型该如何进行编码。我们约定,对数组类型将[]拼接到 key 后面(foo[]=1&foo[]=2),对字典类型将中括号包围的 key 拼接在请求的键后(foo[bar]=baz`)。

HTTP方法

HTTPMethod历数了下边包车型地铁那个艺术:

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"
}

在使用Alamofire.request时,可以流传方法参数:

Alamofire.request("https://httpbin.org/get") // 默认是get请求

Alamofire.request("https://httpbin.org/post", method: .post)
Alamofire.request("https://httpbin.org/put", method: .put)
Alamofire.request("https://httpbin.org/delete", method: .delete)

自定义响应系列化

Alamofire为data、strings、JSON和Property List提供了安放的响应体系化:

Alamofire.request(...).responseData { (resp: DataResponse<Data>) in ... }
Alamofire.request(...).responseString { (resp: DataResponse<String>) in ... }
Alamofire.request(...).responseJSON { (resp: DataResponse<Any>) in ... }
Alamofire.request(...).responsePropertyList { resp: DataResponse<Any>) in ... }

那个响应包装了反类别化的值(Data, String, Any)可能error (network,
validation errors),以及元数据 (UEnclaveL Request, HTTP headers, status code,
metrics, …)。

咱俩能够有多少个主意来自定义全体响应成分:

  • 壹呼百应映射
  • 处理错误
  • 制造二个自定义的响应连串化器
  • 泛型响应对象连串化

1呼百应映射

响应映射是自定义响应最简便的主意。它转换响应的值,同时保留最终错误和元数据。例如,我们得以把3个json响应DataResponse<Any>转换为一个封存应用模型的的响应,例如DataResponse<User>。使用DataResponse.map来开始展览响应映射:

Alamofire.request("https://example.com/users/mattt").responseJSON { (response: DataResponse<Any>) in
    let userResponse = response.map { json in
        // We assume an existing User(json: Any) initializer
        return User(json: json)
    }

    // Process userResponse, of type DataResponse<User>:
    if let user = userResponse.value {
        print("User: { username: \(user.username), name: \(user.name) }")
    }
}

当转换恐怕会抛出错误时,使用flatMap方法:

Alamofire.request("https://example.com/users/mattt").responseJSON { response in
    let userResponse = response.flatMap { json in
        try User(json: json)
    }
}

壹呼百应映射分外适合自定义completion handler:

@discardableResult
func loadUser(completionHandler: @escaping (DataResponse<User>) -> Void) -> Alamofire.DataRequest {
    return Alamofire.request("https://example.com/users/mattt").responseJSON { response in
        let userResponse = response.flatMap { json in
            try User(json: json)
        }

        completionHandler(userResponse)
    }
}

loadUser { response in
    if let user = userResponse.value {
        print("User: { username: \(user.username), name: \(user.name) }")
    }
}

地点代码中loadUser方法被@discardableResult标志,意思是调用loadUser格局能够不接受它的重返值;也能够用_来忽略重回值。

当 map/flatMap
闭包会产生相比大的数据量时,要确定保证这些闭包在子线程中执行:

@discardableResult
func loadUser(completionHandler: @escaping (DataResponse<User>) -> Void) -> Alamofire.DataRequest {
    let utilityQueue = DispatchQueue.global(qos: .utility)

    return Alamofire.request("https://example.com/users/mattt").responseJSON(queue: utilityQueue) { response in
        let userResponse = response.flatMap { json in
            try User(json: json)
        }

        DispatchQueue.main.async {
            completionHandler(userResponse)
        }
    }
}

mapflatMap也足以用来下载响应。

获取使用 U昂CoraL 编码参数的请求
let parameters: Parameters = ["foo": "bar"]// All three of these calls are equivalentAlamofire.request("https://httpbin.org/get", parameters: parameters) // encoding defaults to `URLEncoding.default`Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding.default)Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding(destination: .methodDependent))// https://httpbin.org/get?foo=bar

参数编码

Alamofire帮衬三种参数编码:URLJSONPropertyList。还援救遵从了ParameterEncoding切磋的自定义编码。

一呼百应映射

响应映射是自定义响应最简单易行的格局。它转换响应的值,同时保留最终错误和元数据。例如,大家得以把2个json响应DataResponse<Any>改换为一个保存应用模型的的响应,例如DataResponse<User>。使用DataResponse.map来开始展览响应映射:

Alamofire.request("https://example.com/users/mattt").responseJSON { (response: DataResponse<Any>) in
    let userResponse = response.map { json in
        // We assume an existing User(json: Any) initializer
        return User(json: json)
    }

    // Process userResponse, of type DataResponse<User>:
    if let user = userResponse.value {
        print("User: { username: \(user.username), name: \(user.name) }")
    }
}

当转换恐怕会抛出荒谬时,使用flatMap方法:

Alamofire.request("https://example.com/users/mattt").responseJSON { response in
    let userResponse = response.flatMap { json in
        try User(json: json)
    }
}

一呼百应映射十二分适合自定义completion handler:

@discardableResult
func loadUser(completionHandler: @escaping (DataResponse<User>) -> Void) -> Alamofire.DataRequest {
    return Alamofire.request("https://example.com/users/mattt").responseJSON { response in
        let userResponse = response.flatMap { json in
            try User(json: json)
        }

        completionHandler(userResponse)
    }
}

loadUser { response in
    if let user = userResponse.value {
        print("User: { username: \(user.username), name: \(user.name) }")
    }
}

地点代码中loadUser方法被@discardableResult标志,意思是调用loadUser办法能够不接受它的重返值;也足以用_来忽略重临值。

当 map/flatMap
闭包会爆发比较大的数据量时,要力保这些闭包在子线程中执行:

@discardableResult
func loadUser(completionHandler: @escaping (DataResponse<User>) -> Void) -> Alamofire.DataRequest {
    let utilityQueue = DispatchQueue.global(qos: .utility)

    return Alamofire.request("https://example.com/users/mattt").responseJSON(queue: utilityQueue) { response in
        let userResponse = response.flatMap { json in
            try User(json: json)
        }

        DispatchQueue.main.async {
            completionHandler(userResponse)
        }
    }
}

mapflatMap也得以用来下载响应。

处理错误

在达成自定义响应系列化器大概目的体系化方法前,思虑怎么着处理全数十分大可能率出现的一无可取是1贰分重大的。有八个主意:一)传递未修改的失实,在响应时间拍卖;二)把具有的不当封装在三个Error类型中。

比如,上边是等会要用用到的后端错误:

enum BackendError: Error {
    case network(error: Error) // 捕获任何从URLSession API产生的错误
    case dataSerialization(error: Error)
    case jsonSerialization(error: Error)
    case xmlSerialization(error: Error)
    case objectSerialization(reason: String)
}
发起使用 U奇骏L 编码参数的伸手
let parameters: Parameters = [ "foo": "bar", "baz": ["a", 1], "qux": [ "x": 1, "y": 2, "z": 3 ]]// All three of these calls are equivalentAlamofire.request("https://httpbin.org/post", parameters: parameters)Alamofire.request("https://httpbin.org/post", parameters: parameters, encoding: URLEncoding.default)Alamofire.request("https://httpbin.org/post", parameters: parameters, encoding: URLEncoding.httpBody)// HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3

JSONEncoding 的编码格局制造了 JSON 格式的乞求参数,并设置为呼吁的 HTTP
body。HTTP 请求头的 Content-Type 字段设置为 applicatioin/json

URL编码

URLEncoding类型创设了二个UBMWX三L编码的查询字符串来设置也许添加到三个存世的U宝马X3L查询字符串,或然安装UEvoqueL请求的请求体。查询字符串是不是被设置大概添加到存活的UGL450L查询字符串,只怕被看成HTTP请求体,决定于编码的Destination。编码的Destination有三个case:

  • .methodDependent:为GETHEADDELETE伸手使用编码查询字符串来安装只怕加上到存活查询字符串,并且应用别的HTTP方法来设置请求体。
  • .queryString:设置恐怕添加编码查询字符串到现有查询字符串
  • .httpBody:把编码查询字符串作为U昂CoraL请求的请求体

贰个编码请求的请求体的Content-Type字段被安装为application/x-www-form-urlencoded; charset=utf-8。因为未有公开的正经认证什么编码集合类型,所以依据规矩在key前边添加[]来代表数组的值(foo[]=1&foo[]=2),在key外面包1其中括号来表示字典的值(foo[bar]=baz)。

处理错误

在促成自定义响应连串化器或然目的类别化方法前,考虑怎样处理全数一点都不小希望出现的荒唐是十三分关键的。有七个法子:一)传递未修改的荒谬,在响应时间拍卖;2)把装有的一无可取封装在一个Error类型中。

譬如说,上边是等会要用用到的后端错误:

enum BackendError: Error {
    case network(error: Error) // 捕获任何从URLSession API产生的错误
    case dataSerialization(error: Error)
    case jsonSerialization(error: Error)
    case xmlSerialization(error: Error)
    case objectSerialization(reason: String)
}

创制1个自定义的响应连串化器

Alamofire为strings、JSON和Property
List提供了安放的响应连串化,可是大家得以因此增加Alamofire.DataRequest或者Alamofire.DownloadRequest来添加其余种类化。

例如,下边那么些例子是二个利用Ono
(贰个实用的处理iOS和macOS平台的XML和HTML的措施)的响应handler的兑现:

extension DataRequest {
    static func xmlResponseSerializer() -> DataResponseSerializer<ONOXMLDocument> {
        return DataResponseSerializer { request, response, data, error in
            // 把任何底层的URLSession error传递给 .network case
            guard error == nil else { return .failure(BackendError.network(error: error!)) }

            // 使用Alamofire已有的数据序列化器来提取数据,error为nil,因为上一行代码已经把不是nil的error过滤了
            let result = Request.serializeResponseData(response: response, data: data, error: nil)

            guard case let .success(validData) = result else {
                return .failure(BackendError.dataSerialization(error: result.error! as! AFError))
            }

            do {
                let xml = try ONOXMLDocument(data: validData)
                return .success(xml)
            } catch {
                return .failure(BackendError.xmlSerialization(error: error))
            }
        }
    }

    @discardableResult
    func responseXMLDocument(
        queue: DispatchQueue? = nil,
        completionHandler: @escaping (DataResponse<ONOXMLDocument>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: DataRequest.xmlResponseSerializer(),
            completionHandler: completionHandler
        )
    }
}
倡议使用 JSON 编码参数的请求
let parameters: Parameters = [ "foo": [1,2,3], "bar": [ "baz": "qux" ]]// Both calls are equivalentAlamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding.default)Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding(options: []))// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}

当 Alamofire
提供的参数编码情势不能够满意供给时,能够创设自定义的编码形式。下边是三个自定义的
JSONStringEncoding 编码格局的例子,该措施将 string 数组的 JSON
对象编码到 Request 中。

struct JSONStringArrayEncoding: ParameterEncoding { private let array: [String] init(array: [String]) { self.array = array } func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest { var urlRequest = urlRequest.urlRequest let data = try JSONSerialization.data(withJSONObject: array, options: []) if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil { urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") } urlRequest.httpBody = data return urlRequest }}

Alamofire
从服务器收到到的数额保存在缓存或硬盘上。到近年来为止全部例子中采用
Alamofire.request
接口获取的数额都封存在缓存中。对于小数码那是很迅猛的,但对此较大的数据量也许会耗尽缓存。由此须求利用
Alamofire.download 接口将数据保存在硬盘的临时文件中。

Alamofire.download("https://httpbin.org/image/png").responseData { response in if let data = response.result.value { let image = UIImage(data: data) }}

当须求在后台下载数据时也应有运用 Alamofire.download
接口。越多消息请查看 Session Manager Configurations 章节

您能够提供1个 DownloadFileDestination
闭包用于把一时半刻文件移动到内定的不二等秘书籍下。在移动一时文件前会先进行闭包中钦点的
DownloadOptioins。当前帮助的两种 DownloadOptions 分别是:

  • .createIntermediateDirectories – 为内定的门道创立完整的门道
  • .removePreviousFile – 移除目的路径下存在的文本

let destination: DownloadRequest.DownloadFileDestination = { _, _ in let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let fileURL = documentsURL.appendPathComponent("pig.png") return (fileURL, [.removePreviousFile, .createIntermediateDirectories])}Alamofire.download(urlString, to: destination).response { response in print if response.error == nil, let imagePath = response.destinationURL?.path { let image = UIImage(contentsOfFile: imagePath) }}

也足以使用推荐下载路径 API。

let destination = DownloadRequest.suggestedDownloadDestination(directory: .documentDirectory)Alamofire.download("https://httpbin.org/image/png", to: destination)

在下载时亦可告诉下载进程是格外有效的。任何 DownloadRequest
请求能够透过 downloadProgress 接口报告下载进程。

Alamofire.download("https://httpbin.org/image/png") .downloadProgress { progress in print("Download Progress: \(progress.fractionCompleted)") } .responseData { response in if let data = response.result.value { let image = UIImage(data: data) } }

也可以为 downloadProgress 接口钦赐下载速度闭包执行的派发队列。

let utilityQueue = DispatchQueue.global(qos: .utility)Alamofire.download("https://httpbin.org/image/png") .downloadProgress(queue: utilityQueue) { progress in print("Download Progress: \(progress.fractionCompleted)") } .responseData { response in if let data = response.result.value { let image = UIImage(data: data) } }

要是2个 DownloadRequest 请求裁撤或暂停了,U奥迪Q5L
会话大概会为该请求生成恢复数据,该苏醒数据可用于 DownloadRequest
请求从中断的地点苏醒下载。恢复生机数据足以从下载响应中取得,然后用于苏醒下载。

class ImageRequestor { private var resumeData: Data? private var image: UIImage? func fetchImage(completion:  -> Void) { guard image == nil else { completion ; return } let destination: DownloadRequest.DownloadFileDestination = { _, _ in let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let fileURL = documentsURL.appendPathComponent("pig.png") return (fileURL, [.removePreviousFile, .createIntermediateDirectories]) } let request: DownloadRequest if let resumeData = resumeData { request = Alamofire.download(resumingWith: resumeData) } else { request = Alamofire.download("https://httpbin.org/image/png") } request.responseData { response in switch response.result { case .success: self.image = UIImage(data: data) case .failure: self.resumeData = response.resumeData } } }}
利用UTiggoL编码参数的GET请求
let parameters: Parameters = ["foo": "bar"]

// 下面这三种写法是等价的
Alamofire.request("https://httpbin.org/get", parameters: parameters) // encoding 默认是`URLEncoding.default`
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding(destination: .methodDependent))

// https://httpbin.org/get?foo=bar

始建二个自定义的响应类别化器

Alamofire为strings、JSON和Property
List提供了内置的响应连串化,然则大家得以经过扩张Alamofire.DataRequest或者Alamofire.DownloadRequest来添加其余连串化。

譬如说,下边那个事例是多个使用Ono
(三个实用的处理iOS和macOS平台的XML和HTML的不二诀窍)的响应handler的贯彻:

extension DataRequest {
    static func xmlResponseSerializer() -> DataResponseSerializer<ONOXMLDocument> {
        return DataResponseSerializer { request, response, data, error in
            // 把任何底层的URLSession error传递给 .network case
            guard error == nil else { return .failure(BackendError.network(error: error!)) }

            // 使用Alamofire已有的数据序列化器来提取数据,error为nil,因为上一行代码已经把不是nil的error过滤了
            let result = Request.serializeResponseData(response: response, data: data, error: nil)

            guard case let .success(validData) = result else {
                return .failure(BackendError.dataSerialization(error: result.error! as! AFError))
            }

            do {
                let xml = try ONOXMLDocument(data: validData)
                return .success(xml)
            } catch {
                return .failure(BackendError.xmlSerialization(error: error))
            }
        }
    }

    @discardableResult
    func responseXMLDocument(
        queue: DispatchQueue? = nil,
        completionHandler: @escaping (DataResponse<ONOXMLDocument>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: DataRequest.xmlResponseSerializer(),
            completionHandler: completionHandler
        )
    }
}

泛型响应对象系列化

泛型可以用来提供自动的、类型安全的响应对象种类化。

protocol ResponseObjectSerializable {
    init?(response: HTTPURLResponse, representation: Any)
}

extension DataRequest {
    func responseObject<T: ResponseObjectSerializable>(
        queue: DispatchQueue? = nil,
        completionHandler: @escaping (DataResponse<T>) -> Void)
        -> Self
    {
        let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
            guard error == nil else { return .failure(BackendError.network(error: error!)) }

            let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
            let result = jsonResponseSerializer.serializeResponse(request, response, data, nil)

            guard case let .success(jsonObject) = result else {
                return .failure(BackendError.jsonSerialization(error: result.error!))
            }

            guard let response = response, let responseObject = T(response: response, representation: jsonObject) else {
                return .failure(BackendError.objectSerialization(reason: "JSON could not be serialized: \(jsonObject)"))
            }

            return .success(responseObject)
        }

        return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
    }
}

struct User: ResponseObjectSerializable, CustomStringConvertible {
    let username: String
    let name: String

    var description: String {
        return "User: { username: \(username), name: \(name) }"
    }

    init?(response: HTTPURLResponse, representation: Any) {
        guard
            let username = response.url?.lastPathComponent,
            let representation = representation as? [String: Any],
            let name = representation["name"] as? String
        else { return nil }

        self.username = username
        self.name = name
    }
}

Alamofire.request("https://example.com/users/mattt").responseObject { (response: DataResponse<User>) in
    debugPrint(response)

    if let user = response.result.value {
        print("User: { username: \(user.username), name: \(user.name) }")
    }
}

同等地点法能够用来处理回来对象集合的接口:

protocol ResponseCollectionSerializable {
    static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self]
}

extension ResponseCollectionSerializable where Self: ResponseObjectSerializable {
    static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] {
        var collection: [Self] = []

        if let representation = representation as? [[String: Any]] {
            for itemRepresentation in representation {
                if let item = Self(response: response, representation: itemRepresentation) {
                    collection.append(item)
                }
            }
        }

        return collection
    }
}

extension DataRequest {
    @discardableResult
    func responseCollection<T: ResponseCollectionSerializable>(
        queue: DispatchQueue? = nil,
        completionHandler: @escaping (DataResponse<[T]>) -> Void) -> Self
    {
        let responseSerializer = DataResponseSerializer<[T]> { request, response, data, error in
            guard error == nil else { return .failure(BackendError.network(error: error!)) }

            let jsonSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
            let result = jsonSerializer.serializeResponse(request, response, data, nil)

            guard case let .success(jsonObject) = result else {
                return .failure(BackendError.jsonSerialization(error: result.error!))
            }

            guard let response = response else {
                let reason = "Response collection could not be serialized due to nil response."
                return .failure(BackendError.objectSerialization(reason: reason))
            }

            return .success(T.collection(from: response, withRepresentation: jsonObject))
        }

        return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
    }
}

struct User: ResponseObjectSerializable, ResponseCollectionSerializable, CustomStringConvertible {
    let username: String
    let name: String

    var description: String {
        return "User: { username: \(username), name: \(name) }"
    }

    init?(response: HTTPURLResponse, representation: Any) {
        guard
            let username = response.url?.lastPathComponent,
            let representation = representation as? [String: Any],
            let name = representation["name"] as? String
        else { return nil }

        self.username = username
        self.name = name
    }
}

Alamofire.request("https://example.com/users").responseCollection { (response: DataResponse<[User]>) in
    debugPrint(response)

    if let users = response.result.value {
        users.forEach { print("- \($0)") }
    }
}

上传数据到服务器

上传少量的数码到服务器能够运用 JSON 大概 UEscortL 编码参数的点子展开,那时
Alamofire.request 接口平时很飞速。当须要上传的多少较大,比如文件只怕
InputStream,那时必要选取 Alamofire.upload 接口。

当需求在后台上传数据时也应有利用 Alamofire.upload,愈多音信请查看
Session Manager Configurations 章节。

let imageData = UIPNGRepresentation!Alamofire.upload(imageData, to: "https://httpbin.org/post").responseJSON { response in debugPrint}

let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")Alamofire.upload(fileURL, to: "https://httpbin.org/post").responseJSON { response in debugPrint}

Alamofire.upload( multipartFormData: { multipartFormData in multipartFormData.append(unicornImageURL, withName: "unicorn") multipartFormData.append(rainbowImageURL, withName: "rainbow") }, to: "https://httpbin.org/post", encodingCompletion: { encodingResult in switch encodingResult { case .success(let upload, _, _): upload.responseJSON { response in debugPrint } case .failure(let encodingError): print(encodingError) } })

当用户在上传时能够显得上传进程是卓殊友善的。任何 UploadRequest
请求都能经过 uploadProgressdoanloadProgress
接口报告上传进程和下载速度。

let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")Alamofire.upload(fileURL, to: "https://httpbin.org/post") .uploadProgress { progress in // main queue by default print("Upload Progress: \(progress.fractionCompleted)") } .downloadProgress { progress in // main queue by default print("Download Progress: \(progress.fractionCompleted)") } .responseJSON { response in debugPrint }
应用U宝马7系L编码参数的POST请求
let parameters: Parameters = [
    "foo": "bar",
    "baz": ["a", 1],
    "qux": [
        "x": 1,
        "y": 2,
        "z": 3
    ]
]

// 下面这三种写法是等价的
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.httpBody)

// HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3

泛型响应对象连串化

泛型能够用来提供自动的、类型安全的响应对象系列化。

protocol ResponseObjectSerializable {
    init?(response: HTTPURLResponse, representation: Any)
}

extension DataRequest {
    func responseObject<T: ResponseObjectSerializable>(
        queue: DispatchQueue? = nil,
        completionHandler: @escaping (DataResponse<T>) -> Void)
        -> Self
    {
        let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
            guard error == nil else { return .failure(BackendError.network(error: error!)) }

            let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
            let result = jsonResponseSerializer.serializeResponse(request, response, data, nil)

            guard case let .success(jsonObject) = result else {
                return .failure(BackendError.jsonSerialization(error: result.error!))
            }

            guard let response = response, let responseObject = T(response: response, representation: jsonObject) else {
                return .failure(BackendError.objectSerialization(reason: "JSON could not be serialized: \(jsonObject)"))
            }

            return .success(responseObject)
        }

        return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
    }
}

struct User: ResponseObjectSerializable, CustomStringConvertible {
    let username: String
    let name: String

    var description: String {
        return "User: { username: \(username), name: \(name) }"
    }

    init?(response: HTTPURLResponse, representation: Any) {
        guard
            let username = response.url?.lastPathComponent,
            let representation = representation as? [String: Any],
            let name = representation["name"] as? String
        else { return nil }

        self.username = username
        self.name = name
    }
}

Alamofire.request("https://example.com/users/mattt").responseObject { (response: DataResponse<User>) in
    debugPrint(response)

    if let user = response.result.value {
        print("User: { username: \(user.username), name: \(user.name) }")
    }
}

一致地方法可以用来处理回来对象集合的接口:

protocol ResponseCollectionSerializable {
    static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self]
}

extension ResponseCollectionSerializable where Self: ResponseObjectSerializable {
    static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] {
        var collection: [Self] = []

        if let representation = representation as? [[String: Any]] {
            for itemRepresentation in representation {
                if let item = Self(response: response, representation: itemRepresentation) {
                    collection.append(item)
                }
            }
        }

        return collection
    }
}

extension DataRequest {
    @discardableResult
    func responseCollection<T: ResponseCollectionSerializable>(
        queue: DispatchQueue? = nil,
        completionHandler: @escaping (DataResponse<[T]>) -> Void) -> Self
    {
        let responseSerializer = DataResponseSerializer<[T]> { request, response, data, error in
            guard error == nil else { return .failure(BackendError.network(error: error!)) }

            let jsonSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments)
            let result = jsonSerializer.serializeResponse(request, response, data, nil)

            guard case let .success(jsonObject) = result else {
                return .failure(BackendError.jsonSerialization(error: result.error!))
            }

            guard let response = response else {
                let reason = "Response collection could not be serialized due to nil response."
                return .failure(BackendError.objectSerialization(reason: reason))
            }

            return .success(T.collection(from: response, withRepresentation: jsonObject))
        }

        return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
    }
}

struct User: ResponseObjectSerializable, ResponseCollectionSerializable, CustomStringConvertible {
    let username: String
    let name: String

    var description: String {
        return "User: { username: \(username), name: \(name) }"
    }

    init?(response: HTTPURLResponse, representation: Any) {
        guard
            let username = response.url?.lastPathComponent,
            let representation = representation as? [String: Any],
            let name = representation["name"] as? String
        else { return nil }

        self.username = username
        self.name = name
    }
}

Alamofire.request("https://example.com/users").responseCollection { (response: DataResponse<[User]>) in
    debugPrint(response)

    if let users = response.result.value {
        users.forEach { print("- \($0)") }
    }
}

安全

对此平安敏感的多少以来,在与服务器和web服务交互时行使安全的HTTPS连接是十二分重大的一步。默许景况下,Alamofire会使用苹果安全框架内置的印证格局来评估服务器提供的证书链。就算保险了证书链是可行的,不过不能防备man-in-the-middle
(MITM)攻击可能别的潜在的尾巴。为了缩小MITM攻击,处理用户的敏感数据或财务音信的使用,应该利用ServerTrustPolicy提供的certificate或者public
key pinning。

Statistical Metrics

Alamofire collects timings throughout the lifecycle of a Request and
creates a Timeline object exposed as a property on all response types.

Alamofire.request("https://httpbin.org/get").responseJSON { response in print(response.timeline)}

The above reports the following Timeline info:

  • Latency: 0.428 seconds
  • Request Duration: 0.428 seconds
  • Serialization Duration: 0.001 seconds
  • Total Duration: 0.429 seconds

In iOS and tvOS 10 and macOS 10.12, Apple introduced the new
URLSessionTaskMetrics APIs. The task metrics encapsulate some fantastic
statistical information about the request and response execution. The
API is very similar to the Timeline, but provides many more statistics
that Alamofire doesn’t have access to compute. The metrics can be
accessed through any response type.

Alamofire.request("https://httpbin.org/get").responseJSON { response in print(response.metrics)}

专注,这几个接口仅在 iOS,tvOS 十 和 macOS 10.12多个阳台上可用。由此,取决于你的布局环境,您要求做以下检查实验:

Alamofire.request("https://httpbin.org/get").responseJSON { response in if #available(iOS 10.0. *) { print(response.metrics) }}
JSON编码

JSONEncoding花色创制了1个参数对象的JOSN呈现,并作为请求体。编码请求的请求头的Content-Type恳请字段被安装为application/json

安全

对此平安敏感的数目来说,在与服务器和web服务交互时使用安全的HTTPS连接是老大重大的一步。暗许情形下,Alamofire会使用苹果安全框架内置的辨证办法来评估服务器提供的证书链。尽管保证了证书链是行得通的,不过无法预防man-in-the-middle
(MITM)攻击只怕别的潜在的漏洞。为了削减MITM攻击,处理用户的敏锐性数据或财务新闻的利用,应该采纳ServerTrustPolicy提供的certificate或者public
key pinning。

ServerTrustPolicy

在经过HTTPS安全连接连接到服务器时,ServerTrustPolicy枚举平常会评估URLAuthenticationChallenge提供的server
trust。

let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
    certificates: ServerTrustPolicy.certificates(),
    validateCertificateChain: true,
    validateHost: true
)

在表明的长河中,有多种办法可以让大家全然控制server trust的评估:

  • performDefaultEvaluation:使用默许的server
    trust评估,允许我们决定是不是验证challenge提供的host。
  • pinCertificates:使用pinned certificates来验证server
    trust。假使pinned certificates相配在那之中叁个服务器证书,那么认为server
    trust是行得通的。
  • pinPublicKeys:使用pinned public keys来验证server
    trust。要是pinned public
    keys相称当中一个服务器证书公钥,那么认为server trust是立见作用的。
  • disableEvaluation:禁止使用全部评估,总是认为server trust是实用的。
  • customEvaluation:使用有关的闭包来评估server
    trust的有用,大家能够完全控制总体验证进程。不过要小心翼翼运用。

cUTucsonL 命令输出

不好的调节和测试平台会让工作变得很麻烦. 幸而, Alamofire Request 对象完毕了
CustomStringConvertibleCustomDebugStringConvertible
协议,那为大家提供了很好的调剂工具。

let request = Alamofire.request("https://httpbin.org/ip")print// GET https://httpbin.org/ip 

let request = Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"])debugPrint

输出:

$ curl -i \ -H "User-Agent: Alamofire/4.0.0" \ -H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" \ -H "Accept-Language: en;q=1.0,fr;q=0.9,de;q=0.8,zh-Hans;q=0.7,zh-Hant;q=0.6,ja;q=0.5" \ "https://httpbin.org/get?foo=bar"

Alamofire 建立在 URLSession 和 U昂CoraL
加载系统上。为了更加好的运用该框架,强烈提出要十分熟练底层网络栈的相关概念

推荐阅读

  • UHavalL 加载系统编制程序指南
  • UGL450LSession 参考文书档案
  • U凯雷德LCache 参考文书档案
  • U奥迪Q5LAuthenticationChallenge 参考文书档案
选择JSON编码参数的POST请求
let parameters: Parameters = [
    "foo": [1,2,3],
    "bar": [
        "baz": "qux"
    ]
]

// 下面这两种写法是等价的
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding(options: []))

// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}

ServerTrustPolicy

在经过HTTPS安全连接连接到服务器时,ServerTrustPolicy枚举平常会评估URLAuthenticationChallenge提供的server
trust。

let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
    certificates: ServerTrustPolicy.certificates(),
    validateCertificateChain: true,
    validateHost: true
)

在注解的经过中,有二种办法能够让大家全然控制server trust的评估:

  • performDefaultEvaluation:使用私下认可的server
    trust评估,允许我们决定是不是验证challenge提供的host。
  • pinCertificates:使用pinned certificates来验证server
    trust。假如pinned certificates匹配个中一个服务器证书,那么认为server
    trust是行之有效的。
  • pinPublicKeys:使用pinned public keys来验证server
    trust。即使pinned public
    keys相称个中三个服务器证书公钥,那么认为server trust是实惠的。
  • disableEvaluation:禁止使用全数评估,总是觉得server trust是可行的。
  • customEvaluation:使用相关的闭包来评估server
    trust的实惠,大家得以完全控制总体验证进度。不过要小心选用。

服务器信任策略高管 (Server Trust Policy Manager)

ServerTrustPolicyManager担当储存一个里边的服务器信任策略到一定主机的炫耀。那样Alamofire就足以评估每种主机差异服务器信任策略。

let serverTrustPolicies: [String: ServerTrustPolicy] = [
    "test.example.com": .pinCertificates(
        certificates: ServerTrustPolicy.certificates(),
        validateCertificateChain: true,
        validateHost: true
    ),
    "insecure.expired-apis.com": .disableEvaluation
]

let sessionManager = SessionManager(
    serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)

注意:要保证有八个强引用引用着SessionManager实例,否则当sessionManager被销毁时,请求将会撤除。

这几个服务器信任策略将会形成上边包车型大巴结果:

  • test.example.com:始终使用证书链固定的证明和启用主机验证,因而供给以下标准才能是TLS握手成功:
    • 证书链必须是卓有功能的。
    • 证书链必须包涵一个已经稳定的注明。
    • Challenge主机必须匹配主机证书链的子证书。
  • insecure.expired-apis.com:将从不评估证书链,并且连接允许TLS握手成功。
  • 其他主机将会默许使用苹果提供的验证。

对话管理

顶层的 Alamofire 接口例如 Alamofire.request 使用了私下认可的
Alamofire.SessionManager
会话管理对象发起互联网请求。该会话管理对象默许使用了
URLSessionConfiguration 进行配置。

故此上边两段代码的是均等的:

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

let sessionManager = Alamofire.SessionManager.defaultsessionManager.request("https://httpbin.org/get")

你能够为使用创设会后台义务会话管理对象,一时半刻会话管理对象,同时也得以修改暗中同意的对话配置,比如暗中认可的请求头
(httpAdditionalHeaders) 或许请求超时时间
(timeoutIntervalForRequest)。

let configuration = URLSessionConfiguration.defaultlet sessionManager = Alamofire.SessionManager(configuration: configuration)

let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app.background")let sessionManager = Alamofire.SessionManager(configuration: configuration)

let configuration = URLSessionConfiguration.ephemerallet sessionManager = Alamofire.SessionManager(configuration: configuration)

var defaultHeaders = Alamofire.SessionManager.default.defaultHTTPHeadersdefaultHeaders["DNT"] = "1 (Do Not Track Enabled)"let configuration = URLSessionConfiguration.defaultconfiguration.httpAdditionalHeaders = defaultHeaderslet sessionManager = Alamofire.SessionManager(configuration: configuration)

引入使用那种方法修改 AuthorizationContent-Type
等请求头音讯。推荐使用 Alamofire.request 接口中的 headers 参数,
URLRequestConvertibleParameterEncoding 等办法修改请求头音信。

特性列表编码

PropertyListEncoding依据关系格式和写选项值,使用PropertyListSerialization来创设一个参数对象的品质列表体现,并视作请求体。编码请求的请求头的Content-Type伸手字段被安装为application/x-plist

服务器信任策略老董 (Server Trust Policy Manager)

ServerTrustPolicyManager肩负储存三个之中的服务器信任策略到特定主机的照射。那样Alamofire就足以评估种种主机分歧服务器信任策略。

let serverTrustPolicies: [String: ServerTrustPolicy] = [
    "test.example.com": .pinCertificates(
        certificates: ServerTrustPolicy.certificates(),
        validateCertificateChain: true,
        validateHost: true
    ),
    "insecure.expired-apis.com": .disableEvaluation
]

let sessionManager = SessionManager(
    serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)

注意:要保管有贰个强引用引用着SessionManager实例,否则当sessionManager被销毁时,请求将会撤除。

那几个服务器信任策略将会形成上面包车型大巴结果:

  • test.example.com:始终使用证书链固定的证书和启用主机验证,由此须求以下标准才能是TLS握手成功:
    • 证书链必须是实用的。
    • 证书链必须含蓄2个早已固化的申明。
    • Challenge主机必须同盟主机证书链的子证书。
  • insecure.expired-apis.com:将从不评估证书链,并且连接允许TLS握手成功。
  • 4858.com ,别的主机将会暗中同意使用苹果提供的印证。
子类化服务器信任策略经理

倘若大家须求一个越来越灵活的服务器信任策略来协作其余表现(例如通配符域名),能够子类化ServerTrustPolicyManager,并且重写serverTrustPolicyForHost方法。

class CustomServerTrustPolicyManager: ServerTrustPolicyManager {
    override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
        var policy: ServerTrustPolicy?

        // Implement your custom domain matching behavior...

        return policy
    }
}

对话代理

Alamofire 的会话管理对象私下认可创制了2个会话代理对象来处理
URLSession
发生的各个代理回调事件。这个代理方法完结的效果能够应付绝当先4捌%的利用情况并且为隐匿了复杂的内部调用为用户提供了简易的上层接口。然则,您仍有望会因为各个各类的供给而重载那些代理方法的落到实处。

先是种自定义 SessionDelegate
行为的方法是重载闭包。通过闭包您能够重载对应的 SessionDelegate
接口,并且别的接口的兑现将保持不变。那让贯彻贰个自定义的代办方法集合变得很不难。上面是部分可用的可重载的闭包:

/// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?/// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`.open var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?/// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`.open var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?/// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`.open var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?

下边包车型地铁例子通过重载 taskWillPerformHTTPRedirection
闭包来幸免请求重定向到apple.com的域名。

let sessionManager = Alamofire.SessionManager(configuration: URLSessionConfiguration.default)let delegate: Alamofire.SessionDelegate = sessionManager.delegatedelegate.taskWillPerformHTTPRedirection = { session, task, response, request in var finalRequest = request if let originalRequest = task.originalRequest, let urlString = originalRequest.url?.urlString, urlString.contains("apple.com") { finalRequest = originalRequest } return finalRequest}

另一种重载 SessionDelegate
默许完毕的章程是继续。通过持续您能够兑现完全的自定义或然仍旧接纳私下认可完结仅为接口制造1个代理。通过为接口创制代理,您能够在调用接口暗中认可实现的前后扩充日志音信,派发文告等作用。下边包车型客车例子继承了
SessionDelegate,并且当发生重定向时打印音讯日志。

class LoggingSessionDelegate: SessionDelegate { override func urlSession( _ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { print("URLSession will perform HTTP redirection to request: \") super.urlSession( session, task: task, willPerformHTTPRedirection: response, newRequest: request, completionHandler: completionHandler ) }}
自定义编码

只要提供的ParameterEncoding体系无法满足大家的渴求,能够创造自定义编码。上边演示怎么着火速自定义二个JSONStringArrayEncoding品类把JSON字符串数组编码到请求中。

struct JSONStringArrayEncoding: ParameterEncoding {
    private let array: [String]

    init(array: [String]) {
        self.array = array
    }

    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = urlRequest.urlRequest

        let data = try JSONSerialization.data(withJSONObject: array, options: [])

        if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
            urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        }

        urlRequest.httpBody = data

        return urlRequest
    }
}
子类化服务器信任策略老板

假定大家必要3个更加灵敏的服务器信任策略来合作其余行为(例如通配符域名),能够子类化ServerTrustPolicyManager,并且重写serverTrustPolicyForHost方法。

class CustomServerTrustPolicyManager: ServerTrustPolicyManager {
    override func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? {
        var policy: ServerTrustPolicy?

        // Implement your custom domain matching behavior...

        return policy
    }
}

证实主机

.performDefaultEvaluation.pinCertificates.pinPublicKeys这三个服务器信任策略都饱含三个validateHost参数。把那一个值设为true,服务器信任评估就会注解与challenge主机名字相配的在注解里面包车型大巴主机名字。倘诺他们不相称,验证退步。固然设置为false,仍旧会评估整个证书链,不过不会验证子证书的主机名字。

注意:建议在骨子里支付中,把validateHost设置为true

请求

request,download,upload,stream 等艺术的回到值 DataRequest,
DownloadRequest, UploadRequestStreamRequest 均是持续于
Request。所有的 Request
实例都以由自身的对话管理对象创造,并且不会直接开端化。

各样子类都有1部分破例的章程比如 authenticate, validate,
responseJSON
uploadProgress,这么些措施均重回调用者以便能够展开链式调用。

恳请能够被挂起,苏醒,打消:

  • suspend(): 挂起底层职责和派发队列。
  • resume(): 恢复职分和派发队列。借使会话管理对象未有设置
    startRequestsImmediatelytrue,那么请求须要调用 resume()
    才能开首。
  • cancel(): 废除职务,产生错误消息并将错误音讯传递到响应回调。
手动UKugaL请求参数编码

ParameterEncodingAPI能够在创制网络请求外界使用。

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

let parameters: Parameters = ["foo": "bar"]
let encodedURLRequest = try URLEncoding.queryString.encode(urlRequest, with: parameters)

证实主机

.performDefaultEvaluation.pinCertificates.pinPublicKeys那多个服务器信任策略都包含一个validateHost参数。把这一个值设为true,服务器信任评估就会评释与challenge主机名字相配的在评释里面包车型地铁主机名字。假如他们不相配,验证退步。要是设置为false,仍旧会评估整个证书链,不过不会验证子证书的主机名字。

注意:建议在实际开发中,把validateHost设置为true

证实证书链

Pinning certificate 和 public keys
都能够透过validateCertificateChain参数拥有验证证书链的选项。把它设置为true,除了对Pinning
certificate 和 public
keys举行字节相等检查外,还将会表明整个证书链。假设是false,将会跳过证件链验证,但还会开始展览字节相等检查。

还有不少情形会造成禁止使用证书链认证。最常用的章程就是自签署和过期的证书。在那些景况下,验证始终会失利。可是字节相等检查会保险大家从服务器收到到证书。

注意:建议在其实开发中,把validateCertificateChain设置为true

呼吁路由

趁着 App
变得复杂,使用通用情势创设你协调的网络栈就变得很是主要了。个中2个重大的宏图正是何等路由你的伸手。遵守
URLConvertibleURLRequestConvertible 协议的 Router
就变得要命实惠。

遵循 URLConvertible 协议的类能够用来布局 U科雷傲Ls,然后将 U君越Ls 用来组织UOdysseyL 请求。String, URL, 和 URLComponents 都服从了 URLConvertible
协议,那四个类的指标均能够看做 url 参数字传送递给 request, upload, 和
download 方法:

let urlString = "https://httpbin.org/post"Alamofire.request(urlString, method: .post)let url = URL(string: urlString)!Alamofire.request(url, method: .post)let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true)Alamofire.request(.post, URLComponents)

与 web 服务器交互时推荐通过落实 URLConvertible
协议来做域名型模型与服务器财富的投射。

HTTP请求头

能够一贯在央浼方法添加自定义HTTP请求头,那便于大家在呼吁中添加请求头。

let headers: HTTPHeaders = [
    "Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
    "Accept": "application/json"
]

Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
    debugPrint(response)
}

对于那个不变的恳求头,提出在URLSessionConfiguration安装,那样就可以自行被用来别的URLSession创建的URLSessionTask

默认的Alamofire
SessionManager为每一个请求提供了二个默许的请求头集合,包含:

  • Accept-Encoding,默认是gzip;q=1.0, compress;q=0.5
  • Accept-Language,默许是系统的前陆个偏好语言,格式类似于en;q=1.0
  • User-Agent,包括当前应用程序的版本音信。例如iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0

只要要自定义这一个请求头集合,我们必须创制三个自定义的URLSessionConfigurationdefaultHTTPHeaders性格将会被更新,并且自定义的对话配置也会使用到新的SessionManager实例。

声明证书链

Pinning certificate 和 public keys
都能够经过validateCertificateChain参数拥有验证证书链的选项。把它设置为true,除了对Pinning
certificate 和 public
keys进行字节相等检查外,还将会注脚整个证书链。要是是false,将会跳过表明链验证,但还会进展字节相等检查。

还有许多动静会招致禁止使用证书链认证。最常用的点子正是自签署和过期的证书。在那一个情状下,验证始终会退步。不过字节相等检查会保障我们从服务器收到到证书。

注意:提出在其实付出中,把validateCertificateChain设置为true

使用传输安全 (App Transport Security)

从iOS玖初叶,就添加了App Transport Security
(ATS),使用ServerTrustPolicyManager和多个ServerTrustPolicy指标只怕没什么影响。要是我们不住看到CFNetwork SSLHandshake failed (-9806)不当,我们大概遇见了这一个题材。苹果的ATS系统重写了整套challenge系统,除非大家在plist文件中配备ATS设置来允许行使评估服务器信任。

<dict>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>example.com</key>
            <dict>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSExceptionRequiresForwardSecrecy</key>
                <false/>
                <key>NSIncludesSubdomains</key>
                <true/>
                <!-- 可选的: 指定TLS的最小版本 -->
                <key>NSTemporaryExceptionMinimumTLSVersion</key>
                <string>TLSv1.2</string>
            </dict>
        </dict>
    </dict>
</dict>

是还是不是需求把NSExceptionRequiresForwardSecrecy设置为NO在于TLS连接是或不是利用三个同意的密码套件。在好几情形下,它需求安装为NONSExceptionAllowsInsecureHTTPLoads总得安装为YES,然后SessionDelegate才能接收到challenge回调。壹旦challenge回调被调用,ServerTrustPolicyManager将接管服务器信任评估。假若我们要接二连三到一个仅帮忙小于1.2本子的TSL主机,那么还要钦点NSTemporaryExceptionMinimumTLSVersion

注意:在其实支出中,建议始终使用有效的证件。

体系安全路由
extension User: URLConvertible { static let baseURLString = "https://example.com" func asURL() throws -> URL { let urlString = User.baseURLString + "/users/\/" return try urlString.asURL() }}

let user = User(username: "mattt")Alamofire.request // https://example.com/users/mattt

实现了 URLRequestConvertible 协议的档次能够用来协会 ULacrosseL
请求。URLRequest 私下认可实现了 URLRequestConvertible 协议,这使得
URLRequest 可一向传送给
request,upload,download等办法(推荐应用那种措施完毕自定义 HTTP
body)

let url = URL(string: "https://httpbin.org/post")!var urlRequest = URLRequesturlRequest.httpMethod = "POST"let parameters = ["foo": "bar"]do { urlRequest.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])} catch { // No-op}urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")Alamofire.request(urlRequest)

与 web 服务器交互时推荐通过兑现 URLRequestConvertible
协议以保险请求端点的1致性。那种措施能够用于抽象出服务器端区别并提供品类安全路由,以及管理认证凭证和此外情形

认证

表达是采用系统框架URLCredentialURLAuthenticationChallenge实现的。

接纳传输安全 (App Transport Security)

从iOS九从头,就添加了App Transport Security
(ATS),使用ServerTrustPolicyManager和多个ServerTrustPolicy目的大概没什么影响。借使大家不停看到CFNetwork SSLHandshake failed (-9806)不当,大家大概遇见了那么些题材。苹果的ATS系统重写了整套challenge系统,除非大家在plist文件中配备ATS设置来允许行使评估服务器信任。

<dict>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>example.com</key>
            <dict>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSExceptionRequiresForwardSecrecy</key>
                <false/>
                <key>NSIncludesSubdomains</key>
                <true/>
                <!-- 可选的: 指定TLS的最小版本 -->
                <key>NSTemporaryExceptionMinimumTLSVersion</key>
                <string>TLSv1.2</string>
            </dict>
        </dict>
    </dict>
</dict>

是或不是须求把NSExceptionRequiresForwardSecrecy设置为NO在于TLS连接是或不是选取一个允许的密码套件。在有个别情状下,它须求安装为NONSExceptionAllowsInsecureHTTPLoads必须安装为YES,然后SessionDelegate才能收到到challenge回调。一旦challenge回调被调用,ServerTrustPolicyManager将接管服务器信任评估。假使大家要一连到1个仅支持小于1.2本子的TSL主机,那么还要钦定NSTemporaryExceptionMinimumTLSVersion

注意:在实际支付中,提出始终使用有效的证书。

互连网可达性 (Network Reachability)

NetworkReachabilityManager监听WWANWiFi网络接口和主机地址的可达性变化。

let manager = NetworkReachabilityManager(host: "www.apple.com")

manager?.listener = { status in
    print("Network Status Changed: \(status)")
}

manager?.startListening()

注意:要保管manager被强引用,不然会接受不到状态变化。别的,在主机字符串中毫无包蕴scheme,也正是说要把https://去掉,不然不能监听。

当使用互联网可达性来决定接下去要做怎样时,有以下几点供给珍视注意的:

  • 不要行使Reachability来控制是或不是发送三个网络请求。
    • 我们必须要发送请求。
  • 当Reachability苏醒了,要重试网络请求。
    • 正是网络请求失利,在这一年也分外适合重试请求。
  • 互联网可达性的情事格外适合用来决定为何网络请求会破产。
    • 万1三个伸手失利,应该告诉用户是离线导致请求失败的,而不是技巧错误,例如请求超时。

有趣味的能够看看WWDC 2012 Session 706, “Networking Best
Practices”。

API 抽象参数
enum Router: URLRequestConvertible { case search(query: String, page: Int) static let baseURLString = "https://example.com" static let perPage = 50 // MARK: URLRequestConvertible func asURLRequest() throws -> URLRequest { let result: (path: String, parameters: Parameters) = { switch self { case let .search(query, page) where page > 0: return ("/search", ["q": query, "offset": Router.perPage * page]) case let .search: return ("/search", ["q": query]) } }() let url = try Router.baseURLString.asURL() let urlRequest = URLRequest(url: url.appendingPathComponent(result.path)) return try URLEncoding.default.encode(urlRequest, with: result.parameters) }}

Alamofire.request(Router.search(query: "foo bar", page: 1)) // https://example.com/search?q=foo%20bar&offset=50
援助的评释方案
  • HTTP Basic
  • HTTP Digest
  • Kerberos
  • NTLM

网络可达性 (Network Reachability)

NetworkReachabilityManager监听WWANWiFi互联网接口和主机地址的可达性别变化化。

let manager = NetworkReachabilityManager(host: "www.apple.com")

manager?.listener = { status in
    print("Network Status Changed: \(status)")
}

manager?.startListening()

注意:要保管manager被强引用,不然会收取不到状态变化。其余,在主机字符串中不要包蕴scheme,也正是说要把https://去掉,不然不能监听。

当使用网络可达性来决定接下去要做什么时,有以下几点供给重点注意的:

  • 不要运用Reachability来决定是或不是发送四个互连网请求。
    • 我们必须要发送请求。
  • 当Reachability苏醒了,要重试网络请求。
    • 哪怕网络请求战败,在这一年也分外适合重试请求。
  • 互连网可达性的场馆十一分适合用来支配为何互连网请求会退步。
    • 一经二个呼吁战败,应该告诉用户是离线导致请求失利的,而不是技巧错误,例如请求超时。

有趣味的可以看看WWDC 2012 Session 706, “Networking Best
Practices”。

FAQ

CRUD & Authorization
import Alamofireenum Router: URLRequestConvertible { case createUser(parameters: Parameters) case readUser(username: String) case updateUser(username: String, parameters: Parameters) case destroyUser(username: String) static let baseURLString = "https://example.com" var method: HTTPMethod { switch self { case .createUser: return .post case .readUser: return .get case .updateUser: return .put case .destroyUser: return .delete } } var path: String { switch self { case .createUser: return "/users" case .readUser(let username): return "/users/\" case .updateUser(let username, _): return "/users/\" case .destroyUser(let username): return "/users/\" } } // MARK: URLRequestConvertible func asURLRequest() throws -> URLRequest { let url = try Router.baseURLString.asURL() var urlRequest = URLRequest(url: url.appendingPathComponent urlRequest.httpMethod = method.rawValue switch self { case .createUser(let parameters): urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters) case .updateUser(_, let parameters): urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters) default: break } return urlRequest }}

Alamofire.request(Router.readUser // GET https://example.com/users/mattt
HTTP Basic认证

在伏贴的时候,在二个请求的authenticate方法会自动提供二个URLCredentialURLAuthenticationChallenge

let user = "user"
let password = "password"

Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
    .authenticate(user: user, password: password)
    .responseJSON { response in
        debugPrint(response)
}

基于服务器达成,Authorization header也说不定是相符的:

let user = "user"
let password = "password"

var headers: HTTPHeaders = [:]

if let authorizationHeader = Request.authorizationHeader(user: user, password: password) {
    headers[authorizationHeader.key] = authorizationHeader.value
}

Alamofire.request("https://httpbin.org/basic-auth/user/password", headers: headers)
    .responseJSON { response in
        debugPrint(response)
}

FAQ

Alamofire的起源是如何?

Alamofire是根据 Alamo Fire
flower
命名的,是壹种矢车菊的交集变种,内布Russ加的州花。

Adapting and Retrying Requests

于今的诸多 web 服务都足以通过授权系统开始展览走访。当中最常用的是
OAuth。OAuth 会生成三个拜访令牌来授权你的施用访问权限内的 web
服务。创立令牌恐怕会很麻烦,令牌过期需求思量很十二线程安全的标题,那会让景况变得更扑朔迷离。

RequestAdapterRequestRetrier
协议让创设线程安全的授权系统变得不难。

RequestAdapter 协议允许 SessionManager 在创建 Request 前为
Request
做额外的反省和适配工作。相比常用的选择场景是为呼吁拼接授权参数。

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")

RequestRetrier 协议允许网络请求产生错误时再次发起呼吁。通过并且落到实处
RequestAdapterRequestRetrier 协议,您能够为
OAuth一,OAuth二,基本授权,重试策略创设四个证书刷新系统。您能促成的机能不囿于于此。上边包车型大巴例证体现了
OAuth二 令牌的基础代谢流程。

豁免义务注明:不是1个大局的 OAuth2
消除方案。上边包车型地铁代码仅作为不难示例显示了如何通过 RequestAdapter
RequestRetrier 协议来落到实处线程安全的基础代谢系统。

重申,毫无拷贝上边包车型大巴以身作则代码到您的成品中。该代码片段仅能看做示范。每3个授权系统应该遵照平台和授权类型做相应的修改。

class OAuth2Handler: RequestAdapter, RequestRetrier { private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?, _ refreshToken: String?) -> Void private let sessionManager: SessionManager = { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders return SessionManager(configuration: configuration) }() private let lock = NSLock() private var clientID: String private var baseURLString: String private var accessToken: String private var refreshToken: String private var isRefreshing = false private var requestsToRetry: [RequestRetryCompletion] = [] // MARK: - Initialization public init(clientID: String, baseURLString: String, accessToken: String, refreshToken: String) { self.clientID = clientID self.baseURLString = baseURLString self.accessToken = accessToken self.refreshToken = refreshToken } // MARK: - RequestAdapter func adapt(_ urlRequest: URLRequest) throws -> URLRequest { if let url = urlRequest.url, url.urlString.hasPrefix(baseURLString) { var urlRequest = urlRequest urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization") return urlRequest } return urlRequest } // MARK: - RequestRetrier func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) { lock.lock() ; defer { lock.unlock() } if let response = request.task.response as? HTTPURLResponse, response.statusCode == 401 { requestsToRetry.append(completion) if !isRefreshing { refreshTokens { [weak self] succeeded, accessToken, refreshToken in guard let strongSelf = self else { return } strongSelf.lock.lock() ; defer { strongSelf.lock.unlock() } if let accessToken = accessToken, let refreshToken = refreshToken { strongSelf.accessToken = accessToken strongSelf.refreshToken = refreshToken } strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) } strongSelf.requestsToRetry.removeAll() } } } else { completion(false, 0.0) } } // MARK: - Private - Refresh Tokens private func refreshTokens(completion: @escaping RefreshCompletion) { guard !isRefreshing else { return } isRefreshing = true let urlString = "\(baseURLString)/oauth2/token" let parameters: [String: Any] = [ "access_token": accessToken, "refresh_token": refreshToken, "client_id": clientID, "grant_type": "refresh_token" ] sessionManager.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default) .responseJSON { [weak self] response in guard let strongSelf = self else { return } if let json = response.result.value as? [String: Any], let accessToken = json["access_token"] as? String, let refreshToken = json["refresh_token"] as? String { completion(true, accessToken, refreshToken) } else { completion(false, nil, nil) } strongSelf.isRefreshing = false } }}

let baseURLString = "https://some.domain-behind-oauth2.com"let oauthHandler = OAuth2Handler( clientID: "12345678", baseURLString: baseURLString, accessToken: "abcd1234", refreshToken: "ef56789a")let sessionManager = SessionManager()sessionManager.adapter = oauthHandlersessionManager.retrier = oauthHandlerlet urlString = "\(baseURLString)/some/endpoint"sessionManager.request(urlString).validate().responseJSON { response in debugPrint}

SessionManageradapterretrier 棉被服装置为
OAuth2Handler后,当令牌失效时,便会自动刷新令牌并尝试按战败的1①重新发起呼吁。

若是您想按创制互连网请求的逐1重新发起呼吁,您能够通过网络请求职务的 id
实行排序。

该示例仅检查了响应的 401
状态码,作为检查测试失效令牌的事例那已经丰富。O在骨子里产品中,您应该还要检查评定响应头中的
reamlwww-authenticate 等字段。

还亟需专注的是该授权系统能够在多少个会话管理对象间共享。比如,您能够为同一个web 服务集同时利用 defaultephemeral 会话配置。上边的例证允许
oauthHandler 实例对象在多个会话管理对象间共享并管制分别的刷新流程。

使用URLCredential认证
let user = "user"
let password = "password"

let credential = URLCredential(user: user, password: password, persistence: .forSession)

Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
    .authenticate(usingCredential: credential)
    .responseJSON { response in
        debugPrint(response)
}

注意:使用URLCredential来做表明,假如服务器发出四个challenge,底层的URLSession实质上最后会发五次呼吁。第三遍呼吁不会含有credential,因为大概会接触服务器发出3个challenge。这一个challenge会被Alamofire接收,credential会被加上,并且URLSessin会再次取得请求。

Alamofire的根源是如何?

Alamofire是根据 Alamo Fire
flower
命名的,是1种矢车菊的混杂变种,阿肯色的州花。

Router和Request Adapter的逻辑是什么?

大约和静态的数目,例如paths、parameters和1道的headers放在Router。动态的数量,例如1个Authorization
header,它的值会随着叁个验证种类生成,放在RequestAdapter

动态的数码必须放在ReqeustAdapter的缘由是要扶助重试操作。当重试3个伸手时,原来的呼吁不会重新树立,也就意味着Router不会再重复调用。RequestAdapter能够重新调用,那能够让大家在重试请求以前更新原始请求的动态数据。


自定义响应连串化器

千古在达成自定义响应种类化器或对象连串化方法时首要挂念的是错误音信的拍卖。那里有五个可选择:对错误消息不做别的处理直接向下传递,由用户在响应回调解和处理拍卖;只怕为你的利用定义二个带有全数错误类型的
Error 枚举类。

下面的 BackendError 枚举类在前边的事例中也会冒出:

enum BackendError: Error { case network(error: Error) // Capture any underlying Error from the URLSession API case dataSerialization(error: Error) case jsonSerialization(error: Error) case xmlSerialization(error: Error) case objectSerialization(reason: String)}

Alamofire 为 strings,JSON,property lsits
提供了内置的响应连串化器,您也能够为 Alamofire.DataRequest
Alamofire.DownloadRequest 实行增加。

上边包车型大巴例证体现了响应种类化器使用 Ono 的落实方式:

extension DataRequest { static func xmlResponseSerializer() -> DataResponseSerializer<ONOXMLDocument> { return DataResponseSerializer { request, response, data, error in // Pass through any underlying URLSession error to the .network case. guard error == nil else { return .failure(BackendError.network(error: error!)) } // Use Alamofire's existing data serializer to extract the data, passing the error as nil, as it has // already been handled. let result = Request.serializeResponseData(response: response, data: data, error: nil) guard case let .success(validData) = result else { return .failure(BackendError.dataSerialization(error: result.error! as! AFError)) } do { let xml = try ONOXMLDocument(data: validData) return .success } catch { return .failure(BackendError.xmlSerialization(error: error)) } } } @discardableResult func responseXMLDocument( queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse<ONOXMLDocument>) -> Void) -> Self { return response( queue: queue, responseSerializer: DataRequest.xmlResponseSerializer(), completionHandler: completionHandler ) }}

通用体系化可以实行自动,类型安全的指标系列化。

protocol ResponseObjectSerializable { init?(response: HTTPURLResponse, representation: Any)}extension DataRequest { func responseObject<T: ResponseObjectSerializable>( queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse<T>) -> Void) -> Self { let responseSerializer = DataResponseSerializer<T> { request, response, data, error in guard error == nil else { return .failure(BackendError.network(error: error!)) } let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) let result = jsonResponseSerializer.serializeResponse(request, response, data, nil) guard case let .success(jsonObject) = result else { return .failure(BackendError.jsonSerialization(error: result.error!)) } guard let response = response, let responseObject = T(response: response, representation: jsonObject) else { return .failure(BackendError.objectSerialization(reason: "JSON could not be serialized: \(jsonObject)")) } return .success(responseObject) } return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler) }}

struct User: ResponseObjectSerializable, CustomStringConvertible { let username: String let name: String var description: String { return "User: { username: \, name: \ }" } init?(response: HTTPURLResponse, representation: Any) { guard let username = response.url?.lastPathComponent, let representation = representation as? [String: Any], let name = representation["name"] as? String else { return nil } self.username = username self.name = name }}

Alamofire.request("https://example.com/users/mattt").responseObject { (response: DataResponse<User>) in debugPrint if let user = response.result.value { print("User: { username: \(user.username), name: \(user.name) }") }}

平等的艺术也能够用来拍卖终端再次来到的靶子集合:

protocol ResponseCollectionSerializable { static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self]}extension ResponseCollectionSerializable where Self: ResponseObjectSerializable { static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] { var collection: [Self] = [] if let representation = representation as? [[String: Any]] { for itemRepresentation in representation { if let item = Self(response: response, representation: itemRepresentation) { collection.append } } } return collection }}

extension DataRequest { @discardableResult func responseCollection<T: ResponseCollectionSerializable>( queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse<[T]>) -> Void) -> Self { let responseSerializer = DataResponseSerializer<[T]> { request, response, data, error in guard error == nil else { return .failure(BackendError.network(error: error!)) } let jsonSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) let result = jsonSerializer.serializeResponse(request, response, data, nil) guard case let .success(jsonObject) = result else { return .failure(BackendError.jsonSerialization(error: result.error!)) } guard let response = response else { let reason = "Response collection could not be serialized due to nil response." return .failure(BackendError.objectSerialization(reason: reason)) } return .success(T.collection(from: response, withRepresentation: jsonObject)) } return response(responseSerializer: responseSerializer, completionHandler: completionHandler) }}

struct User: ResponseObjectSerializable, ResponseCollectionSerializable, CustomStringConvertible { let username: String let name: String var description: String { return "User: { username: \, name: \ }" } init?(response: HTTPURLResponse, representation: Any) { guard let username = response.url?.lastPathComponent, let representation = representation as? [String: Any], let name = representation["name"] as? String else { return nil } self.username = username self.name = name }}

Alamofire.request("https://example.com/users").responseCollection { (response: DataResponse<[User]>) in debugPrint if let users = response.result.value { users.forEach { print") } }}

将数据下载到文件

Alamofire能够把服务器的多少下载到内部存款和储蓄器(in-memory)或许硬盘(on-disk)中。全部Alamofire.requestAPI下载的数据都以储存在内部存款和储蓄器中。那正如相符小文件,更神速;但是不相符大文件,因为大文件会把内部存款和储蓄器耗尽。大家要使用Alamofire.downloadAPI把服务器的数目下载到硬盘中。

下边这些法子只适用于macOS。因为在此外平台不容许在运用沙盒外访问文件系统。下边会讲到怎样在此外平台下载文件。

Alamofire.download("https://httpbin.org/image/png").responseData { response in
    if let data = response.result.value {
        let image = UIImage(data: data)
    }
}

Router和Request Adapter的逻辑是怎么着?

简言之和静态的数目,例如paths、parameters和联合的headers放在Router。动态的多少,例如二个Authorization
header,它的值会随着一个证实系统变化,放在RequestAdapter

动态的数码必须放在ReqeustAdapter的因由是要帮助重试操作。当重试一个呼吁时,原来的呼吁不会再一次树立,也就代表Router不会再重复调用。RequestAdapter能够另行调用,那足以让我们在重试请求在此之前更新原始请求的动态数据。


安全性

在与 web 服务器交互传输敏感数据时应该采用安全的 HTTPS
连接。默许景况下,Alamofire 会使用苹果提供的 Security
框架对服务器提供的证书串实行求证。这样只是能确认保证服务器端证书是或不是可行,并不能够预防中间人抨击
man-in-the-middle
或其余潜在的漏洞。为了下落遇到中间人抨击的大概,应用在拍卖敏感用户的数据或金融音讯时应该合营使用证书或
ServerTrustPolicy 提供的公钥锁定

下载文件指标

笔者们得以提供二个DownloadFileDestination闭包把一时文件夹的文书移动到四个对象文件夹。在一时半刻文件真正移动到destinationURL事先,闭包内部钦点的DownloadOptions将会被执行。最近支撑的DownloadOptions有上面八个:

  • .createIntermediateDirectories:假诺钦点了指标UCRUISERL,将会创制中间目录。
  • .removePreviousFile:假设钦定了目的U猎豹CS陆L,将会移除此前的文书

let destination: DownloadRequest.DownloadFileDestination = { _, _ in
    let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let fileURL = documentsURL.appendPathComponent("pig.png")

    return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}

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

    if response.error == nil, let imagePath = response.destinationURL?.path {
        let image = UIImage(contentsOfFile: imagePath)
    }
}

也能够直接使用提议的下载目的API:

let destination = DownloadRequest.suggestedDownloadDestination(directory: .documentDirectory)
Alamofire.download("https://httpbin.org/image/png", to: destination)

作者:Lebron_James
链接:
來源:简书
简书小说权归作者全数,任何方式的转发都请联系小编得到授权并评释出处。

继承 Server Trust Policy Manager
下载速度

所有的DownloadRequest都能够应用downloadProgressAPI来反映下载进度。

Alamofire.download("https://httpbin.org/image/png")
    .downloadProgress { progress in
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseData { response in
        if let data = response.result.value {
            let image = UIImage(data: data)
    }
}

downloadProgressAPI还足以承受三个queue参数来钦赐下载速度闭包在哪个DispatchQueue中执行。

let utilityQueue = DispatchQueue.global(qos: .utility)

Alamofire.download("https://httpbin.org/image/png")
    .downloadProgress(queue: utilityQueue) { progress in
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseData { response in
        if let data = response.result.value {
            let image = UIImage(data: data)
    }
}
证实证书串
<dict> <key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>example.com</key> <dict> <key>NSExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSExceptionRequiresForwardSecrecy</key> <false/> <key>NSIncludesSubdomains</key> <true/> <!-- Optional: Specify minimum TLS version --> <key>NSTemporaryExceptionMinimumTLSVersion</key> <string>TLSv1.2</string> </dict> </dict> </dict></dict>
回复下载

只要贰个DownloadRequest被收回或刹车,底层的U奥迪Q5L会话会生成三个上涨数据。恢复生机数据足以被重复行使并在暂停的职位接二连三下载。复苏数据足以因而下载响应访问,然后在再次起头请求的时候被利用。

重要:在新型的iOS 十, macOS 10.1二, tvOS 10, watchOS
三中,resumeData会被后台UBMWX5L会话配置破坏。因为在resumeData的转变逻辑有三个底层的bug,无法恢复生机下载。具体情状能够到Stack
Overflow探望。方今的新星景况是:在iOS
10.第22中学一度修复了那个bug。

class ImageRequestor {
    private var resumeData: Data?
    private var image: UIImage?

    func fetchImage(completion: (UIImage?) -> Void) {
        guard image == nil else { completion(image) ; return }

        let destination: DownloadRequest.DownloadFileDestination = { _, _ in
            let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let fileURL = documentsURL.appendPathComponent("pig.png")

            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
        }

        let request: DownloadRequest

        if let resumeData = resumeData {
            request = Alamofire.download(resumingWith: resumeData)
        } else {
            request = Alamofire.download("https://httpbin.org/image/png")
        }

        request.responseData { response in
            switch response.result {
            case .success(let data):
                self.image = UIImage(data: data)
            case .failure:
                self.resumeData = response.resumeData
            }
        }
    }
}

互连网可用性

NetworkReachabilityManager 可用于监听 WWAN 和 WiFi 网络到钦命主机或 IP
地址的两次三番景况。

let manager = NetworkReachabilityManager(host: "www.apple.com")manager?.listener = { status in print("Network Status Changed: \}manager?.startListening()

请保管对 网络状态监听对象 有强引用,不然不会监听到别的网络状态。

在监听互连网状态时索要留意以下几点:

  • 不要据他们说互联网状态来控制是还是不是发送网络请求。
    • 只管出殡就行
  • 当互连网复苏连接,对破产的互联网请求重新发起呼吁。
    • 即使重新发起呼吁仍有相当的大也许破产,但您仍应该尝试。
  • 互连网状态推进分析出请求失败原因。
    • 借使互联网请求失利,提醒用户网络处于离线状态要比更现实的错误消息比如”请求超时”等更和谐。

越多消息请参见 WWDC 二〇一一 Session 70陆, “Networking Best Practices” for
more info.

The following radars have some effect on the current implementation of
Alamofire.

  • rdar://21349340 – Compiler throwing warning due to toll-free
    bridging issue in test case
  • rdar://26761490 – Swift string interpolation causing memory leak
    with common usage
  • rdar://26870455 – Background URL Session Configurations do not
    work in the simulator
  • rdar://26849668 – Some URLProtocol APIs do not properly handle
    URLRequest

上传数据到服务器

利用JOSN恐怕U奥迪Q7L编码参数上传1些小数码到服务器,使用Alamofire.request
API就曾经够用了。要是急需发送非常大的数目,供给使用Alamofire.upload
API。当大家必要在后台上传数据时,也得以运用Alamofire.upload

Alamofire 名字由来

Alamofire 花,矢车菊的一种,是俄勒冈州的官方州花。

上传数据
let imageData = UIPNGRepresentation(image)!

Alamofire.upload(imageData, to: "https://httpbin.org/post").responseJSON { response in
    debugPrint(response)
}

呼吁路由和呼吁适配器的界别

资源路径,请求参数,公共请求头这几个静态数据属于 路由 范畴。认证
头那类会随着认证系统发生变化的动态数据属于 请求适配器 范畴。

Alamofire 由 Alamofire 软件基金会 全部并爱抚。您能够由此关切大家的
Facebook 官方账号 @AlamofireSF 来获取最新的翻新公布信息。

Alamofire 在 MIT 开源协议下公布。越多音信请查看 LICENSE 文件。

欢迎关切本人的简书,小编会定期做一些技巧分享:)

上传文件
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

Alamofire.upload(fileURL, to: "https://httpbin.org/post").responseJSON { response in
    debugPrint(response)
}
上传多部分表单数据
Alamofire.upload(
    multipartFormData: { multipartFormData in
        multipartFormData.append(unicornImageURL, withName: "unicorn")
        multipartFormData.append(rainbowImageURL, withName: "rainbow")
    },
    to: "https://httpbin.org/post",
    encodingCompletion: { encodingResult in
        switch encodingResult {
        case .success(let upload, _, _):
            upload.responseJSON { response in
                debugPrint(response)
            }
        case .failure(let encodingError):
            print(encodingError)
        }
    }
)
上传进程

所有的UploadRequest都得以动用uploadProgressdownloadProgress
APIs来报告上传和下载速度。

let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

Alamofire.upload(fileURL, to: "https://httpbin.org/post")
    .uploadProgress { progress in // 默认在主线程中执行
        print("Upload Progress: \(progress.fractionCompleted)")
    }
    .downloadProgress { progress in // 默认在主线程中执行
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseJSON { response in
        debugPrint(response)
}

总括指标

时间表

Alamofire在1个呼吁周期内募集时间,并创设三个Tineline对象,它是响应类型的八个天性。

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    print(response.timeline)
}

上面的Timeline消息包含:

  • Latency: 0.428 seconds (延迟)
  • Request Duration: 0.42捌 seconds (请求时间)
  • Serialization Duration: 0.00一 seconds (系列化时间)
  • Total Duration: 0.429 seconds (总时间)
U瑞虎L会话职务指标

在iOS和tvOS 拾和macOS 拾.1第22中学,苹果发表了新的URLSessionTaskMetrics
APIs。这些职分目标封装了有关请求和响应实施的神奇计算消息。那么些API和Timeline可怜相像,不过提供了很多Alamofire未有提供的计算音讯。那一个目标可以通过其余响应去拜访。

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    print(response.metrics)
}

注意:这个API只可以在iOS和tvOS 10和macOS
拾.1第22中学应用。所以,根据配置指标,恐怕必要进入版本判断:

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    if #available(iOS 10.0. *) {
        print(response.metrics)
    }
}

cUHummerH贰L命令输出

调节平台难点很令人切齿痛恨。庆幸的是,Alamofire的Request对象遵循了CustomStringConvertibleCustomDebugStringConvertible说道来提供部分要命管用的调节工具。

CustomStringConvertible
let request = Alamofire.request("https://httpbin.org/ip")

print(request)
// GET https://httpbin.org/ip (200)
CustomDebugStringConvertible
let request = Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"])
debugPrint(request)

输出:

$ curl -i \
    -H "User-Agent: Alamofire/4.0.0" \
    -H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" \
    -H "Accept-Language: en;q=1.0,fr;q=0.9,de;q=0.8,zh-Hans;q=0.7,zh-Hant;q=0.6,ja;q=0.5" \
    "https://httpbin.org/get?foo=bar"

先是部分完。

第三篇小说 >>

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有