You Cannot Trust the Simulator
The iOS Simulator runs on your Mac's CPU. Timers in the simulator are suspiciously accurate because they are backed by a desktop processor that is not juggling cellular radios, thermal throttling, or aggressive power management. Real devices lie in different and more interesting ways.
My Testing Methodology
When I built BoxTime, I needed to verify that the timer was accurate across a range of devices. I set up a simple test: start a 3-minute round, compare the app's completion time against an external reference (a stopwatch on another device).
The Test Harness
#if DEBUG
class TimerAccuracyLogger {
private var roundStartTime: Date?
private var expectedDuration: TimeInterval = 0
func roundStarted(expectedDuration: TimeInterval) {
self.roundStartTime = Date()
self.expectedDuration = expectedDuration
}
func roundEnded() {
guard let start = roundStartTime else { return }
let actual = Date().timeIntervalSince(start)
let drift = actual - expectedDuration
print("Expected: \(expectedDuration)s, Actual: \(String(format: "%.4f", actual))s, Drift: \(String(format: "%.4f", drift))s")
}
}
#endif
I ran this across five devices: iPhone SE (2nd gen), iPhone 12, iPhone 14 Pro, iPhone 15, and an iPad Air. Each device ran a full 12-round, 3-minute workout with 1-minute rest periods.
Results
| Device | Per-Round Drift | Total Drift (36 min) |
|---|---|---|
| iPhone SE (2nd gen) | < 1ms | < 12ms |
| iPhone 12 | < 1ms | < 12ms |
| iPhone 14 Pro | < 0.5ms | < 6ms |
| iPhone 15 | < 0.5ms | < 6ms |
| iPad Air (5th gen) | < 1ms | < 12ms |
With the absolute-time-anchored approach (computing remaining time from Date() rather than counting ticks), drift is negligible on all devices. The sub-millisecond drift comes from the time between the Date() call and the actual notification/haptic firing.
Where Things Go Wrong
Thermal Throttling
I ran the test while the iPhone SE was charging in a warm room. The device thermal-throttled, and frame rates dropped. But the timer accuracy was unaffected because it is not tied to frame rate. The display update was choppy, but the round ended at the correct time.
Low Power Mode
Low Power Mode reduces CPU frequency and limits background activity. Timer accuracy was unaffected in the foreground. However, if the app moves to the background while Low Power Mode is active, iOS is more aggressive about suspending it. The local notification fallback handles this.
App Backgrounding
This is the real threat to accuracy. When the app goes to the background, iOS can suspend it within seconds. My test:
- Start a 3-minute round
- Switch to Safari for 30 seconds
- Return to BoxTime
Result: the timer correctly shows the right remaining time because it recalculates from the absolute end time on every frame after returning to foreground.
func sceneDidBecomeActive(_ scene: UIScene) {
// Timer automatically shows correct time because remainingTime
// is computed from Date(), not from accumulated ticks
// No manual state restoration needed for the display
}
Edge Case: System Clock Changes
What if the user changes their system clock mid-workout? This could theoretically break an absolute-time approach. I handle it with ProcessInfo.processInfo.systemUptime as a sanity check:
func validateTimeConsistency() {
let wallClockElapsed = Date().timeIntervalSince(workoutStartDate)
let uptimeElapsed = ProcessInfo.processInfo.systemUptime - workoutStartUptime
// If wall clock and uptime disagree by more than 2 seconds,
// the system clock was probably changed
if abs(wallClockElapsed - uptimeElapsed) > 2.0 {
// Fall back to uptime-based calculation
recalibrateFromUptime()
}
}
This is an edge case I have never seen in production, but it costs five lines of code to handle.
The Takeaway
Timer accuracy on iOS is a solved problem if you use the right architecture. Anchor to absolute time, use CADisplayLink for display updates, and handle backgrounding gracefully. Testing on real devices confirms what the theory predicts -- the approach works across the full range of iOS hardware.
BoxTime has been used for thousands of workout sessions with zero reported timing issues.
Top comments (0)