※2018/8/18 Githubのコードをswift4に変更。iOS12 betaで動作するように修正。
ダウンロード最中にアプリが終了したとき、アプリを再起動したらダウンロードを途中から始める。という機能をAlamofireを使用して実装してみました。
環境
Xcode10.0 beta 6
Swift4.0
ライブラリ
SessionManagerの初期化
URLSessionConfigurationを作成します。identifierはセッションを識別するためのユニークな識別子です。URLSessionConfigurationにはタイムアウトやキャッシュポリシーなどを設定することができます。
configurationでSessionManagerを初期化することでアプリが終了されても前回のタスクを取り出すことができます。
let configuration = URLSessionConfiguration.background(withIdentifier: identifier)
self.manager = Alamofire.SessionManager(configuration: configuration)
前回終了したタスクを取り出す
getAllTaskでセッションに登録されているタスクを取得します。前回終了したときのタスクはエラー扱いでコードがNSURLErrorCancelled(-999)として取得できます。このerror.userInfoにはData型で前回終了したタスクを再開するためのplistが格納されています。今回は一度キャッシュディレクトリに保存してから利用する形にしました。
self.manager.session.getAllTasks(completionHandler: {[weak self] tasks in
guard let wself = self else {
return
}
for task in tasks {
guard let error = task.error else {
continue
}
//エラーコードがキャンセル
if (error as NSError).code == NSURLErrorCancelled {
//リクエストのクエリからtag名を取得する
let query = task.originalRequest!.url!.query!
let queryAr = query.components(separatedBy: "&")
for keyParam in queryAr {
let keyParamAr = keyParam.components(separatedBy: "=")
if keyParamAr[0] == "tag" {
let id = Int(keyParamAr[1])!
do {
//plistを保存する
let resumeData = (((error as NSError).userInfo) as NSDictionary)["NSURLSessionDownloadTaskResumeData"] as! Data
let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let fileUrl = cacheURL.appendingPathComponent(String(format : DownloadService.resumeFileFormat, id))
try resumeData.write(to: fileUrl)
//ビュー更新のためのクロージャー
if let prevDownloadClosure = wself.prevDownloadClosure {
prevDownloadClosure(id)
}
//ダウンロードを再開する
wself.resumeDownload(id: id, url: task.originalRequest!.url!.path)
} catch {
print("plist write error")
continue
}
}
}
}
}
})
作成したプロジェクト
#最後に
ついこの間までiOS10で動きませんでした。OS側のバグでcancelByProducingResumeDataで出力されるデータが正しくないようです。StackOverflowに助けられました。
※2017/3/28 追記
iOS10.2でキャンセル処理をしたときに取得できるデータからダウンロードの再開は正常に行えるようになりましたが、データの中のoriginalRequest(リクエスト先)とcurrentRequest(リダイレクト先)がnilになっている問題がありました。
iOS10.3では以前と同じようにoriginalRequestとcurrentRequestがURLRequestがData型にキャストされたものを取得できるように修正されていることを確認できました。