URLSession
是一個(gè)完整的網(wǎng)絡(luò)API,用于通過(guò)HTTP上傳和下載內(nèi)容哥牍。
URL loading system包括加載URL的類(lèi)以及許多重要的幫助程序類(lèi)毕泌,這些輔助程序類(lèi)與這些URL loading 一起使用以修改其行為。主要幫助程序類(lèi)分為五類(lèi):協(xié)議支持嗅辣,身份驗(yàn)證和憑據(jù)撼泛,cookie存儲(chǔ),配置管理和緩存管理辩诞。
簡(jiǎn)單來(lái)說(shuō)URL loading system 就是一組用于與服務(wù)器通信的類(lèi).下圖有點(diǎn)老了是2013年的版本, 最新的版本沒(méi)找到.
使用URLSessionAPI坎弯,您的應(yīng)用會(huì)創(chuàng)建一個(gè)或多個(gè)會(huì)話(huà),每個(gè)會(huì)話(huà)都會(huì)協(xié)調(diào)一組相關(guān)的數(shù)據(jù)傳輸任務(wù)译暂。例如抠忘,你可以創(chuàng)建多個(gè)會(huì)話(huà), 一個(gè)可以用于下載數(shù)據(jù), 一個(gè)用于請(qǐng)求數(shù)據(jù), 在每個(gè)會(huì)話(huà)中,您的應(yīng)用程序會(huì)添加一系列任務(wù)外永,每個(gè)任務(wù)都代表對(duì)特定URL的請(qǐng)求崎脉。
URL會(huì)話(huà)的類(lèi)型
URLSession是負(fù)責(zé)發(fā)送和接收HTTP請(qǐng)求的關(guān)鍵對(duì)象, URLSession
具有shared
基本請(qǐng)求的單例會(huì)話(huà)(沒(méi)有配置對(duì)象)。創(chuàng)建的會(huì)話(huà)不可自定義. 對(duì)于其他類(lèi)型的會(huì)話(huà)伯顶。通過(guò)URLSessionConfiguration創(chuàng)建囚灼,有三種形式:
.default:
默認(rèn)會(huì)話(huà)的行為與shared會(huì)話(huà)非常相似(除非您進(jìn)一步自定義它們),但是他可以讓您使用委托
以增量方式
獲取數(shù)據(jù)祭衩。您可以通過(guò)調(diào)用URLSessionConfiguration類(lèi)上的默認(rèn)方法來(lái)創(chuàng)建默認(rèn)會(huì)話(huà)配置灶体。
shared 是無(wú)法設(shè)置委托的, 所以只能使用系統(tǒng)提供的閉包處理數(shù)據(jù)
open func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
但是default會(huì)話(huà)是可以自定義委托對(duì)象, 使用委托回調(diào)來(lái)處理數(shù)據(jù), 這里一定要注意以增量的方式獲取數(shù)據(jù), 這點(diǎn)與閉包區(qū)別很大, 下面會(huì)具體講到
.ephemeral:
臨時(shí)會(huì)話(huà)與默認(rèn)會(huì)話(huà)類(lèi)似,但它們不會(huì)將高速緩存掐暮,cookie或憑據(jù)寫(xiě)入磁盤(pán)蝎抽。您可以通過(guò)調(diào)用URLSessionConfiguration類(lèi)上的臨時(shí)方法來(lái)創(chuàng)建臨時(shí)會(huì)話(huà)配置。
.background:
允許會(huì)話(huà)在后臺(tái)執(zhí)行上載或下載任務(wù)路克。即使應(yīng)用程序本身被系統(tǒng)暫驼两幔或終止养交,傳輸仍會(huì)繼續(xù)。(這個(gè)很牛呀)
URLSessionConfiguration
是一個(gè)配置會(huì)話(huà)的類(lèi), 例如, 請(qǐng)求的超時(shí)時(shí)長(zhǎng), 緩存策略, 請(qǐng)求頭等等
URLSessionTask
是一個(gè)表示任務(wù)對(duì)象的抽象類(lèi), 抽象類(lèi)是不能直接創(chuàng)建對(duì)象來(lái)使用的, 須有其子類(lèi)來(lái)完成, 子類(lèi)會(huì)話(huà)創(chuàng)建一個(gè)或多個(gè)任務(wù)來(lái)執(zhí)行獲取數(shù)據(jù)和下載或上載文件的事件瓢宦。
有三種類(lèi)型的具體會(huì)話(huà)任務(wù):
URLSessionDataTask
:將此任務(wù)用于HTTP GET請(qǐng)求碎连,以將數(shù)據(jù)從服務(wù)器檢索到內(nèi)存。
URLSessionUploadTask
:使用此任務(wù)通常通過(guò) POST或PUT方法將文件從磁盤(pán)上傳到Web服務(wù)驮履。
URLSessionDownloadTask
:使用此任務(wù)將文件從遠(yuǎn)程服務(wù)下載到臨時(shí)文件位置鱼辙。
具體的工作流程首先創(chuàng)建一個(gè)會(huì)話(huà)URLSession
, 然后使用URLSessionConfiguration
來(lái)配置會(huì)話(huà), 然后使用URLSession去調(diào)用task方法, 調(diào)用不同的task會(huì)返回對(duì)應(yīng)的task任務(wù), 然后由task執(zhí)行resume()
具體的任務(wù).
每一個(gè)App里面不可能只存在一個(gè)網(wǎng)絡(luò)請(qǐng)求, 存在多個(gè)網(wǎng)絡(luò)請(qǐng)求, 如果這些網(wǎng)絡(luò)請(qǐng)求的配置(URLSessionConfiguration)是相同的, 你不應(yīng)該為每個(gè)請(qǐng)求創(chuàng)建一個(gè)URLSession, 而是創(chuàng)建一個(gè)URLSession單例對(duì)象, 在他們之間共享.
上面說(shuō)到URLSessionTask
有三種任務(wù), 數(shù)據(jù), 上傳和下載任務(wù)
數(shù)據(jù)任務(wù)
獲取數(shù)據(jù)有兩種方式一種使用閉包(Receive Results with a Completion Handler
), 一種使用委托(Receive Transfer Details and Results with a Delegate
)
1. 使用閉包
獲取數(shù)據(jù)的最簡(jiǎn)單方法是創(chuàng)建使用閉包處理程序的數(shù)據(jù)任務(wù)。通過(guò)這種方式, 不需要配置會(huì)話(huà), 直接調(diào)用共享會(huì)話(huà)URLSession.shared
玫镐,任務(wù)將服務(wù)器的響應(yīng)座每,數(shù)據(jù)和可能的錯(cuò)誤傳遞給您提供的閉包。最關(guān)鍵的是數(shù)據(jù)不是增量返回的, 是一次完整的返回, 不需要自己去處理增量數(shù)據(jù).
let session = URLSession.shared
let sessionTask = session.dataTask(with: request) { (data, response, error) in
if let error = error {
print(error as Any)
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print(response as Any)
return
}
if let mimeType = httpResponse.mimeType, mimeType == "application/json",
let data = data {
DispatchQueue.main.async {
let json = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)
print(json as Any)
}
}
}
sessionTask.resume()
- 驗(yàn)證
error
參數(shù)是否nil
摘悴。如果不是,則發(fā)生傳輸錯(cuò)誤; 處理錯(cuò)誤并退出舰绘。 - 檢查
response
參數(shù)以驗(yàn)證狀態(tài)代碼是否指示成功蹂喻,以及MIME類(lèi)型是否為預(yù)期值。如果沒(méi)有捂寿,請(qǐng)?zhí)幚矸?wù)器錯(cuò)誤并退出口四。
mimeType代表服務(wù)器給我們返回的數(shù)據(jù)類(lèi)型, 根據(jù)這個(gè)字段來(lái)區(qū)分如何去解析數(shù)據(jù), 如application/json
表示JSON數(shù)據(jù),text/html
表示HTML數(shù)據(jù)等等 -
data
我們需要的數(shù)據(jù), dataTask是異步任務(wù), 一半獲取數(shù)據(jù)之后如果要進(jìn)行在主線(xiàn)程刷新UI, 這時(shí)需要回到主線(xiàn)程. - 創(chuàng)建的task任務(wù), 是處于掛起狀態(tài)的, 他自己不會(huì)自動(dòng)執(zhí)行請(qǐng)求數(shù)據(jù)的任務(wù), 需要手動(dòng)執(zhí)行
resume()
使用閉包處理有兩種方法, 如下所示
open func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
open func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
2. 使用代理
共享URLSession雖然使用起來(lái)簡(jiǎn)單方便, 但是存在很多局限, 如果想使用更加完善的請(qǐng)求, 則可以使用代理.
通過(guò)代理方法,數(shù)據(jù)是分批依次返回的秦陋,直到傳輸完成或失敗出現(xiàn)錯(cuò)誤蔓彩。隨著數(shù)據(jù)傳輸?shù)倪M(jìn)行,URLSessionDataDelegate會(huì)收到代理事件urlSession(_:dataTask:didReceive:)
在使用閉包處理時(shí), 使用和系統(tǒng)給我們提供的單例對(duì)象shared
, 我們自己創(chuàng)建時(shí), 也應(yīng)該使用單例對(duì)象, 因?yàn)閁RLSession在請(qǐng)求數(shù)據(jù)沒(méi)有完成之前不能被釋放掉, 否則, 數(shù)據(jù)將不能被請(qǐng)求
如下所示
override func viewDidLoad() {
super.viewDidLoad()
let sessionConfigure = URLSessionConfiguration.default
sessionConfigure.networkServiceType = .default
let session = URLSession.init(configuration: sessionConfigure, delegate: self, delegateQueue: OperationQueue.main)
let sessionTask = session.dataTask(with: request)
}
這樣URLSession會(huì)在viewDidLoad() 方法執(zhí)行完畢之后就會(huì)被釋放掉, 您將不會(huì)受到代理的回調(diào)數(shù)據(jù). (這是一個(gè)巨坑, 這個(gè)和AVPlayer一樣, AVPlayer如果被釋放了, 音視頻也將不能播放, 使用時(shí)要注意)
創(chuàng)建使用委托的URLSession
var receiveData:Data?
private lazy var session: URLSession = {
let configuration = URLSessionConfiguration.default
configuration.waitsForConnectivity = true
return URLSession(configuration: configuration,
delegate: self, delegateQueue: nil)
}()
func delegateHandler(request:URLRequest) {
let sessionTask = session.dataTask(with: request)
receiveData = Data()
sessionTask.resume()
}
receiveData用來(lái)拼接每次請(qǐng)求獲取的數(shù)據(jù), 懶加載session, 如果多次創(chuàng)建只會(huì)存在一個(gè). delegateHandler 用來(lái)發(fā)起請(qǐng)求
URLSessionDataDelegate代理方法
// 1
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
guard let response = response as? HTTPURLResponse,
(200...299).contains(response.statusCode),
let mimeType = response.mimeType,
mimeType == "application/json" else {
completionHandler(.cancel)
return
}
completionHandler(URLSession.ResponseDisposition.allow)
}
//2
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
print("receive = \(data.count)")
receiveData?.append(data)
}
//3
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil {
print("fail")
}else {
if let data = receiveData {
let json = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)
print(json as Any)
}
print("success")
}
}
- 收到響應(yīng)的回調(diào), 在該代理方法中可以進(jìn)行一些數(shù)據(jù)驗(yàn)證, 決定請(qǐng)求時(shí)繼續(xù)還是取消, 或者其他的操作
- 依次獲取的數(shù)據(jù)
- 請(qǐng)求結(jié)束或者出現(xiàn)錯(cuò)誤的回調(diào)
下載任務(wù)
URLSessionDataTask
和 URLSessionDownloadTask
都可以用來(lái)實(shí)現(xiàn)下載任務(wù), 在上面使用代理的方式請(qǐng)求數(shù)據(jù)的時(shí)候, 代理方法
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
以增量的方式來(lái)獲取數(shù)據(jù), 我們下載文件的時(shí)候也可以使用這種方法.
以下載mp3文件為例
1. URLSessionDataTask下載方式
let url = URL.init(string: "")
let request = URLRequest.init(url: url!)
let sessionTask = session.dataTask(with: request)
receiveData = Data()
sessionTask.resume()
前面實(shí)現(xiàn)URLSessionDataDelegate的代理方法, 是用來(lái)接收J(rèn)SON數(shù)據(jù)的, 既然我們要下載mp3文件, 就要對(duì)代理修改, 適應(yīng)mp3 文件的下載
首先要?jiǎng)?chuàng)建一個(gè)下載路徑
var downloadPath = ""
if let documentsPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first {
print(documentsPath)
downloadPath = documentsPath + "/download"
do {
try FileManager.default.createDirectory(atPath: downloadPath, withIntermediateDirectories: true, attributes: nil)
} catch {
print(error)
}
}
// URLSessionDataDelegate 代理方法
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
guard let response = response as? HTTPURLResponse,
(200...299).contains(response.statusCode),
let mimeType = response.mimeType,
mimeType == "audio/mpeg" else { // 1.
completionHandler(.cancel)
return
}
completionHandler(URLSession.ResponseDisposition.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
print("receive = \(data.count)")
receiveData?.append(data)
print("當(dāng)前下載 = \(data.count), 已經(jīng)下載 = \(dataTask.countOfBytesReceived), 總共需要下載 = \(dataTask.countOfBytesExpectedToReceive)")
}
// URLSessionDownloadDelegate 下載完成也會(huì)走這個(gè)方法,后走
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil {
print("fail")
}else {
if let data = receiveData { // 2.
do {
// 如果名稱(chēng)相同, 會(huì)覆蓋之前的數(shù)據(jù)
let url = URL.init(fileURLWithPath: downloadPath + "/\(Date().description).mp3")
try data.write(to: url, options: Data.WritingOptions.atomicWrite)
} catch let error as NSError {
print(error.localizedDescription)
}
}
print("success")
}
}
- mimeType的類(lèi)型要修改成audio/mpeg
- 當(dāng)數(shù)據(jù)下載完成時(shí), 要把數(shù)據(jù)寫(xiě)入磁盤(pán)
- 需要注意的是, 當(dāng)寫(xiě)入文件是, 如果名字相同, 后者會(huì)覆蓋前者的文件
并不是所有的文件都支持下載的, 這個(gè)要具體需要服務(wù)器來(lái)配置, 打印URLResponse, 會(huì)看到下面的內(nèi)容
<NSHTTPURLResponse: 0x60000243ba20> { URL: http://.mp3 } { Status Code: 200, Headers {
"Accept-Ranges" = (
bytes
);
"Content-Length" = (
6502067
);
"Content-Type" = (
"audio/mpeg"
);
Date = (
"Wed, 26 Dec 2018 14:59:59 GMT"
);
Etag = (
"\"8078c31b864ce1:0\""
);
"Last-Modified" = (
"Sun, 09 Jun 2013 02:22:29 GMT"
);
Server = (
"Microsoft-IIS/7.5"
);
"X-Powered-By" = (
"ASP.NET"
);
} }
-
Accept-Ranges
代表是否支持?jǐn)帱c(diǎn)下載bytes
支持,none
不支持 -
Content-Length
文件大小驳概,以字節(jié)為單位的十進(jìn)制數(shù), 對(duì)應(yīng)的expectedContentLength
-
Content-Type
文件類(lèi)型, 對(duì)應(yīng)的mimeType
一般的文件下載如果有一個(gè)下載進(jìn)度條會(huì)更好, 但是URLSessionDataDelegate沒(méi)有提供給我們進(jìn)度更新的代理方法, 這個(gè)只能有我們自己去處理, 要計(jì)算進(jìn)度需要三個(gè)值, 當(dāng)前下載的字節(jié)數(shù), 已經(jīng)下載的字節(jié)數(shù), 期望下載的字節(jié)數(shù),
- 在
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
的代理方法返回了每次請(qǐng)求到的數(shù)據(jù) - 在響應(yīng)頭里既然已經(jīng)返回了文件的大小, 可以通過(guò)
expectedContentLength
來(lái)獲取期望下載的數(shù)據(jù)大小, 也可以通過(guò)URLSessionTask的countOfBytesExpectedToReceive
獲取 - 已經(jīng)下載的字節(jié)數(shù), 可使用累加每次下載的字節(jié)數(shù)來(lái)獲取, 也可以通過(guò)URLSessionTask 的
countOfBytesReceived
獲取
如此, 可完成一個(gè)進(jìn)度條的功能
2. URLSessionDownloadTask下載方式
雖然使用URLSessionDataTask也完成了下載, 但是需要我們自己去處理的事情有點(diǎn)多, 而URLSessionDownloadTask則是Apple封裝的用于下載的任務(wù), 包括斷點(diǎn)下載都做了很好的封裝處理, 下面使用URLSessionDownloadTask 來(lái)完成同樣的mp3文件的下載
創(chuàng)建一個(gè)URLSessionDownloadTask任務(wù)
let sessionTask = session.downloadTask(with: request)
實(shí)現(xiàn)URLSessionDownloadDelegate
// 1
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print(location)
do {
try FileManager.default.moveItem(at: location, to: URL.init(fileURLWithPath: downloadPath + "/\(Date().description).mp3"))
} catch let error as NSError {
print(error)
}
}
//2
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
print("當(dāng)前下載 = \(bytesWritten), 已經(jīng)下載 = \(totalBytesWritten), 總共需要下載 = \(totalBytesExpectedToWrite)")
print("\(Float(totalBytesWritten) / Float(totalBytesExpectedToWrite) * 100)%")
print(Float(totalBytesWritten) / Float(totalBytesExpectedToWrite))
DispatchQueue.main.async {
self.progress.setProgress(Float(totalBytesWritten) / Float(totalBytesExpectedToWrite), animated: true)
}
}
//3
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
print("fileOffset = \(fileOffset), expectedTotalBytes = \(expectedTotalBytes)")
}
URLSessionDownloadDelegate有三個(gè)代理方法
- 下載完成的回調(diào), 改回調(diào)的location是下載的文件路徑, URLSessionDownloadTask任務(wù)會(huì)把文件下載到tmp文件, tmp文件是一個(gè)臨時(shí)文件里面的數(shù)據(jù)會(huì)被系統(tǒng)刪除, 所以當(dāng)收到下載完成的回調(diào)時(shí), 要立馬把該文件移動(dòng)到其他的文件目錄下.
- 下載進(jìn)度的回調(diào), 下載任務(wù)是默認(rèn)異步線(xiàn)程, 刷新UI需要回到UI線(xiàn)程
- 斷點(diǎn)下載時(shí)的文件偏移量
需要注意的是, 如果實(shí)現(xiàn)了URLSessionDownloadDelegate, 則URLSessionDataDelegate的方法只有func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)會(huì)在文件下載完成好會(huì)執(zhí)行, 在didFinishDownloadingTo代理執(zhí)行完之后再執(zhí)行該方法
每一個(gè)URLSessionDownloadTask管理者自己的下載任務(wù), 創(chuàng)建完任務(wù)后, 每執(zhí)行一次
sessionTask.resume()
便開(kāi)始了一個(gè)異步下載任務(wù), 不同的任務(wù)之間相互不影響. 如果要實(shí)現(xiàn)多個(gè)下載任務(wù), 則只需自己處理好數(shù)據(jù)接受即可.
暫停下載, 繼續(xù)下載, 取消下載
1. 暫停下載
如果暫停成功, 則在閉包里返回當(dāng)前已經(jīng)下載的文件, 如果繼續(xù)下載只需把該文件存儲(chǔ)起來(lái), 以供繼續(xù)下載時(shí)使用
open func cancel(byProducingResumeData completionHandler: @escaping (Data?) -> Void)
2. 繼續(xù)下載
let sessionTask = session.downloadTask(withResumeData: data)
sessionTask.resume()
withResumeData參數(shù)是繼續(xù)下載的文件, 傳入該文件, 然后執(zhí)行resume()方法便可以繼續(xù)下載. 繼續(xù)下載時(shí)會(huì)執(zhí)行
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
print("fileOffset = \(fileOffset), expectedTotalBytes = \(expectedTotalBytes)")
}
fileOffset為已經(jīng)下載的數(shù)據(jù)量, expectedTotalBytes為需要下載的數(shù)據(jù)量
這里有一個(gè)坑, 當(dāng)我們使用open func downloadTask(with request: URLRequest) -> URLSessionDownloadTask創(chuàng)建一個(gè)下載任務(wù)時(shí), 返回值是一個(gè)URLSessionDownloadTask
, 是一個(gè)新的下載任務(wù)
當(dāng)我們暫停下載, 在已經(jīng)下載的基礎(chǔ)上繼續(xù)下載時(shí)open func downloadTask(withResumeData resumeData: Data) -> URLSessionDownloadTask, 該方法也是創(chuàng)建了一個(gè)新的下載任務(wù), 返回值也是URLSessionDownloadTask
, 與之前的未下載完成的那個(gè)下載任務(wù)已經(jīng)沒(méi)有關(guān)系了, 要恢復(fù)下載執(zhí)行resume()
方法時(shí), 要使用downloadTask(withResumeData resumeData: Data)
返回的下載任務(wù). 否則無(wú)效.
只有滿(mǎn)足以下條件赤嚼,才能恢復(fù)下載:
- 自您第一次請(qǐng)求資源以來(lái),資源沒(méi)有變化
- 該任務(wù)是HTTP或HTTPS GET請(qǐng)求
- 服務(wù)器在其響應(yīng)中提供ETag或Last-Modified標(biāo)題(或兩者)
- 服務(wù)器支持字節(jié)范圍請(qǐng)求
- 系統(tǒng)尚未刪除臨時(shí)文件(如果磁盤(pán)不足, 系統(tǒng)會(huì)自動(dòng)刪除tmp里的文件)
3. 取消下載
URLSessionTask.cancel()
該方法是不能恢復(fù)下載的
查看URLSessionTask頭文件, 有一個(gè)方法suspend()
, 該方法也是暫停操作, 暫停之后再執(zhí)行resume()
也是可以恢復(fù)下載. 但是系統(tǒng)沒(méi)有方法給我們返回已經(jīng)下載的內(nèi)容, 以供以后繼續(xù)下載. 如果App被殺掉, 則無(wú)法繼續(xù)執(zhí)行resume()
來(lái)完成下載. 雖然suspend()
方法會(huì)把已經(jīng)下載的文件放在tmp文件夾里, 心想可以讀取tmp里面的文件, 然后執(zhí)行downloadTask(withResumeData: data)
繼續(xù)下載, 但是該文件的名稱(chēng)是沒(méi)有規(guī)律的, 無(wú)法知道該文件的名稱(chēng), 也就無(wú)法獲取數(shù)據(jù). (ps, 如果有更好的方法可以實(shí)現(xiàn), 煩請(qǐng)告知下)
后臺(tái)下載
let configuration = URLSessionConfiguration.background(withIdentifier:
"bgSessionConfiguration")
注意:后臺(tái)配置只能創(chuàng)建一個(gè)會(huì)話(huà)顺又,因?yàn)橄到y(tǒng)使用配置的標(biāo)識(shí)符將任務(wù)與會(huì)話(huà)相關(guān)聯(lián).
使用后臺(tái)配置模式, 當(dāng)我們把應(yīng)用程序退到后臺(tái)時(shí), 應(yīng)用程序?qū)⒗^續(xù)在后臺(tái)下載, 當(dāng)下載完成時(shí), 應(yīng)用程序?qū)?huì)收到handleEventsForBackgroundURLSession
和urlSessionDidFinishEvents
的通知
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
}
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
//更細(xì)UI
}
}
先執(zhí)行AppDelegate的代理, 再執(zhí)行URLSessionDelegate的代理, 可以在回調(diào)里面對(duì)下載文件做一些處理, 比如更新UI
開(kāi)啟后臺(tái)要在后有一個(gè)疑問(wèn), 開(kāi)啟下載, tmp文件夾里沒(méi)有臨時(shí)文件, 如果不使用后臺(tái)下載就會(huì)有臨時(shí)文件, 不知道這個(gè)是什么原因.