对于使用
Objective-C
开发者,一定非常熟悉AFNetworking
这个网络框架。在苹果推出的Swift
之后,AFNetworking
的作者专门用Swift
来编写一个类似AFNetworking
的网络框架,称为Alamofire
。它是基于URLSession
封装的,因为在了解Alamofire
之前,我们先来复习一些URLSession
知识.
一、网络请求的基本格式
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error == nil {
print("请求成功\(String(describing: response))" )
}
}.resume()
1.创建URLSession
2.创建dataTask(url)
3.发起请求resume
二、URLSessionConfiguration
open class URLSession : NSObject {
public /*not inherited*/ init(configuration: URLSessionConfiguration)
public /*not inherited*/ init(configuration: URLSessionConfiguration, delegate: URLSessionDelegate?, delegateQueue queue: OperationQueue?)
}
通过指定URLSessionConfiguration
创建URLSession
。下面我们看一下URLSessionConfiguration
有些什么配置项。
open class URLSessionConfiguration : NSObject, NSCopying {
open class var `default`: URLSessionConfiguration { get }
open class var ephemeral: URLSessionConfiguration { get }
@available(iOS 8.0, *)
open class func background(withIdentifier identifier: String) -> URLSessionConfiguration
}
首先我们看到创建URLSessionConfiguration
,包含三种模式default
,ephemeral ``background
,它们之间有什么区别呢?
let configuration1 = URLSessionConfiguration.default
let configuration2 = URLSessionConfiguration.ephemeral
print("沙盒大小: \(String(describing: configuration1.urlCache?.diskCapacity))")
print("内存大小: \(String(describing: configuration1.urlCache?.memoryCapacity))")
print("沙盒大小: \(String(describing: configuration2.urlCache?.diskCapacity))")
print("内存大小: \(String(describing: configuration2.urlCache?.memoryCapacity))")
打印结果:
沙盒大小: Optional(10000000)
内存大小: Optional(512000)
沙盒大小: Optional(0)
内存大小: Optional(512000)
.default
模式比.ephemeral
模式多了100K
的沙盒容量。这个容量用来做什么?.default
模式下系统会创建一个持久化的缓存(缓存就放置到这个沙盒容量)并在用户的钥匙串中存储证书。
而ephemeral
模式下 系统没有任何持久性存储,所有内容的生命周期都与session
相同,当session无效时,所有内容自动释放。
再来看看.background
模式,.background
创建一个可以在后台甚至APP已经关闭的时候仍然在传输数据的会话。.background
模式与.default
模式非常相似,不过background模式会用一个独立线程来进行数据传输。background模式可以在程序挂起,退出,崩溃的情况下运行task。也可以利用标识符来恢复。注意,后台Session一定要在创建的时候赋予一个唯一的identifier,这样在APP下次运行的时候,能够根据identifier来进行相关的区分。如果用户关闭了APP,IOS 系统会关闭所有的background Session。而且,被用户强制关闭了以后,IOS系统不会主动唤醒APP,只有用户下次启动了APP,数据传输才会继续。
let configuration = URLSessionConfiguration.background(withIdentifier: self.createID())
let session = URLSession.init(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
session.downloadTask(with: url).resume()
后台下载使用步骤:
1.初始化一个后台的模式的会话配置
2.初始化session
会话
3.传入url
开启下载resume
4.实现URLSessionDownloadDelegate
代理的下载进度监控
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
5.实现URLSessionDownloadDelegate
代理的下载完成方法
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
开启后台下载这里还有2个点需要注意:
1.需要开启后台下载权限:
var backgroundSessionCompletionHandler: (() -> Void)?
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
self.backgroundSessionCompletionHandler = completionHandler
}
实现`handleEventsForBackgroundURLSession`才可以完美后台下载
2.需要回调系统,告知系统及时更新屏幕
实现NSURLSessionDelegate
代理的urlSessionDidFinishEventsforBackgroundURLSession:
方法
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
print("后台任务下载回来")
DispatchQueue.main.async {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let backgroundHandle = appDelegate.backgroundSessionCompletionHandler else { return }
backgroundHandle()
}
}
那么如果不实现这个代理里面的回调函数的执行会有什么问题?
1.后台下载的能力是不会影响的
2.但是会爆出非常验证界面刷新卡顿,影响用户体验
3.同时打印台会爆出警告
Warning: Application delegate received call to -
application:handleEventsForBackgroundURLSession:completionHandler:
but the completion handler was never called.
URLSessionConfiguration
除了模式的指定,还有很多其他的属性设置。我们分为常规属性、Cookie设置、安全策略设置、缓存策略、支持后台转移、支持自定义协议、 支持多路径TCP、设置HTTP策略和代理属性、支持连接变化等
常规属性
identifier:配置对象的后台会话标识符。
httpAdditionalHeaders:与请求一起发送的附加头文件的字典。
networkServiceType:网络服务的类型
allowsCellularAccess:一个布尔值,用于确定是否应通过蜂窝网络进行连接。
timeoutIntervalForRequest:等待其他数据时使用的超时间隔。
timeoutIntervalForResource:资源请求应该允许的最大时间量。
sharedContainerIdentifier:应该下载后台URL会话中的文件的共享容器的标识符。
waitsForConnectivity:一个布尔值,指示会话是否应等待连接变为可用或者立即失败
设置Cookie政策
httpCookieAcceptPolicy:决定何时应该接受Cookie的策略常量
httpShouldSetCookies:一个布尔值,用于确定请求是否应包含来自Cookie存储的Cookie。
httpCookieStorage:管理cookie存储的单一对象(共享实例)
HTTPCookie:表示HTTP cookie的对象。它是一个不可变的对象,从包含cookie属性的字典中初始化
设置安全策略
tlsMaximumSupportedProtocol:在此会话中进行连接时客户端应请求的最大TLS协议版本。
tlsMinimumSupportedProtocol:协议协商期间应该接受的最小TLS协议。
urlCredentialStorage:提供身份验证凭据的凭证存储
设置缓存策略
urlCache:用于向会话中的请求提供缓存响应的URL缓存
requestCachePolicy:一个预定义常量,用于确定何时从缓存中返回响应
支持后台转移
sessionSendsLaunchEvents:一个布尔值,指示在传输完成时是否应该在后台继续或启动应用程序
isDiscretionary:一个布尔值,用于确定是否可以根据系统的判断来调度后台任务以获得最佳性能。
支持自定义协议
protocolClasses:在会话中处理请求的额外协议子类的数组
URLProtocol:一个NSURLProtocol对象处理加载协议特定的URL数据。在NSURLProtocol类本身是一个抽象类,可以为与特定URL方案的URL处理基础设施。您可以为您的应用支持的任何自定义协议或URL方案创建子类
支持多路径TCP
multipathServiceType:指定用于通过Wi-Fi和蜂窝接口传输数据的多路径TCP连接策略的服务类型
URLSessionConfiguration.MultipathServiceType:指定多路径TCP使用的服务类型的常量
设置HTTP策略和代理属性
httpMaximumConnectionsPerHost:同时连接到给定主机的最大数量。
httpShouldUsePipelining:一个布尔值,用于确定会话是否应使用HTTP流水线
connectionProxyDictionary:包含有关在此会话中使用的代理信息的字典
支持连接变化
waitsForConnectivity:一个布尔值,指示会话是否应等待连接变为可用或者立即失败。
三、NSURLRequestCachePolicy
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
NSURLRequestUseProtocolCachePolicy = 0,
NSURLRequestReloadIgnoringLocalCacheData = 1,
NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
NSURLRequestReturnCacheDataElseLoad = 2,
NSURLRequestReturnCacheDataDontLoad = 3,
NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented
};
1.NSURLRequestUseProtocolCachePolicy
默认的缓存策略,其行为是由协议指定的针对该协议最好的实现方式。
(1)如果请求的缓存响应不存在,则URL加载系统直接从源端加载数据;
(2)否则,如果缓存响应中没有明确表示每次请求必须重新验证,则如果不是响应的缓存过期了,则URL加载系统会返回缓存数据
(3)如果缓存的响应过期或者需要重新验证,URL加载系统发送HEAD请求到源端,查看资源是否发生了变化。如果变化了,则URL加载系统取出从始发源的数据。否则,它返回缓存的响应。
缓存的响应过期或者需要重新验证说明:
对于缓存的响应过期或者需要重新验证的情况,可以通过HTTP中请求和响应头判断:
Cache-Control
在第一次请求到服务器资源的时候,服务器需要使用Cache-Control这个响应头来指定缓存策略,它的格式如下:Cache-Control:max-age=xxxx,这个头指明缓存过期的时间。
Cache-Control
头具有如下选项:
public
指示响应可被任何缓存区缓存
private
内容只缓存到私有缓存中(仅客户端可以缓存)
no-cache
指示请求或响应消息不能缓存
no-store
所有内容都不会被缓存到缓存或 Internet 临时文件中
must-revalidation
如果缓存的内容失效,请求必须发送到服务器进行重新验证
max-age
可以接收生存期不大于指定时间(以秒为单位)的响应
min-fresh
可以接收响应时间小于当前时间加上指定时间的响应
max-stale
可以接收超出超时期间的响应消息
Expires
Expires表示存在时间,允许客户端在这个时间之前不去检查(发请求),等同max-age的效果。但是如果同时存在,则被Cache-Control的max-age覆盖。
格式:Expires = "Expires" ":" HTTP-date"
例如:Expires: Thu, 01 Dec 1994 16:00:00 GMT (必须是GMT格式)
Last-Modified/If-Modified-Since
Last-Modified是由服务器返回响应头,标识资源的最后修改时间.
If-Modified-Since 则由客户端发送,标识客户端所记录的,资源的最后修改时间。服务器接收到带有该请求头的请求时,会使用该时间与资源的最后修改时间进行对比,如果发现资源未被修改过,则直接返回HTTP 304而不返回包体,告诉客户端直接使用本地的缓存。否则响应完整的消息内容。
Etag/If-None-Match
Etag 由服务器发送,告之当资源在服务器上的一个唯一标识符。
客户端请求时,如果发现资源过期(使用Cache-Control的max-age),发现资源具有Etag声明,这时请求服务器时则带上If-None-Match头,服务器收到后则与资源的标识进行对比,决定返回200或者304。
2.NSURLRequestReloadIgnoringCacheData
从服务端加载数据,完全忽略缓存。
3.NSURLRequestReturnCacheDataElseLoad
使用缓存数据,忽略其过期时间;只有在没有缓存版本的时候才从源端加载数据。
4.NSURLRequestReturnCacheDataDontLoad
只使用cache数据,如果不存在cache,请求失败;用于没有建立网络连接离线模式