10
10

More than 5 years have passed since last update.

widgetPerformUpdateWithCompletionHandler()の挙動

Last updated at Posted at 2014-10-25

以下はiOS8.1で試した結果です。

NCWidgetProvidingを実装したViewController(以下WidgetViewController)は、Widgetの表示時に生成され、ユーザがWidgetを閉じると破棄される。
しかし、widgetPerformUpdateWithCompletionHandler()のcompletionHandlerを呼ぶ前にWidgetを閉じると、completionHandlerが呼ばれるまで破棄を待つ。約10秒待ってもcompletionHandlerが呼ばれない場合、そのままWidgetViewControllerを破棄する。
この状態になると、その後でcompletionHandlerを呼んでも、次回以降二度とwidgetPerformUpdateWithCompletionHandler()は呼ばれなくなる。
これを回避するためにいろいろ試した。

  • selfをキャプチャして破棄を防いでみる
    func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
        NSLog("widgetPerformUpdateWithCompletionHandler")
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
            NSThread.sleepForTimeInterval(20)
            dispatch_async(dispatch_get_main_queue()) {
                self.finish(completionHandler)
            }
        }
    }

    func finish(completionHandler: ((NCUpdateResult) -> Void)!) {
        println("completionHandler")
        completionHandler(.NoData)
    }

これだとcompletionHandlerを呼んだ後にselfが破棄されるが、widgetPerformUpdateWithCompletionHandler()が呼ばれなくなる現象は直らない。
たぶんシステムがWidgetViewControllerへの参照を外した時点でcompletionHandlerが呼ばれていなければダメなのでは。

  • viewWillDisappear()でcompletionHandlerを呼ぶ
    private var handler: ((NCUpdateResult) -> Void)?

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)

        if let block = handler {
            NSLog("viewWillDisappear completionHandler")
            block(.NoData)
            handler = nil
        }
    }

    func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {
        NSLog("widgetPerformUpdateWithCompletionHandler")
        handler = completionHandler
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { [weak self] in
            NSThread.sleepForTimeInterval(20)
            dispatch_async(dispatch_get_main_queue()) {
                NSLog("completionHandler")
                if let block = self?.handler {
                    NSLog("block")
                    block(.NoData)
                    self?.handler = nil
                }
            }
        }
    }

これだとWidgetを閉じたときにcompletionHandlerを呼ぶことができ、widgetPerformUpdateWithCompletionHandler()が呼ばれなくなることを回避できる。
実際はNSThread.sleepForTimeInterval()の部分に更新処理が入るのだが、viewWillDisappear()でキャンセルするのがよさそう。

  • まとめ

widgetPerformUpdateWithCompletionHandler()のcompletionHandlerは、Widgetが非表示になってから約10秒以内に呼ぶ必要がある。そうしなければそれ以降widgetPerformUpdateWithCompletionHandler()は呼ばれなくなる。
この仕様はOSのアップデートで変わる可能性があるので、更新処理はviewWillAppear()でやったほうが無難かも。

10
10
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
10
10