版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2019.06.15 星期六 |
前言
我們做APP發(fā)起網(wǎng)絡請求,一般都是使用框架瓶摆,這些框架的底層也都是蘋果的API,接下來幾篇就一起來看一下和網(wǎng)絡有關的幾個類性宏。感興趣的可以看上面幾篇文章群井。
1. 詳細解析幾個和網(wǎng)絡請求有關的類 (一) —— NSURLSession
2. 詳細解析幾個和網(wǎng)絡請求有關的類(二) —— NSURLRequest和NSMutableURLRequest
3. 詳細解析幾個和網(wǎng)絡請求有關的類(三) —— NSURLConnection
4. 詳細解析幾個和網(wǎng)絡請求有關的類(四) —— NSURLSession和NSURLConnection的區(qū)別
5. 詳細解析幾個和網(wǎng)絡請求有關的類(五) —— 關于NSURL加載系統(tǒng)(一)
6. 詳細解析幾個和網(wǎng)絡請求有關的類(六) —— 使用NSURLSession(二)
7. 詳細解析幾個和網(wǎng)絡請求有關的類(七) —— URL數(shù)據(jù)的編碼和解碼(三)
8. 詳細解析幾個和網(wǎng)絡請求有關的類(八) —— 處理重定向和其他請求更改(四)
9. 詳細解析幾個和網(wǎng)絡請求有關的類(九) —— 身份驗證挑戰(zhàn)和TLS鏈驗證(五)
10. 詳細解析幾個和網(wǎng)絡請求有關的類(十) —— 理解獲取緩存(六)
11. 詳細解析幾個和網(wǎng)絡請求有關的類(十一) —— Cookies和自定義協(xié)議(七)
12. 詳細解析幾個和網(wǎng)絡請求有關的類(十二) —— URL Session的生命周期(八)
13. 詳細解析幾個和網(wǎng)絡請求有關的類(十三) —— NSURLResponse(一)
14. 詳細解析幾個和網(wǎng)絡請求有關的類(十四) —— NSHTTPCookie(一)
15. 詳細解析幾個和網(wǎng)絡請求有關的類(十五) —— NSHTTPCookieStorage(一)
16. 詳細解析幾個和網(wǎng)絡請求有關的類(十六) —— NSURLCache(一)
17. 詳細解析幾個和網(wǎng)絡請求有關的類(十七) —— NSCachedURLResponse(一)
18. 詳細解析幾個和網(wǎng)絡請求有關的類(十八) —— NSURLAuthenticationChallenge(一)
19. 詳細解析幾個和網(wǎng)絡請求有關的類(十九) —— NSURLProtectionSpace(一)
20. 詳細解析幾個和網(wǎng)絡請求有關的類(二十) —— NSURLCredential(一)
21. 詳細解析幾個和網(wǎng)絡請求有關的類(二十一) —— NSURLCredentialStorage(一)
22. 詳細解析幾個和網(wǎng)絡請求有關的類(二十二) —— NSStream(一)
23. 詳細解析幾個和網(wǎng)絡請求有關的類(二十三) —— NSInputStream(一)
24. 詳細解析幾個和網(wǎng)絡請求有關的類(二十四) —— NSOutputStream(一)
25. 詳細解析幾個和網(wǎng)絡請求有關的類(二十五) —— NSHTTPCookie之設置刪除和通信(二)
26. 詳細解析幾個和網(wǎng)絡請求有關的類(二十六) —— NSURLError錯誤碼及其相關作用(一)
開始
首先看下寫作環(huán)境
Swift 5, iOS 12, Xcode 10
在此URLSession
教程中,您將學習如何創(chuàng)建HTTP
請求以及實現(xiàn)可以暫停和恢復的后臺下載毫胜。
無論應用程序從服務器檢索應用程序數(shù)據(jù)书斜,更新您的社交媒體狀態(tài)還是將遠程文件下載到磁盤诬辈,網(wǎng)絡請求都是讓奇跡發(fā)生的原因。 為了幫助您滿足網(wǎng)絡請求的許多要求荐吉,Apple提供了URLSession
焙糟,這是一個用于上傳和下載內容的完整網(wǎng)絡API。
在本教程中样屠,您將學習如何構建Half Tunes
酬荞,一個查詢iTunes Search API的應用程序,然后下載30秒的歌曲預覽瞧哟。 完成的應用程序將支持后臺傳輸混巧,并允許用戶暫停,恢復或取消正在進行的下載勤揩。
打開下載好的入門項目咧党,它包含用于搜索歌曲和顯示搜索結果的用戶界面,具有一些存根功能的網(wǎng)絡類以及用于存儲和播放曲目的輔助方法陨亡。 這使您可以專注于實現(xiàn)應用程序的網(wǎng)絡方面傍衡。
構建并運行項目。 您將在頂部看到一個帶有搜索欄的視圖负蠕,下面是一個空表視圖:
在搜索欄中鍵入查詢蛙埂,然后點按Search
。 視圖仍然是空的遮糖。 不過不用擔心绣的,您將使用新的URLSession
調用更改此情況。
URLSession Overview
在開始之前欲账,了解URLSession
及其組成類非常重要屡江,因此請查看下面的快速概述。
URLSession
既是一類赛不,也是一套用于處理基于HTTP
和HTTPS
的請求的類:
URLSession
是負責發(fā)送和接收請求的關鍵對象惩嘉。 您可以通過URLSessionConfiguration
創(chuàng)建它,它有三種形式:
- default:創(chuàng)建使用磁盤持久全局緩存踢故,憑據(jù)和cookie存儲對象的默認配置對象文黎。
-
ephemeral:與
default
配置類似,不同之處在于您將所有與會話相關的數(shù)據(jù)存儲在內存中殿较。 將此視為“私人”會話耸峭。 - background:允許會話在后臺執(zhí)行上載或下載任務。 即使應用程序本身被系統(tǒng)暫托敝或終止抓艳,傳輸仍會繼續(xù)触机。
URLSessionConfiguration
還允許您配置會話屬性帚戳,例如超時timeout
值玷或,緩存策略和HTTP標頭。 有關配置選項的完整列表片任,請參Apple’s documentation偏友。
URLSessionTask
是一個表示任務對象的抽象類。 會話創(chuàng)建一個或多個任務來執(zhí)行獲取數(shù)據(jù)和下載或上載文件的實際工作对供。
1. Understanding Session Task Types
有三種類型的具體會話任務:
-
URLSessionDataTask:將此任務用于
GET
請求位他,以將數(shù)據(jù)從服務器檢索到內存。 -
URLSessionUploadTask:使用此任務通過
POST
或PUT
方法將文件從磁盤上載到Web
服務。 - URLSessionDownloadTask:使用此任務將文件從遠程服務下載到臨時文件位置。
您還可以暫停毙死,恢復和取消任務舞蔽。 URLSessionDownloadTask
具有暫停以供將來恢復的額外功能。
通常伦籍,URLSession
以兩種方式返回數(shù)據(jù):
- 當任務完成時,成功或出錯都會走
completion handler
, - 通過調用在創(chuàng)建會話時設置的代理上的方法醒串。
既然您已經(jīng)了解了URLSession
可以做什么,那么您已準備好將理論付諸實踐鄙皇!
DataTask and DownloadTask
首先芜赌,您將創(chuàng)建一個數(shù)據(jù)任務,以便在iTunes Search API
中查詢用戶的搜索詞伴逸。
在SearchViewController.swift
中缠沈,searchBarSearchButtonClicked
啟用狀態(tài)欄上的網(wǎng)絡活動指示器,以向用戶顯示網(wǎng)絡進程正在運行错蝴。 然后它調用getSearchResults(searchTerm:completion :)
博烂,它在QueryService.swift
中被刪除。 您即將構建它以發(fā)出網(wǎng)絡請求漱竖。
在QueryService.swift
中禽篱,使用以下內容替換// TODO 1
:
let defaultSession = URLSession(configuration: .default)
和// TODO 2
用下面替換
var dataTask: URLSessionDataTask?
這就是你所做的:
- 1) 創(chuàng)建了一個
URLSession
并使用default
會話配置對其進行了初始化。 - 2) 聲明了
URLSessionDataTask
馍惹,用于在用戶執(zhí)行搜索時向iTunes
搜索Web服務發(fā)出GET
請求躺率。 每次用戶輸入新的搜索字符串時,都會重新初始化數(shù)據(jù)任務万矾。
接下來悼吱,使用以下內容替換getSearchResults(searchTerm:completion :)
中的內容:
// 1
dataTask?.cancel()
// 2
if var urlComponents = URLComponents(string: "https://itunes.apple.com/search") {
urlComponents.query = "media=music&entity=song&term=\(searchTerm)"
// 3
guard let url = urlComponents.url else {
return
}
// 4
dataTask =
defaultSession.dataTask(with: url) { [weak self] data, response, error in
defer {
self?.dataTask = nil
}
// 5
if let error = error {
self?.errorMessage += "DataTask error: " +
error.localizedDescription + "\n"
} else if
let data = data,
let response = response as? HTTPURLResponse,
response.statusCode == 200 {
self?.updateSearchResults(data)
// 6
DispatchQueue.main.async {
completion(self?.tracks, self?.errorMessage ?? "")
}
}
}
// 7
dataTask?.resume()
}
依次記錄每個編號的評論:
- 1) 對于新用戶查詢,您將取消已存在的任何數(shù)據(jù)任務良狈,因為您要為此新查詢重用數(shù)據(jù)任務對象后添。
- 2) 要在查詢
URL
中包含用戶的搜索字符串,請從iTunes Search base URL
創(chuàng)建URLComponents
薪丁,然后設置其查詢字符串遇西。這可確保您的搜索字符串使用轉義字符馅精。 - 3)
urlComponents
的url
屬性是可選的,因此您將其解包為url
并在它為nil
時提前return
粱檀。 - 4) 在您創(chuàng)建的會話中洲敢,使用查詢
URL
初始化URLSessionDataTask
,并在數(shù)據(jù)任務完成時調用完成處理程序茄蚯。 - 5) 如果請求成功压彭,則調用輔助方法
updateSearchResults
,該方法將響應data
解析為tracks
數(shù)組渗常。 - 6) 切換到主隊列以將
tracks
傳遞給completion handler
壮不。 - 7) 默認情況下,所有任務都以掛起狀態(tài)啟動皱碘。調用
resume()
啟動數(shù)據(jù)任務忆畅。
在SearchViewController
中,查看對getSearchResults(searchTerm:completion :)
的調用中的完成閉包尸执。隱藏活動指示器后家凯,它會將results
存儲在searchResults
中,然后更新表視圖如失。
注意:默認請求方法是GET绊诲。如果要將數(shù)據(jù)任務設置為POST,PUT或DELETE褪贵,請使用
url
創(chuàng)建URLRequest
掂之,設置請求的HTTPMethod
屬性,然后使用URLRequest
而不是URL
創(chuàng)建數(shù)據(jù)任務脆丁。
構建并運行您的應用程序世舰。搜索任何歌曲,您將看到表格視圖填充相關的曲目結果槽卫,如下所示:
有了一些URLSession
代碼跟压,Half Tunes
現(xiàn)在有點功能了!
能夠查看歌曲結果很不錯歼培,但如果你可以點擊一首歌下載它會不會更好震蒋? 這是你的下一個業(yè)務訂單。 您將使用download task
躲庄,這樣可以輕松地將歌曲片段保存在本地文件中查剖。
1. Downloading Classes
處理多次下載需要做的第一件事是創(chuàng)建一個自定義對象來保存活動下載的狀態(tài)。
在Model
組中創(chuàng)建一個名為Download.swift
的新Swift
文件噪窘。
打開Download.swift
笋庄,并在Foundation導入下面添加以下實現(xiàn):
class Download {
var isDownloading = false
var progress: Float = 0
var resumeData: Data?
var task: URLSessionDownloadTask?
var track: Track
init(track: Track) {
self.track = track
}
}
這是Download
屬性的簡要說明:
- isDownloading:下載是正在進行還是暫停。
- progress:下載的小數(shù)進度,表示為介于0.0和1.0之間的浮點數(shù)直砂。
-
resumeData:存儲用戶暫停下載任務時生成的
Data
菌仁。 如果主機服務器支持它,您的應用程序可以使用它來恢復暫停的下載哆键。 -
task:下載
track
的URLSessionDownloadTask
掘托。 -
track:要下載的曲目瘦锹。
track
的url
屬性也充當Download
的唯一標識符籍嘹。
接下來,在DownloadService.swift
中弯院,將// TODO 4
替換為以下屬性:
var activeDownloads: [URL: Download] = [:]
該字典將維護URL與其活動Download
之間的映射(如果有)辱士。
URLSession Delegates
您可以使用完成處理程序completion handler
創(chuàng)建下載任務,就像創(chuàng)建數(shù)據(jù)任務data task
時一樣听绳。 但是颂碘,在本教程的后面部分,您將檢查并更新下載進度椅挣,這需要您實現(xiàn)自定義委托头岔。 所以你現(xiàn)在也可以這樣做。
Apple’s URLSession documentation中列出了幾種會話代理協(xié)議鼠证。 URLSessionDownloadDelegate
處理特定于下載任務的任務級事件峡竣。
您將需要盡快將SearchViewController
設置為會話委托,因此現(xiàn)在您將創(chuàng)建一個符合會話委托協(xié)議的擴展量九。
打開SearchViewController.swift
并將// TODO 5
替換為以下URLSessionDownloadDelegate
擴展名:
extension SearchViewController: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL) {
print("Finished downloading to \(location).")
}
}
唯一的非可選URLSessionDownloadDelegate
方法是urlSession(_:downloadTask:didFinishDownloadingTo :)
适掰,應用程序在下載完成后會調用該方法。 目前荠列,只要下載完成类浪,您就會打印一條消息。
1. Downloading a Track
通過所有準備工作肌似,您現(xiàn)在可以將文件下載到位费就。 您的第一步是創(chuàng)建一個專用會話來處理您的下載任務。
在SearchViewController.swift
中川队,使用以下代碼替換// TODO 6
:
lazy var downloadsSession: URLSession = {
let configuration = URLSessionConfiguration.default
return URLSession(configuration: configuration,
delegate: self,
delegateQueue: nil)
}()
在這里受楼,您使用默認配置初始化單獨的會話,并指定一個委托呼寸,該委托允許您通過委托調用接收URLSession
事件艳汽。 這對于監(jiān)視任務的進度非常有用。
將委托隊列delegate queue
設置為nil
會導致會話創(chuàng)建一個串行操作隊列对雪,以執(zhí)行委派方法和完成處理程序的所有調用河狐。
請注意downloadsSession
的延遲創(chuàng)建;這使您可以在初始化視圖控制器之后延遲創(chuàng)建會話。 這樣做允許您將self
作為委托參數(shù)傳遞給會話初始化程序馋艺。
現(xiàn)在使用以下行替換viewDidLoad()
末尾的// TODO 7
:
downloadService.downloadsSession = downloadsSession
這會將DownloadService
的downloadsSession
屬性設置為您剛剛定義的會話栅干。
通過配置會話和代理,您最終可以在用戶請求跟蹤下載時創(chuàng)建下載任務捐祠。
在DownloadService.swift
中碱鳞,使用以下實現(xiàn)替換startDownload(_ :)
的內容:
// 1
let download = Download(track: track)
// 2
download.task = downloadsSession.downloadTask(with: track.previewURL)
// 3
download.task?.resume()
// 4
download.isDownloading = true
// 5
activeDownloads[download.track.previewURL] = download
當用戶點擊表格視圖單元格的Download
按鈕時,作為TrackCellDelegate
的SearchViewController
會識別此單元格的Track
踱蛀,然后使用該Track
調用startDownload(_ :)
窿给。
這是startDownload(_ :)
中發(fā)生的事情:
- 1) 首先使用軌道
track
初始化Download
。 - 2) 使用新的會話對象率拒,使用
track’s preview URL
創(chuàng)建URLSessionDownloadTask
崩泡,并將其設置為Download
的task
屬性。 - 3) 您可以通過調用
resume()
來啟動下載任務猬膨。 - 4) 您表明下載正在進行中角撞。
- 5) 最后,將下載URL映射到
activeDownloads
中的Download
勃痴。
構建并運行您的應用程序谒所,搜索任何track
,然后點擊單元格上的Download
按鈕沛申。 過了一會兒劣领,您將在調試控制臺中看到一條消息,表示下載已完成污它。
Finished downloading to file:///Users/mymac/Library/Developer/CoreSimulator/Devices/74A1CE9B-7C49-46CA-9390-3B8198594088/data/Containers/Data/Application/FF0D263D-4F1D-4305-B98B-85B6F0ECFE16/tmp/CFNetworkDownload_BsbzIk.tmp.
Download
按鈕仍在顯示剖踊,但您很快就會解決這個問題。 首先衫贬,你想要播放一些曲調德澈!
2. Saving and Playing the Track
下載任務完成后,urlSession(_:downloadTask:didFinishDownloadingTo :)
會提供臨時文件位置的URL
固惯,如打印消息中所示梆造。 您的工作是在從方法返回之前將其移動到應用程序的沙箱容器目錄中的永久位置。
在SearchViewController.swift
中葬毫,使用以下代碼替換urlSession(_:downloadTask:didFinishDownloadingTo :)
中的print
語句:
// 1
guard let sourceURL = downloadTask.originalRequest?.url else {
return
}
let download = downloadService.activeDownloads[sourceURL]
downloadService.activeDownloads[sourceURL] = nil
// 2
let destinationURL = localFilePath(for: sourceURL)
print(destinationURL)
// 3
let fileManager = FileManager.default
try? fileManager.removeItem(at: destinationURL)
do {
try fileManager.copyItem(at: location, to: destinationURL)
download?.track.downloaded = true
} catch let error {
print("Could not copy file to disk: \(error.localizedDescription)")
}
// 4
if let index = download?.track.index {
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadRows(at: [IndexPath(row: index, section: 0)],
with: .none)
}
}
以下是您在每個步驟中所做的事情:
- 1) 您從任務中提取原始請求URL镇辉,在活動下載
(active downloads)
中查找相應的下載并從該字典中刪除它。 - 2) 然后贴捡,將URL傳遞給
localFilePath(for :)
忽肛,它通過將URL
的lastPathComponent
(文件的文件名和擴展名)附加到應用程序的Documents
目錄的路徑,生成要保存的永久本地文件路徑烂斋。 - 3) 使用
fileManager
屹逛,將下載的文件從其臨時文件位置移動到所需的目標文件路徑础废,首先清除該位置的任何項目,然后再開始復制任務罕模。 您還將下載track’s
的downloaded
屬性設置為true
评腺。 - 4) 最后,使用下載
track’s
的index
屬性重新加載相應的單元格淑掌。
構建并運行項目蒿讥,運行查詢,然后選擇任何track
并下載它抛腕。 下載完成后芋绸,您將看到打印到控制臺的文件路徑位置:
file:///Users/mymac/Library/Developer/CoreSimulator/Devices/74A1CE9B-7C49-46CA-9390-3B8198594088/data/Containers/Data/Application/087C38CC-0CEB-4895-ADB6-F44D13C2CA5A/Documents/mzaf_2494277700123015788.plus.aac.p.m4a
Download
按鈕現(xiàn)在消失,因為委托方法將track
的downloaded
屬性設置為true
兽埃。 點按track
侥钳,您將聽到它在AVPlayerViewController
中播放适袜,如下所示:
Pausing, Resuming, and Canceling Downloads
如果用戶想暫停下載或完全取消該怎么辦柄错? 在本節(jié)中,您將實現(xiàn)暫停苦酱,恢復和取消功能售貌,以便用戶完全控制下載過程。
您將首先允許用戶取消活動下載active download
疫萤。
1. Canceling Downloads
在DownloadService.swift
中颂跨,在cancelDownload(_ :)
中添加以下代碼:
guard let download = activeDownloads[track.previewURL] else {
return
}
download.task?.cancel()
activeDownloads[track.previewURL] = nil
要取消下載,您將從active downloads
詞典中的相應Download
中檢索下載任務扯饶,并在其上調用cancel()
以取消該任務恒削。 然后,您將從active downloads
字典中刪除下載對象尾序。
2. Pausing Downloads
您的下一個任務是讓您的用戶暫停下載并稍后再繼續(xù)钓丰。
暫停下載與取消下載類似。 暫停取消下載任務每币,但也會生成恢復數(shù)據(jù)( resume data)
携丁,其中包含足夠的信息,以便在主機服務器支持該功能時稍后恢復下載兰怠。
注意:您只能在特定條件下恢復下載梦鉴。 例如,自您第一次請求資源以來揭保,資源不得更改肥橙。 有關完整的條件列表,請查看here的文檔秸侣。
使用以下代碼替換pauseDownload(_ :)
的內容:
guard
let download = activeDownloads[track.previewURL],
download.isDownloading
else {
return
}
download.task?.cancel(byProducingResumeData: { data in
download.resumeData = data
})
download.isDownloading = false
這里的關鍵區(qū)別是你調用cancel(byProducingResumeData :)
而不是cancel()
存筏。 您為此方法提供了一個閉包參數(shù)娜庇,該參數(shù)允許您將恢復數(shù)據(jù)resume data
保存到相應的Download
以供將來恢復。
您還將Download
的isDownloading
屬性設置為false
方篮,以指示用戶已暫停下載名秀。
現(xiàn)在暫停功能已經(jīng)完成,下一個業(yè)務順序是允許用戶恢復暫停的下載藕溅。
3. Resuming Downloads
使用以下代碼替換resumeDownload(_ :)
的內容:
guard let download = activeDownloads[track.previewURL] else {
return
}
if let resumeData = download.resumeData {
download.task = downloadsSession.downloadTask(withResumeData: resumeData)
} else {
download.task = downloadsSession
.downloadTask(with: download.track.previewURL)
}
download.task?.resume()
download.isDownloading = true
當用戶恢復下載時匕得,請檢查相應的Download
是否存在恢復數(shù)據(jù)resume data
。 如果找到巾表,您將通過使用恢復數(shù)據(jù)調用downloadTask(withResumeData :)
來創(chuàng)建新的下載任務汁掠。 如果由于任何原因缺少恢復數(shù)據(jù),您將使用下載URL創(chuàng)建新的下載任務集币。
在任何一種情況下考阱,您都將通過調用resume
啟動任務,并將Download
的isDownloading
標志設置為true
以指示下載已恢復鞠苟。
Showing and Hiding the Pause/Resume and Cancel Buttons
這三個功能只需要執(zhí)行一項操作:您需要根據(jù)需要顯示或隱藏Pause/Resume and Cancel
按鈕乞榨。
要做到這一點,TrackCell
的configure(track:downloaded:)
需要知道該track
是否有active download
以及它是否正在下載当娱。
在TrackCell.swift
中吃既,將configure(track:downloaded :)
更改為configure(track:downloaded:download :)
:
func configure(track: Track, downloaded: Bool, download: Download?) {
在SearchViewController.swift
中,調用tableView(_:cellForRowAt:)
cell.configure(track: track,
downloaded: track.downloaded,
download: downloadService.activeDownloads[track.previewURL])
在這里跨细,您從activeDownloads
中提取track
的下載對象鹦倚。
回到TrackCell.swift
,在configure(track:downloaded:download :)
中找到// TODO 14
并添加以下屬性:
var showDownloadControls = false
然后使用以下內容替換// TODO 15
:
if let download = download {
showDownloadControls = true
let title = download.isDownloading ? "Pause" : "Resume"
pauseButton.setTitle(title, for: .normal)
}
正如注釋所述冀惭,非nil下載對象意味著下載正在進行中震叙,因此單元格應顯示下載控件:Pause/Resume and Cancel
。 由于暫停和恢復功能共享相同的按鈕散休,您將根據(jù)需要在兩種狀態(tài)之間切換按鈕媒楼。
在這個if-closure
下面,添加以下代碼:
pauseButton.isHidden = !showDownloadControls
cancelButton.isHidden = !showDownloadControls
在此處溃槐,僅在下載處于活動狀態(tài)時才顯示單元格的按鈕匣砖。
最后,替換此方法的最后一行:
downloadButton.isHidden = downloaded
使用以下代碼:
downloadButton.isHidden = downloaded || showDownloadControls
在這里昏滴,如果正在下載曲目track
猴鲫,則告訴單元格隱藏Download
按鈕。
構建并運行您的項目谣殊。 同時下載幾首曲目拂共,您將能夠隨意暫停,恢復和取消它們:
Showing Download Progress
此時姻几,應用程序正常運行宜狐,但未顯示下載進度势告。 要改善用戶體驗,您需要更改應用以偵聽下載進度事件并在單元格中顯示進度抚恒。 有一個會話委托方法咱台,非常適合這項工作!
首先俭驮,在TrackCell.swift
中回溺,使用以下輔助方法替換// TODO 16
:
func updateDisplay(progress: Float, totalSize : String) {
progressView.progress = progress
progressLabel.text = String(format: "%.1f%% of %@", progress * 100, totalSize)
}
track cell
具有progressView
和progressLabel
outlets
。 委托方法將調用此幫助程序方法來設置其值混萝。
接下來遗遵,在SearchViewController.swift
中,將以下委托方法添加到URLSessionDownloadDelegate
擴展:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask,
didWriteData bytesWritten: Int64, totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64) {
// 1
guard
let url = downloadTask.originalRequest?.url,
let download = downloadService.activeDownloads[url]
else {
return
}
// 2
download.progress =
Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
// 3
let totalSize =
ByteCountFormatter.string(fromByteCount: totalBytesExpectedToWrite,
countStyle: .file)
// 4
DispatchQueue.main.async {
if let trackCell =
self.tableView.cellForRow(at: IndexPath(row: download.track.index,
section: 0)) as? TrackCell {
trackCell.updateDisplay(progress: download.progress,
totalSize: totalSize)
}
}
}
仔細研究這種委托方法逸嘀,一步一步:
- 1) 您提取所提供的
downloadTask
的URL
车要,并使用它在您的活動下載字典中查找匹配的Download
。 - 2) 該方法還提供了您編寫的總字節(jié)數(shù)以及您希望寫入的總字節(jié)數(shù)崭倘。您將進度計算為這兩個值的比率翼岁,并將結果保存在
Download
中。track
單元格將使用此值更新進度視圖绳姨。 - 3)
ByteCountFormatter
獲取一個字節(jié)值并生成一個人類可讀的字符串登澜,顯示總下載文件大小阔挠。您將使用此字符串顯示下載大小以及完成百分比飘庄。 - 4) 最后,您找到負責顯示
Track
的單元格购撼,并調用單元格的輔助方法以使用從前面步驟派生的值更新其progress view and progress label
跪削。這涉及到UI,因此您可以在主隊列中執(zhí)行此操作迂求。
Displaying the Download’s Progress
現(xiàn)在碾盐,更新單元的配置以在下載進行時顯示進度視圖和狀態(tài)。
打開TrackCell.swift
揩局。在configure(track:downloaded:download :)
中毫玖,在設置了pause
按鈕標題后,在if-closure
中添加以下行:
progressLabel.text = download.isDownloading ? "Downloading..." : "Paused"
這使得單元格在委托方法第一次更新之前顯示凌盯,并且下載暫停時顯示付枫。
現(xiàn)在,在if-closure
下面添加以下代碼驰怎,在兩個按鈕的isHidden
行下面:
progressView.isHidden = !showDownloadControls
progressLabel.isHidden = !showDownloadControls
這僅在下載過程中顯示progress view and label
阐滩。
構建并運行您的項目。 下載任何track
县忌,隨著下載的進行掂榔,您應該會看到進度條狀態(tài)更新:
Enabling Background Transfers
此時您的應用程序非常實用继效,但還有一個主要的增強功能:后臺傳輸(Background transfers)
。
在此模式下装获,即使您的應用在后臺或因任何原因崩潰瑞信,下載也會繼續(xù)。這對于非常小的歌曲片段來說并不是必需的穴豫,但如果您的應用傳輸大型文件喧伞,您的用戶將會喜歡此功能。
但是绩郎,如果您的應用沒有運行潘鲫,這怎么做到的?
操作系統(tǒng)OS
在應用程序外部運行一個單獨的守護程序來管理后臺傳輸任務肋杖,并在下載任務運行時將適當?shù)奈邢l(fā)送到應用程序溉仑。如果應用程序在主動傳輸期間終止,則任務將在后臺繼續(xù)運行状植,不受影響浊竟。
任務完成后,守護程序(daemon)
將在后臺重新啟動應用程序津畸。重新啟動的應用程序將重新創(chuàng)建后臺會話以接收相關的完成委托消息并執(zhí)行任何所需的操作振定,例如將下載的文件保存到磁盤。
注意:如果用戶通過從應用切換器強制退出來終止應用肉拓,系統(tǒng)將取消所有會話的后臺傳輸后频,并且不會嘗試重新啟動應用。
您可以通過使用后臺background
會話配置創(chuàng)建會話來訪問此魔法暖途。
在SearchViewController.swift
中卑惜,在downloadsSession
的初始化中,找到以下代碼行:
let configuration = URLSessionConfiguration.default
替換為下面代碼
let configuration =
URLSessionConfiguration.background(withIdentifier:
"com.xxxx.HalfTunes.bgSession")
您將使用特殊的后臺background
會話配置驻售,而不是使用默認default
會話配置露久。 請注意,您還為會話設置了唯一標識符欺栗,以便您的應用在需要時創(chuàng)建新的后臺會話毫痕。
注意:您不能為后臺配置創(chuàng)建多個會話,因為系統(tǒng)使用配置的標識符將任務與會話相關聯(lián)迟几。
Relaunching Your App
如果后臺任務在應用程序未運行時完成消请,則應用程序將在后臺重新啟動。 您需要從應用代理處理此事件瘤旨。
切換到AppDelegate.swift
梯啤,用以下代碼替換// TODO 17
:
var backgroundSessionCompletionHandler: (() -> Void)?
將// TODO 18
替換為下面
func application(
_ application: UIApplication,
handleEventsForBackgroundURLSession
handleEventsForBackgroundURLSessionidentifier: String,
completionHandler: @escaping () -> Void) {
backgroundSessionCompletionHandler = completionHandler
}
在這里,您將提供的completionHandler
保存為app delegate
中的變量供以后使用存哲。
application(_:handleEventsForBackgroundURLSession :)
喚醒應用程序來處理已完成的后臺任務因宇。您需要在此方法中處理兩個項目:
- 首先七婴,應用程序需要使用此委托方法提供的標識符重新創(chuàng)建適當?shù)暮笈_配置和會話。但是由于這個應用程序在實例化
SearchViewController
時會創(chuàng)建后臺會話察滑,所以此時您已經(jīng)重新連接了打厘! - 其次,您需要捕獲此委托方法提供的完成處理程序贺辰。調用完成處理程序告訴操作系統(tǒng)您的應用程序已完成當前會話的所有后臺活動户盯。它還會使操作系統(tǒng)對更新的UI進行快照,以便在應用切換器中顯示饲化。
調用提供的完成處理completion handler
程序的地方是urlSessionDidFinishEvents(forBackgroundURLSession :)
莽鸭,這是一個URLSessionDelegate
方法,當后臺會話上的所有任務都完成時觸發(fā)吃靠。
在SearchViewController.swift
中硫眨,使用以下擴展名替換// TODO 19
:
extension SearchViewController: URLSessionDelegate {
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let completionHandler = appDelegate.backgroundSessionCompletionHandler {
appDelegate.backgroundSessionCompletionHandler = nil
completionHandler()
}
}
}
}
上面的代碼從app delegate
中獲取存儲的完成處理程序completion handler
,并在主線程上調用它巢块。 您可以通過獲取UIApplication
的共享實例找到app delegate
礁阁,這可以通過UIKit import
進行獲取。
Testing Your App’s Functionality
構建并運行您的應用程序族奢。 開始幾個并發(fā)下載姥闭,然后點擊Home按鈕將應用程序發(fā)送到后臺。 等到您認為下載已完成越走,然后雙擊Home按鈕以顯示應用程序切換器棚品。
下載應該已經(jīng)完成,您應該在應用程序快照中看到它們的新狀態(tài)弥姻。 打開應用程序以確認:
你現(xiàn)在有一個功能音樂流媒體應用程序南片!
如果您想進一步探索該主題,那么URLSession
主題將多于本教程中的主題庭敦。 例如,您還可以嘗試上載任務和會話配置設置薪缆,例如超時值和緩存策略秧廉。
要了解有關這些功能(以及其他功能!)的更多信息拣帽,請查看以下資源:
- Apple的URLSession Programming Guide包含有關您想要做的所有事情的全面信息疼电。
-
AlamoFire是一個受歡迎的第三方
iOS
網(wǎng)絡庫。
后記
本篇主要講述了一種和網(wǎng)絡請求有關的類
NSURLSession
减拭,感興趣的給個贊或者關注~~~