I have a countdown timer interface controller that will, once the timer gets down to 00:00, launch another interface controller. If I keep the watch active until the timer reaches 00:00, then the second interface controller launches as it should. However, if the watch goes to sleep, even if it's active right before the timer reaches 00:00, there will be a delay of several seconds to over a minute before the second interface controller launches.
This defect doesn't appear when running in the watch simulator, just when I'm running on the actual device.
I'm using Xcode 8 and swift 3.
Here's my code from the first interface controller:
// this func will update the countdown timer
@objc private func updateTimer() {
totalNumberOfSeconds += 1
numberOfSeconds += 1
if (numberOfSeconds == numSecondsInMinute) {
numberOfSeconds = 0
}
// only attempt to open the RacingTimer interface if this IC is visible
if (isStillVisible) {
// change to the Racing Timer if the countdown timer hits 00:00
if (totalNumberOfSeconds > originalSecondsTimeInterval) {
// the watch must have gone to sleep when the countdown timer
// hit 00:00, so the total num secs is past the orig timer
// set the numberOfSeconds to total - original to pass to RacingTimer
numberOfSeconds = totalNumberOfSeconds - originalSecondsTimeInterval
// launch the racing timer
WKInterfaceController.reloadRootControllers(withNames: ["RacingTimer"], contexts: [numberOfSeconds])
// destroy the timer and reset the vars
countdownClock.invalidate()
numberOfSeconds = 0
totalNumberOfSeconds = 0
} else if (totalNumberOfSeconds == originalSecondsTimeInterval) {
// launch the racing timer
WKInterfaceController.reloadRootControllers(withNames: ["RacingTimer"], contexts: nil)
// destroy the timer and reset the vars
countdownClock.invalidate()
numberOfSeconds = 0
totalNumberOfSeconds = 0
}
}
}
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// get race and timer data
let numSecs = raceDS.timer * 60
originalSecondsTimeInterval = numSecs
cdt = NSDate(timeIntervalSinceNow: TimeInterval(numSecs))
countdownTimer.setDate(cdt as Date)
countdownClock = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
countdownTimer.start()
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
nearestMinuteButtonOutlet.setTitle("will activate") // debug only
didAppear()
}
// set the visible boolean to true
override func didAppear() {
super.didAppear()
isStillVisible = true
nearestMinuteButtonOutlet.setTitle("did appear") // debug only
}
// set the boolean to false
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
isStillVisible = false
nearestMinuteButtonOutlet.setTitle("sleeping") // debug only
}
I'm really at a loss as to why there's a delay if the watch goes to sleep. Any help would be greatly appreciated. TIA.
As of watchOS3
there is no solution to have a Timer
running in the background. Timer
objects shouldn't be used for precise time measurements on iOS
either. On iOS
you have the alternative to use a CADisplayLink
for accurate timings, however, this isn't available on watchOS3/4
.
For measuring time in the background, you should save the current date before the app is going to background and calculate the elapsed time when the app is launched again.
If you simply need the other InterfaceController
to be visible by the time the user opens your app, you can use the the method described using dates and you can navigate to your other InterfaceController
as soon as the user opens your app again.
If you need some code to execute when the countdown is finished, you should rather schedule a background task, they are the only methods of running code in the background on watchOS
at the moment.