Alamofire系列(一)

195 阅读4分钟

使用URLSession请求网络

URLSession

NSURLSessionNSURLConnection的替代者,在2013年苹果全球开发者大会(WWDC2013) 随iOS7一起发布,是对NSURLConnection进行了重构优化后的新的网络访问接口。从iOS9.0开始, NSURLConnection中发送请求的两个方法已过期(同步请求,异步请求),初始化网络连接(initWithRequest: delegate:)的方法也被设置为过期,系统不再推荐使用,建议使用NSURLSession发送网络请求。

使用URLSession

使用URLSession也是简单的三步:

  • 创建Session会话
  • dataTask
  • resume
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已经关闭的时候仍然在传输数据的会话

对于defaultephemeral的不同可以用以下来验证

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

image.png

可以看出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 ?? [])
        }

首先这里AFpublic let AF = Session.default,至于可以直接调用AF是因为完整的写法是Alamofire.AFAlamofire表示一个模块

另外可以注意到request(urlString)中的urlString是字符串类型,而上面展示原生调用时是需要传入URL类型,那其实Alamofire这里既可以传字符串也可以是URL,其中的技巧就是使用协议以及协议扩展,分别对StringURL进行扩展,那么传入的类型只需要遵循这个协议即可

Alamofire对于此处的处理也正如上述分析所说

image.png

Session初始化

之前已经分析过使用AlamofireAF就是Session.default,其实就是调用了Session的单例,其实重点是看到Session中的便利构造方法,Session其中包括对URLSession的封装,在创建URLSession对象时需要三个重要参数:URLSessionConfigurationdelegatedelegateQueue

其中传入的URLSessionConfigurationURLSessionConfiguration.af.default

image.png

image.png

这里可以看出是在使用Alamofire框架时给HTTPHeader做出定制,其中分别是:

  • Accept-Encoding
  • Accept-Language
  • User-Agent

Accept-Encoding

image.png

image.png

可以推断出Accept-Encoding应该是br;q=1.0, gzip;q=0.9, deflate;q=0.8

Accept-Language

image.png

image.png

image.png

可以推断出Accept-Language应该是en;q=1.0

User-Agent

image.png

这里主要是对字符串进行拼接来设置,大概是iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0

其中具体信息可以通过抓包来看具体信息

此图为使用Alamofire框架的请求 image.png

此图为使用原生框架的请求 image.png

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不再支持后台下载

image.png

扩展的模式中也没有后台模式选项

image.png

对于不再支持后台下载其中的解释可以点击这里查看