woshidan's blog

あいとゆうきとITと、とっておきの話。

NSURLSessionを使って通信処理を書いてみる

http://woshidan.hatenablog.com/entry/2017/06/18/014734 の記事の続きで、先の記事の中の

2. Foregroundでダウンロード処理を開始する場合は、ダウンロード中にアプリが停止ないし終了してもよいようにそのダウンロード処理の管理をシステム側へ手渡すことができる

の部分についてメモします。

ポイント

  • ファイルをダウンロードするときは、NSURLSessionを使う
    • そうすることでアプリがバックグラウンドでデータ通信している間はiOSのシステム側がアプリを停止してもダウンロード処理はシステム側でしてくれる
  • ダウンロード/アップロードが終わったり、アプリに注目させる必要がある場合はアプリを起動させることができる
  • どのNSURLSessionオブジェクトによるデータ通信処理か、どのNSURLSessionオブジェクトによるapplication(_:handleEventsForBackgroundURLSession:completionHandler:コールバックかを判定するためには、NSURLSessionとNSURLSessionConfigurationを作成する際に idenrifier(NSString) を使う

試し書き

試しに書いてみたときのメモと置いておきます。

class ViewController: UIViewController, URLSessionTaskDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        let button = UIButton();
        button.frame = CGRect(x: 200, y: 200, width: 120, height: 50);
        button.setTitle("download test", for: UIControlState.normal);
        button.backgroundColor = UIColor.blue;
        button.addTarget(self, action: #selector(onClick(sender:)), for: UIControlEvents.touchUpInside);
        self.view.addSubview(button);
    }
    
    func onClick(sender: UIButton) {
        // let session = URLSession();
        // -> [NSURLSession downloadTaskForRequest:downloadFilePath:resumeData:completion:]: unrecognized selector sent to instance 0x6180000103a0
        // see: https://stackoverflow.com/questions/32905874/nsurlsession-datataskforrequestcompletion-unrecognized-selector-sent-to-instan
        // let config = URLSessionConfiguration.default;
        let config = URLSessionConfiguration.background(withIdentifier: "test");
        // ここのidentiferが同じで、続けてボタン押してDLしようとした場合
        // 2017-07-05 06:58:17.522 NSURLSessionTest[52291:6981685] A background URLSession with identifier test already exists!
        config.sessionSendsLaunchEvents = true;
        // config.isDiscretionary = true;
        
        // URLSessionのコンストラクタでDelegateの設定をすること
        // let session = URLSession(configuration: config);
        let session = URLSession(configuration: config, delegate: self, delegateQueue: nil);
        let url = URL(string: "https://some.page.html");
        
        // let config = URLSessionConfiguration.background(withIdentifier: "test"); <- URLSessionConfiguration.background.default の変更
        // 上記変更はバックグラウンドで処理ができるようにするため
        // この場合は下記のようにタスクを生成する際に、Completion Handlerを渡すのではなく
        // URLSessionTaskDelegateを渡し、レスポンス取得後の処理はDelegateに書く
        // Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.'
        //        let task =  session.downloadTask(with: url!, completionHandler: { (data, response, error) in
        //            DispatchQueue.main.async(execute: {
        //                print(response);
        //                print("DONE");
        //            })
        //        });
        let task =  session.downloadTask(with: url!);
        task.resume();
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    
    // URLSessionTaskDelegateのメソッド
    // データ通信完了時の処理
    // https://developer.apple.com/documentation/foundation/urlsessiontaskdelegate/1411610-urlsession
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if (error != nil) {
            print("HTTPリクエスト失敗処理");
        } else {
            print(task.response);
        }
    }
    
}

参考

久しぶりだったので試しに少し書いて見るだけで力尽きてしまった。もう一回調べたほうがよさそう。 URLSessionTaskDelegate と URLSessionDelegateの違いとか、各Delegateメソッドの違いとか。