0

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?

Hilton Campbell
  • 6,065
  • 3
  • 47
  • 79

2 Answers2

0

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?).

Hilton Campbell
  • 6,065
  • 3
  • 47
  • 79
0

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.

romrom
  • 642
  • 1
  • 6
  • 14
  • I don't think this will work. (1) Page 1 sets active to `true`, updates user activity, (2) page 2 sets active to `true`, updates user activity, (3) page 2 sets active to `false`, does nothing. At this point, the user activity is still page 2's. – Hilton Campbell Jul 26 '15 at 22:48
  • The purpose of the code above is that each page reports a user activity when it's active. So when page 2 sets active to false, another page sets active to true and the user activity is updated. The code is not meant to be used when a page is supposed to sometimes report its activity when displayed, sometimes not. – romrom Jul 28 '15 at 07:26
  • The series of events I gave matches the [new lifecycle](http://blog.mikeswanson.com/post/119399948909/watch-os-1-0-1-controller-life-cycle-changes) – Hilton Campbell Aug 03 '15 at 17:57