How do I implement per-page handoff in a WatchKit app on Watch OS 1.0.1?

322 views Asked by At

Watch OS 1.0.1 changed the lifecycle for page-based apps: now willActivate/didActivate get called for the next page to preload it.

I was previously calling updateUserActivity:userInfo:webpageURL: in each of my page's willActivate methods, supplying different userInfo for each page. But now that userInfo gets stepped on by the next page's call to the same method in willActivate.

Any idea how to get around this?

2

There are 2 answers

3
romrom On

I think I have a better solution. It consists in maintaining a Bool in each page controller that tells whether the controller is active (displayed), and in keeping a value for the NSUserActivity that needs to be updated to the OS

private var isActive:Bool = false {
    didSet(oldValue) {
        if oldValue != isActive {
            resolveHandoff()
        }
    }
}

private var currentUserActivity: NSUserActivity? {
    didSet(oldUserActivity) {
        if oldUserActivity == nil && currentUserActivity != nil
        || oldUserActivity != nil && currentUserActivity == nil
        || oldUserActivity != currentUserActivity {
            resolveHandoff()
        }
    }
}

override func willActivate() {
    super.willActivate()
    self.isActive = true
}

override func didDeactivate() {
    super.didDeactivate()
    self.isActive = false
}

private func resolveHandoff() {
    if !self.isActive {
        return
    }

    if let activity = currentUserActivity {
        self.updateUserActivity(activity.activityType, userInfo: activity.userInfo, webpageURL: nil)
    } else {
        self.invalidateUserActivity()
    }
}

Now you can «tag» your user activity wherever you want in the interface controller code by simply assigning a value for currentUserActivity. For instance it can be in the awakeWithContext: method

override func awakeWithContext(context: AnyObject?) {
    super.awakeWithContext(context)
    let ua = NSUserActivity(activityType: "com.mycompany.doingSomething")
    ua.userInfo = ["My key": "My value"]
    self.currentUserActivity = ua
}

The user activity will be reported only if currentUserActivity is set and the controller is active (displayed). Note that the way it is implemented above allows you to invalidateUserActivity by simply setting self.currentUserActivity = nil when your controller is active.

1
Hilton Campbell On

I ended up scheduling a timer for 1 second in the future in willActivate, then invalidating the timer in didActivate. When the timer fires, it calls updateUserActivity:userInfo:webpageURL:. I'm not too happy about the arbitrary 1 second interval... I tried 0.5 seconds first, but that was too short (I guess WatchKit is really that slow?).