Reputation: 351
My iOS app needs a custom clock that is synchronized with my server's clock all the time. All the synchronization logic is done.
My clock is based on mach_absolute_time() from which you can calculate the elapsed time since the device was booted. The problem is that when the device goes into sleep mode (by pressing the screen lock key and having no app running in background) the mach time ticking is paused. When the phone is awakened the mach time ticking resumes but it will not account for the time that the phone has been asleep.
This is how I calculate elapsed time since boot (but it won't account for the time the device is asleep)
- (long long) elapsedTimeMillis {
uint64_t elapsedTimeNano = 0;
mach_timebase_info_data_t timeBaseInfo;
mach_timebase_info(&timeBaseInfo);
elapsedTimeNano = mach_absolute_time() * timeBaseInfo.numer / timeBaseInfo.denom;
return (long long)(elapsedTimeNano / 1000000);
}
And this has exactly the same results:
- (long long) elapsedTimeMillis {
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
return mts.tv_nsec / 1000000 + mts.tv_sec * 1000;
}
NSDate, CFAbsoluteTimeGetCurrent() or alike are not an option because they are affected by device time being sync'ed or manually changed by the user.
Is there ANY way to get time in iOS from a non-stop ticking clock that won't jump back or forth?
Upvotes: 10
Views: 7369
Reputation: 126
Other answers related mostly to Obj-C but I've found an official Swift-based solution included in the standard library since iOS 16.0.
There are two structures to choose from:
ContinuousClock
, which includes suspended timeSuspendingClock
, which pauses when the system goes to sleep.After obtaining two Instances from ContinuousClock.now
, the method duration
may be used to obtain the difference.
let start = ContinuousClock.now;
// Do something
let end = ContinuousClock.now;
let delta = start.duration(end)
Upvotes: 2
Reputation: 31427
There is now a mach_continuous_time
function that includes time while the system is asleep:
Returns current value of a clock that increments monotonically in tick units (starting at an arbitrary point), including while the system is asleep.
Note that it returns the time in ticks, which is inconvenient. You can instead use clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW)
to get nanoseconds.
Here's a complete example in Swift:
import Foundation
let elapsed_time_nanos = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW)
(I don't think it was available back when this question was asked. I'm not sure with which iOS version it was introduced exactly, but it's there on at least iOS 15.)
Upvotes: 1
Reputation: 351
This does the trick:
See: Getting iOS system uptime, that doesn't pause when asleep
- (time_t)uptime
{
struct timeval boottime;
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
time_t now;
time_t uptime = -1;
(void)time(&now);
if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0)
{
uptime = now - (boottime.tv_sec);
}
return uptime;
}
That returns SECONDS since last boot. Device sleeps won't affect it, neither will phone time syncs or phone time changes by the user. Unfortunately it doesn't return the milliseconds or nanoseconds.
Upvotes: 4