使用URLSession请求网络
URLSession
NSURLSession是NSURLConnection的替代者,在2013年苹果全球开发者大会(WWDC2013) 随iOS7一起发布,是对NSURLConnection进行了重构优化后的新的网络访问接口。从iOS9.0开始, NSURLConnection中发送请求的两个方法已过期(同步请求,异步请求),初始化网络连接(initWithRequest: delegate:)的方法也被设置为过期,系统不再推荐使用,建议使用NSURLSession发送网络请求。
使用URLSession
使用URLSession也是简单的三步:
- 创建
Session会话 dataTaskresume
URLSession.shared.dataTask(with: url) { data, response, error in
if error == nil {
print("请求成功\(String(describing: response))")
}
}.resume()
但是可以看出这里使用很方便因为是调用的URLSession的单例,那么如果自己来配置究竟要配置哪些东西?
通过URLSessionConfiguration创建
URLSessionConfiguration的三种模式:
default:默认模式,通常我们用这种模式就足够了,default模式下系统会创建一个持久化的缓存并在用户的钥匙串中存储证书ephemeral:系统没有任何持久性存储,所有内容的生命周期都与Session相同,当Session无效时,所有内容自动释放background:创建一个可以在后台甚至APP已经关闭的时候仍然在传输数据的会话
对于default和ephemeral的不同可以用以下来验证
let defaultConfiguration = URLSessionConfiguration.default
let ephemeralConfiguration = URLSessionConfiguration.ephemeral
print("default 沙盒大小:\(String.init(describing: defaultConfiguration.urlCache?.diskCapacity))")
print("default 内存大小:\(String.init(describing: defaultConfiguration.urlCache?.memoryCapacity))")
print("ephemeral 沙盒大小:\(String.init(describing: ephemeralConfiguration.urlCache?.diskCapacity))")
print("ephemeral 内存大小:\(String.init(describing: ephemeralConfiguration.urlCache?.memoryCapacity))")
可以看出ephemeral并没有像default模式一样占用沙盒的存储
background后台下载
let backgroundConfiguration = URLSessionConfiguration.background(withIdentifier: self.createID())
let session = URLSession.init(configuration: backgroundConfiguration, delegate: self, delegateQueue: OperationQueue.main)
session.downloadTask(with: url).resume()
extension ViewController: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
// 下载完成 - 开始沙盒迁移
print("下载完成 - \(location)")
let locationPath = location.path
//拷贝到用户目录(文件名以时间戳命名)
let documnets = NSHomeDirectory() + "/Documents/" + self.lgCurrentDataTurnString() + ".mp4"
print("移动地址:\(documnets)")
//创建文件管理器
let fileManager = FileManager.default
try! fileManager.moveItem(atPath: locationPath, toPath: documnets)
}
//分段传输/分片传输
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
print("bytesWritten \(bytesWritten)\n totalBytesWritten \(totalBytesWritten)\n totalBytesExpectedToWrite \(totalBytesExpectedToWrite)")
print("下载进度: \(Double(totalBytesWritten)/Double(totalBytesExpectedToWrite))\n")
}
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
print("后台任务下载回来")
DispatchQueue.main.async {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let backgroundHandler = appDelegate.backgroundSessionCompletionHandler else { return }
backgroundHandler()
}
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
print("处理后台URLSession事件")
self.backgroundSessionCompletionHandler = completionHandler
}
比较重要的方法就是上面这个在AppDelegate的方法,其中文档点击此处,其最重要的作用是在后台下载完成后告诉系统已经完成,以便于系统对于后台的快照进行更新
使用Alamofire
AF.request(urlString).response { response in
guard let data = response.data else { return }
let json = try? JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as? [String : Any]
print(json ?? [])
}
首先这里AF是public let AF = Session.default,至于可以直接调用AF是因为完整的写法是Alamofire.AF而Alamofire表示一个模块
另外可以注意到request(urlString)中的urlString是字符串类型,而上面展示原生调用时是需要传入URL类型,那其实Alamofire这里既可以传字符串也可以是URL,其中的技巧就是使用协议以及协议扩展,分别对String和URL进行扩展,那么传入的类型只需要遵循这个协议即可
Alamofire对于此处的处理也正如上述分析所说
Session初始化
之前已经分析过使用Alamofire时AF就是Session.default,其实就是调用了Session的单例,其实重点是看到Session中的便利构造方法,Session其中包括对URLSession的封装,在创建URLSession对象时需要三个重要参数:URLSessionConfiguration、delegate、delegateQueue
其中传入的URLSessionConfiguration是URLSessionConfiguration.af.default
这里可以看出是在使用Alamofire框架时给HTTPHeader做出定制,其中分别是:
Accept-EncodingAccept-LanguageUser-Agent
Accept-Encoding
可以推断出Accept-Encoding应该是br;q=1.0, gzip;q=0.9, deflate;q=0.8
Accept-Language
可以推断出Accept-Language应该是en;q=1.0
User-Agent
这里主要是对字符串进行拼接来设置,大概是iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0
其中具体信息可以通过抓包来看具体信息
此图为使用Alamofire框架的请求
此图为使用原生框架的请求
Alamofire下载
AF.download(urlDownloadStr, to: { temporaryURL, response in
var documentUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
documentUrl?.appendPathComponent(response.suggestedFilename!)
return (documentUrl!, [.createIntermediateDirectories, .removePreviousFile])
}).response { res in
print("下载回调信息(res)")
}.downloadProgress { process in
print("下载进度(process)")
}
可以对比之前原生的下载发现十分简洁,不必再写那些代理回调以及自己移动文件的操作,但是在5.0版本之后,Alamofire不再支持后台下载
扩展的模式中也没有后台模式选项