Alamofire框架的使用一 —— 基本用法

【iOS开发】Alamofire框架的使用一 —— 基本用法

96 Lebron_James 关注

2017.03.02 07:23* 字数 3676 阅读 21989评论 17喜欢 58

最近发现在百度搜索Alamofire的使用,这篇文章排在第一的位置,瞬间觉得非常有必要及时更新下。如果发现有错误,请及时指出。谢谢!

最近更新日期:2018年4月17日

另外本人的新文章已经不在简书更新,而是转移到个人博客网站:曾文志 - iOS开发者,专注Swift。 有兴趣的可以在网站最底部通过电子邮件订阅我的博客。谢谢!

Alamofire框架的使用一 —— 基本用法

对于使用Objective-C的开发者,一定非常熟悉AFNetworking这个网络框架。在苹果推出的Swift之后,AFNetworking的作者专门用Swift来编写一个类似AFNetworking的网络框架,称为AlamofireAlamofire地址 >>

我分两篇文章介绍如何使用Alamofire框架。文章的内容主要是翻译Alamofire的readme。第二篇文章 >>

功能

  • 链式请求/响应方法
  • URL / JSON / plist参数编码
  • 上传文件/数据/流/多表单数据
  • 使用请求或者断点下载来下载文件
  • 使用URL凭据进行身份认证
  • HTTP响应验证
  • 包含进度的上传和下载闭包
  • cURL命令的输出
  • 动态适配和重试请求
  • TLS证书和Public Key Pinning
  • 网络可达性
  • 全面的单元和集成测试覆盖率

组件库

为了让Alamofire专注于核心网络的实现,Alamofire生态系统还有另外两个库:

  • AlamofireImage:一个图片库,包括图像响应序列化器、UIImageUIImageView的扩展、自定义图像滤镜、内存中自动清除和基于优先级的图像下载系统。
  • AlamofireNetworkActivityIndicator:控制iOS应用的网络活动指示器。包含可配置的延迟计时器来帮助减少闪光,并且支持不受Alamofire管理的URLSession实例。

要求的使用环境

  • iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 8.3+
  • Swift 3.1+

安装方法

CocoaPods

 

iOS版本和Alamofire版本可以自己根据实际情况自行更改。CocoaPods是比较常用的第三方库管理工具,其他方法就不详细说了。

如何使用

发请求

 

响应处理

直接在请求后面用点语法链接响应处理:

 

在上面的例子中,responseJSON handler直接拼接到请求后面,当请求完成后被调用。这个闭包一旦收到响应后,就会处理这个响应,并不会因为等待服务器的响应而造成阻塞执行。请求的结果仅在响应闭包的范围内可用。其他任何与服务器返回的响应或者数据相关的操作,都必须在这个闭包内执行。

Alamofire默认情况下包含五种不同的响应handler:

 

所有的响应handler都不会对响应进行验证。也就是说响应状态码在400..<500500..<600范围内,都不会触发错误。

响应 Handler

response handler不处理任何响应数据。它仅仅是从URL session delegate中转发信息。

 

一般情况下不建议使用这种没有响应序列化器的handler,而应该使用下面有特定序列化器的handler。

响应数据 Handler

responseData handler使用responseDataSerializer(这个对象把服务器的数据序列化成其他类型)来提取服务器返回的数据。如果没有返回错误并且有数据返回,那么响应Result将会是.successvalueData类型。

 

响应字符串 Handler

responseString handler使用responseStringSerializer对象根据指定的编码格式把服务器返回的数据转换成String。如果没有返回错误并且服务器的数据成功地转换为String,那么响应Result将会是.successvalueString类型。

 

如果没有指定编码格式,将会使用服务器的HTTPURLResponse指定的格式。如果服务器无法确定编码格式,那么默认使用.isoLatin1

响应 JSON Handler

responseJSON handler使用responseJSONSerializer根据指定的JSONSerialization.ReadingOptions把服务器返回的数据转换成Any类型。如果没有返回错误并且服务器的数据成功地转换为JSON对象,那么响应Result将会是.successvalueAny类型。

 

所有JSON的序列化,都是使用JSONSerialization完成的。

链式响应handler

响应handler可以链接在一起:

 

注意:在同一个请求中使用多个响应handler,要求服务器的数据会被序列化多次,每次对应一个handler。

响应handler队列

默认情况下,响应handler是在主队列执行的。但是我们也可以自定义队列:

 

响应验证

默认情况下,Alamofire把所有完成的请求当做是成功的请求,无论响应的内容是什么。如果响应有一个不能被接受的状态码或者MIME类型,在响应handler之前调用validate将会产生错误。

手动验证

 

自动验证

自动验证在200…299范围内的状态码;如果请求头中有指定Accept,那么也会验证响应头的与请求头Accept一样的Content-Type

 

响应缓存

响应缓存是使用系统的框架URLCache来处理的。它提供了内存和磁盘上的缓存,并允许我们控制内存和磁盘的大小。

默认情况下,Alamofire利用共享的URLCache

HTTP方法

HTTPMethod列举了下面的这些方法:

 

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

 

参数编码

Alamofire支持三种参数编码:URLJSONPropertyList。还支持遵循了ParameterEncoding协议的自定义编码。

URL编码

URLEncoding类型创建了一个URL编码的查询字符串来设置或者添加到一个现有的URL查询字符串,或者设置URL请求的请求体。查询字符串是否被设置或者添加到现有的URL查询字符串,或者被作为HTTP请求体,决定于编码的Destination。编码的Destination有三个case:

  • .methodDependent:为GETHEADDELETE请求使用编码查询字符串来设置或者添加到现有查询字符串,并且使用其他HTTP方法来设置请求体。
  • .queryString:设置或者添加编码查询字符串到现有查询字符串
  • .httpBody:把编码查询字符串作为URL请求的请求体

一个编码请求的请求体的Content-Type字段被设置为application/x-www-form-urlencoded; charset=utf-8。因为没有公开的标准说明如何编码集合类型,所以按照惯例在key后面添加[]来表示数组的值(foo[]=1&foo[]=2),在key外面包一个中括号来表示字典的值(foo[bar]=baz)。

使用URL编码参数的GET请求

 

使用URL编码参数的POST请求

 

设置Bool类型参数的编码

URLEncoding.BoolEncoding提供了两种编码方式:

  • .numeric:把true编码为1false编码为0
  • .literal:把true编码为truefalse编码为false

默认情况下:Alamofire使用.numeric

可以使用下面的初始化函数来创建URLEncoding,指定Bool编码的类型:

 

设置Array类型参数编码

URLEncoding.ArrayEncoding提供了两种编码方式:

  • .brackets: 在每个元素值的key后面加上一个[],如foo=[1,2]编码成foo[]=1&foo[]=2
  • .noBrackets:不添加[],例如foo=[1,2]编码成``foo=1&foo=2`

默认情况下,Alamofire使用.brackets

可以使用下面的初始化函数来创建URLEncoding,指定Array编码的类型:

 

JSON编码

JSONEncoding类型创建了一个JOSN对象,并作为请求体。编码请求的请求头的Content-Type请求字段被设置为application/json

使用JSON编码参数的POST请求

 

属性列表编码

PropertyListEncoding根据关联格式和写选项值,使用PropertyListSerialization来创建一个属性列表对象,并作为请求体。编码请求的请求头的Content-Type请求字段被设置为application/x-plist

自定义编码

如果提供的ParameterEncoding类型不能满足我们的要求,可以创建自定义编码。下面演示如何快速自定义一个JSONStringArrayEncoding类型把JSON字符串数组编码到请求中。

 

手动URL请求参数编码

ParameterEncodingAPI可以在创建网络请求外面使用。

 

HTTP请求头

可以直接在请求方法添加自定义HTTP请求头,这有利于我们在请求中添加请求头。

 

对于那些不变的请求头,建议在URLSessionConfiguration设置,这样就可以自动被用于任何URLSession创建的URLSessionTask

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

  • Accept-Encoding,默认是gzip;q=1.0, compress;q=0.5
  • Accept-Language,默认是系统的前6个偏好语言,格式类似于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实例。

认证

认证是使用系统框架URLCredentialURLAuthenticationChallenge实现的。

支持的认证方案

  • HTTP Basic
  • HTTP Digest
  • Kerberos
  • NTLM

HTTP Basic认证

在合适的时候,在一个请求的authenticate方法会自动提供一个URLCredentialURLAuthenticationChallenge

 

根据服务器实现,Authorization header也可能是适合的:

 

使用URLCredential认证

 

注意:使用URLCredential来做认证,如果服务器发出一个challenge,底层的URLSession实际上最终会发两次请求。第一次请求不会包含credential,并且可能会触发服务器发出一个challenge。这个challenge会被Alamofire接收,credential会被添加,然后URLSessin会重试请求。

将数据下载到文件

Alamofire可以把服务器的数据下载到内存(in-memory)或者硬盘(on-disk)中。所有Alamofire.requestAPI下载的数据都是存储在内存中。这比较适合小文件,更高效;但是不适合大文件,因为大文件会把内存耗尽。我们要使用Alamofire.downloadAPI把服务器的数据下载到硬盘中。

下面这个方法只适用于macOS。因为在其他平台不允许在应用沙盒外访问文件系统。下面会讲到如何在其他平台下载文件。

 

下载文件存储位置

我们可以提供一个DownloadFileDestination闭包把临时文件夹的文件移动到一个目标文件夹。在临时文件真正移动到destinationURL之前,闭包内部指定的DownloadOptions将会被执行。目前支持的DownloadOptions有下面两个:

  • .createIntermediateDirectories:如果指定了目标URL,将会创建中间目录。
  • .removePreviousFile:如果指定了目标URL,将会移除之前的文件
 

也可以直接使用建议的下载目标API:

 

下载进度

所有的DownloadRequest都可以使用downloadProgressAPI来反馈下载进度。

 

downloadProgressAPI还可以接受一个queue参数来指定下载进度闭包在哪个DispatchQueue中执行。

 

恢复下载

如果一个DownloadRequest被取消或中断,底层的URL会话会生成一个恢复数据。恢复数据可以被重新利用并在中断的位置继续下载。恢复数据可以通过下载响应访问,然后在重新开始请求的时候被利用。

重要:在iOS 10 - 10.2, macOS 10.12 - 10.12.2, tvOS 10 - 10.1, watchOS 3 - 3.1.1中,resumeData会被后台URL会话配置破坏。因为在resumeData的生成逻辑有一个底层的bug,不能恢复下载。具体情况可以到Stack Overflow看看。

 

上传数据到服务器

使用JOSN或者URL编码参数上传一些小数据到服务器,使用Alamofire.request API就已经足够了。如果需要发送很大的数据,需要使用Alamofire.upload API。当我们需要在后台上传数据时,也可以使用Alamofire.upload

上传数据

 

上传文件

 

上传多部分表单数据

 

上传进度

所有的UploadRequest都可以使用uploadProgressdownloadProgress APIs来反馈上传和下载进度。

 

统计指标

时间表

Alamofire在一个请求周期内收集时间,并创建一个Tineline对象,它是响应类型的一个属性。

 

上面的Timeline信息包括:

  • Latency: 0.428 seconds (延迟)
  • Request Duration: 0.428 seconds (请求时间)
  • Serialization Duration: 0.001 seconds (序列化时间)
  • Total Duration: 0.429 seconds (总时间)

URL会话任务指标

在iOS和tvOS 10和macOS 10.12中,苹果发布了新的URLSessionTaskMetrics APIs。这个任务指标封装了关于请求和响应执行的神奇统计信息。这个API和Timeline非常相似,但是提供了很多Alamofire没有提供的统计信息。这些指标可以通过任何响应去访问。

 

注意:这些API只能在iOS和tvOS 10和macOS 10.12中使用。所以,根据部署目标,可能需要加入版本判断:

 

cURL命令输出

调试平台问题很让人厌烦。庆幸的是,Alamofire的Request对象遵循了CustomStringConvertibleCustomDebugStringConvertible协议来提供一些非常有用的调试工具。

CustomStringConvertible

 

CustomDebugStringConvertible

 

输出:

 

    /**

     AlamofireAction请求

     */

     func AlamofireAction()

    {

        Alamofire.request("http://iappfree.candou.com:8080/free/applications/limited").responseJSON { response in

            print("原始的URL请求 ===== \(response.request!)")  // 原始的URL请求

            print(" HTTP URL响应 ==== \(response.response!)") // HTTP URL响应

            print(" 服务器返回的数据 === \(response.data!)")    // 服务器返回的数据

            print("存储的是JSON数据 === \(response.result)")   // 响应序列化结果,在这个闭包里,存储的是JSON数据

            

            if let JSON = response.result.value {

                print("JSON: \(JSON)")

            }

        }

    }

    

    /**

     GET请求

     */

    func GETACtion()

    {

        //请求URL

        let url:NSURL! = NSURL(string: "http://iappfree.candou.com:8080/free/applications/limited")

        let request:NSMutableURLRequest = NSMutableURLRequest(url: url as URL)

        let list  = NSMutableArray()

        let paramDic = [String: String]()

        

        if paramDic.count > 0 {

            //设置为GET请求

            request.httpMethod = "GET"

            //拆分字典,subDic是其中一项,将key与value变成字符串

            for subDic in paramDic {

                let tmpStr = "\(subDic.0)=\(subDic.1)"

                list.add(tmpStr)

            }

            //用&拼接变成字符串的字典各项

            let paramStr = list.componentsJoined(by: "&")

            //UTF8转码,防止汉字符号引起的非法网址

            let paraData = paramStr.data(using: String.Encoding.utf8)

            //设置请求体

            request.httpBody = paraData

        }

        //默认session配置

        let config = URLSessionConfiguration.default

        let session = URLSession(configuration: config)

        //发起请求

        let dataTask = session.dataTask(with: request as URLRequest) { (data, response, error) in

            

            //            let str:String! = String(data: data!, encoding: NSUTF8StringEncoding)

            //            print("str:\(str)")

            //转Json

            let jsonData:NSDictionary = try! JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! NSDictionary

            

            print("GET 请求数据\(jsonData)")

            

        }

        //请求开始

        dataTask.resume()

    }

    

    

    /**

     POST请求

     */

    func POSTACtion()

    {

        //请求URL

        let url:NSURL! = NSURL(string: "http://iappfree.candou.com:8080/free/applications/limited")

        let request:NSMutableURLRequest = NSMutableURLRequest(url: url as URL)

        let list  = NSMutableArray()

        let paramDic = [String: String]()

        

        if paramDic.count > 0 {

            //设置为POST请求

            request.httpMethod = "POST"

            //拆分字典,subDic是其中一项,将key与value变成字符串

            for subDic in paramDic {

                let tmpStr = "\(subDic.0)=\(subDic.1)"

                print("======tmpStr ===%@",tmpStr)

                list.add(tmpStr)

            }

            //用&拼接变成字符串的字典各项

            let paramStr = list.componentsJoined(by: "&")

            //UTF8转码,防止汉字符号引起的非法网址

            let paraData = paramStr.data(using: String.Encoding.utf8)

            //设置请求体

            request.httpBody = paraData

        }

        //默认session配置

        let config = URLSessionConfiguration.default

        let session = URLSession(configuration: config)

        //发起请求

        let dataTask = session.dataTask(with: request as URLRequest) { (data, response, error) in

            

            //            let str:String! = String(data: data!, encoding: NSUTF8StringEncoding)

            //            print("str:\(str)")

            //转Json

            let jsonData:NSDictionary = try! JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! NSDictionary

            

            print("POST 请求数据\(jsonData)")

            

        }

        //请求开始

        dataTask.resume()

    }

 

 

 

第一部分完。

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

如果想第一时间关注我的文章,可访问我的个人博客网站:曾文志 - iOS开发者,专注Swift。,拉到最下面订阅我的博客,并记得在邮件中确认订阅。谢谢!

欢迎加入我管理的Swift开发群:536353151

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值