咔咔咔,敲完一个Alamofire
的下载实现:api
func downLoadFile() {
SessionManager.default.download(urlString) { (url, response) -> (destinationURL: URL, options: DownloadRequest.DownloadOptions) in
let docUrl = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first
let fileUrl = docUrl?.appendingPathComponent(response.suggestedFilename!)
return (fileUrl!, [.removePreviousFile, .createIntermediateDirectories])
}.downloadProgress { (progress) in
print("\(progress)")
}.response { (respond) in
print("\(respond)")
}
}
复制代码
切到后台时,下载不继续执行,切回后,下载继续执行,后台下载的目的没有达到啊。。。 一一般规操做,目的没有达到啊,为何?确定哪里忽略了,default
有木有很刺眼?看下呗bash
public static let `default`: SessionManager = {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return SessionManager(configuration: configuration)
}()
复制代码
SessionManager
的一个单例URLSessionConfiguration
是default
模式background
再看下SessionManager
的init
方法网络
public init(
configuration: URLSessionConfiguration = URLSessionConfiguration.default,
delegate: SessionDelegate = SessionDelegate(),
serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
{
self.delegate = delegate
self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
}
复制代码
configuration
的默认值仍是URLSessionConfiguration
的defualt
模式这个时候咱们就须要从新配置为background
模式了:session
func downLoadBackground() {
let configuration = URLSessionConfiguration.background(withIdentifier: "com.zimi")
let backgroundManager = SessionManager(configuration: configuration)
backgroundManager.download(urlString) { (url, response) -> (destinationURL: URL, options: DownloadRequest.DownloadOptions) in
let documentUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let fileUrl = documentUrl?.appendingPathComponent(response.suggestedFilename!)
return (fileUrl!, [.createIntermediateDirectories, .removePreviousFile])
}
.response { (response) in
print("\(response)")
}.downloadProgress { (progress) in
print("\(progress.fractionCompleted)")
}
}
// 控制台打印:<1> load failed with error Error Domain=NSURLErrorDomain Code=-999 "cancelled"
复制代码
竟然报错了。。。闭包
原来是SessionManager
在downLoadBackground
方法中是局部变量,进入后台下载时被释放了 改为这样app
let backgroundManager: SessionManager = {
let configuration = URLSessionConfiguration.background(withIdentifier: "com.zimi")
let sessionManager = SessionManager(configuration: configuration)
return sessionManager
}()
复制代码
URLSession
的官方文档关于后台下载有四步,在Swift - 网络 URLSession中有介绍,固然不能忘了这个重要的步骤了:框架
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
BackgroundDLViewModel().backgroundManager.backgroundCompletionHandler = completionHandler
}
复制代码
否则在控制台会打印一个警告,切回的时候也会出下卡顿异步
Warning: Application delegate received call to -application:handleEventsForBackgroundURLSession:completionHandler:
but the completion handler was never called.
复制代码
那么重点来了,在用URLSession
来处理后台下载的时候,须要经过urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession)
代理方法来执行completionHandler
的回调,那么,既然SessionManager
的属性backgroundCompletionHandler
帮咱们保存了completionHandler
这个闭包,它是怎么帮咱们来调用的呢? 在前面贴出的init
方法中有commonInit
这个方法的调用,那么咱们来看下:async
private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
session.serverTrustPolicyManager = serverTrustPolicyManager
delegate.sessionManager = self
delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
guard let strongSelf = self else { return }
DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
}
}
复制代码
sessionDidFinishEventsForBackgroundURLSession
代理的闭包声明里,作了backgroundCompletionHandler
闭包回到主线程异步的回调Alamofire
中有一个专职delegate
的类SessionDelegate
,对URLSession
的代理方法urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession)
进行了实现open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
sessionDidFinishEventsForBackgroundURLSession?(session)
}
复制代码
SessionDelegate
重写了NSObject
的responds
方法,经过sessionDidFinishEventsForBackgroundURLSession
闭包是否为空来判断是否执行urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession)
方法open override func responds(to selector: Selector) -> Bool {
#if !os(macOS)
if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
return sessionDidFinishEventsForBackgroundURLSession != nil
}
#endif
//省略了一些代码
}
复制代码
是否是很6啊?不用咱们再写代理,也不用再写代理方法的实现了,Alamofire
帮咱们省了这一步了。ide
像
Alamofire
这些优秀的框架能帮咱们省不少的代码,但咱们也不能忘了原生API
的基础哦