20
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Alamofireでアプリが再起動したらダウンロードを再開する

Last updated at Posted at 2016-11-01

※2018/8/18 Githubのコードをswift4に変更。iOS12 betaで動作するように修正。

ダウンロード最中にアプリが終了したとき、アプリを再起動したらダウンロードを途中から始める。という機能をAlamofireを使用して実装してみました。

環境

Xcode10.0 beta 6
Swift4.0

ライブラリ

Alamofire

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
                    }
                }
            }
        }
    }
})

作成したプロジェクト

github

#最後に
ついこの間までiOS10で動きませんでした。OS側のバグでcancelByProducingResumeDataで出力されるデータが正しくないようです。StackOverflowに助けられました。

※2017/3/28 追記
iOS10.2でキャンセル処理をしたときに取得できるデータからダウンロードの再開は正常に行えるようになりましたが、データの中のoriginalRequest(リクエスト先)とcurrentRequest(リダイレクト先)がnilになっている問題がありました。
iOS10.3では以前と同じようにoriginalRequestとcurrentRequestがURLRequestがData型にキャストされたものを取得できるように修正されていることを確認できました。

20
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?