咔咔咔,敲完一個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
的基礎哦