a relatively simple question that I've not been able to find a clear answer to. My app is more complex, but answering this question will suffice.
Suppose you're writing a stopwatch app. When the user taps "start", the app stores the current date and time in startTime:
startTime = [NSDate date];
When the user tapes "stop", the app stores the current date and time in stopTime:
stopTime = [NSDate date];
The duration is calculated by:
duration = [stopTime timeIntervalSinceDate:startTime];
and is displayed with something like:
[durationLabel setText:[NSString stringWithFormat:#"%1.2f", duration]];
The typical durations that my app is timing range from 2 to 50 seconds. I need accuracy to 1/100th of a second (e.g. 2.86 seconds).
I'm assuming that there is some protocol that iOS devices use to keep their clocks accurate (cellular or NTP sources?). My concern is that between starting and stopping the stopwatch, the clock on the iOS device is updated which can result in a shift of the current date/time either ahead or back. If this were to happen, the duration calculated would be inaccurate.
I've seen a few posts relating to timing methods for purposes of improving code efficiency. Some suggest using mach_time.h functions, which I'm not familiar with. It's not obvious to me which is the best approach to use.
Is it possible to disable iOS from updating the date & time? Is mach_absolute_time() unaffected by iOS clock updates?
Many thanks!
Tim
You are correct in thinking that CFAbsoluteTime and its derivatives (NSDate dateand so on) are potentially skewed by network updates on 'real' time. Add that to the fact that NSTimer has an accuracy of 50-100ms and you have a timer that is not suited to the most critical of time-sensitive operations.
The answer to this problem seems to be CACurrentMediaTime.
It is a member of the Core Animation group, but there shouldn't be any problem integrating it into non-animation based applications.
CACurrentMediaTime is a wrapper of mach_absolute_time() and makes sense of the "mach absolute time unit," which from my understanding is no fun to tinker with. mach_absolute_time() is calculated by running a non-network synced timer since the device was last booted.
There is relatively little information on CACurrentMediaTime but here are some sources and further reading:
Apple's sparse documentation of CACurrentMediaTime
Stack Overflow - NSTimer vs CACurrentMediaTime()
http://bendodsonapps.com/weblog/2013/01/29/ca-current-media-time/
http://blog.spacemanlabs.com/2011/09/all-in-the-timing-keeping-track-of-time-passed-on-ios/
http://forum.sparrow-framework.org/topic/accurate-timer
Note: If you do use CACurrentMediaTime, make sure you include and link the QuartzCore.framework
Check out this here. I would say forget about the current time check and use a precision timer since it won't rely on the current time but instead uses an interval.
Related
To get accurate time measurements on iOS, mach_absolute_time() should be used. Or CACurrentMediaTime(), which is based on mach_absolute_time(). This is documented in this Apple Q&A, and also explained in several StackOverflow answers (e.g. https://stackoverflow.com/a/17986909, https://stackoverflow.com/a/30363702).
When does the value returned by mach_absolute_time() wrap around? When does the value returned by CACurrentMediaTime() wrap around? Does this happen in any realistic timespan? The return value of mach_absolute_time() is of type uint64, but I'm unsure about how this maps to a real timespan.
The document you reference notes that mach_absolute_time is CPU dependent, so we can't say how much time must elapse before it wraps. On the simulator, mach_absolute_time is nanoseconds, so if it's wrapping at UInt64.max, that translates to 585 years. On my iPhone 7+, it's 24,000,000 mac_absolute_time per second, which translates to 24 thousand years. Bottom line, the theoretical maximum amount of time captured by mach_absolute_time will vary based upon CPU, but you won't ever encounter this in any practical application.
For what it's worth, consistent with those various posts you found, the CFAbsoluteTimeGetCurrent documentation warns that:
Repeated calls to this function do not guarantee monotonically increasing results. The system time may decrease due to synchronization with external time references or due to an explicit user change of the clock.
So, you definitely don't want to use NSDate/Date or CFAbsoluteTimeGetCurrent if you want accurate elapsed times. Neither ensures monotonically increasing values.
In short, when I need that sort of behavior, I generally use CACurrentMediaTime, because it enjoy the benefits of mach_absolute_time, but it converts it to seconds for me, which makes it very simple to use. And neither it nor mach_absolute_time are going to loop in any realistic time period.
SO basically all is in the title. I've searched quite a lot, but didn't find any right solution which doesn't require internet connection.
If the user changes time in settings - i can't find real time since last launch.
I need that for my game, in it for every hour, even when you don't play the game, you get some coins.
If the user changes time in settings - that affect the time in NSDate() and user can cheat with coins.
So save the NSDate() to user defaults on app launch. The next time the app comes to the foreground, or gets launched again, get the current NSDate and subtract the saved date from it. That will give you the number of seconds between the two dates. Calculating hours from seconds is a simple matter of dividing by 3600. – Duncan C just now edit
EDIT:
Note that in newer versions of Swift (starting with Swift 2?) Most Foundation classes were defined as native Swift classes without the NS prefix. For newer versions of swift, replace all occurrences of NSDate with Date in the above.
Also note that in iOS ≥ 7.0, the Calendar has some methods that make this sort of calculation neater and easier. There's a new method dateComponents(_:from:to:) that lets you calculate the difference between 2 dates in whatever units you want. You could use that to calculate the seconds between the 2 dates more cleanly than calculating seconds, as outlined in my original answer. Calendar methods also tend to handle boundary conditions like spanning daylight savings time, leap seconds, etc.
Consider the following Swift 4/5 playground code:
import UIKit
let now = Date()
let randomSeconds = Double.random(in: 100000...3000000)
let later = now + randomSeconds
if let difference = Calendar.current.dateComponents([.second],
from: now,
to: later)
.second {
print(difference)
Try this.
Step 1. When user exits game. Set a NSUserDefault with current time.
Step 2. When app launches, in your appDelagate file, get this value.
Step 3. Calculate diff between and award coins accordingly.
This question already has answers here:
What kind of logarithm functions / methods are available in objective-c / cocoa-touch?
(3 answers)
Closed 9 years ago.
I am building a game using Sprite Kit and I want to gradually increase the difficulty (starting at 1.0) based on the time since starting the game.
Someone suggested that I should use a logarithmic calculation for this but I'm unsure how to implement this in Objective-C.
- (float)difficulty
{
timeSinceStart = ???; // I don't what kind of object this should be to make it play nice w/ `log`
return log(???);
}
Update #1
I know that I need to use the log method but I'm uncertain what values I need to pass to it.
Objective C is a superset of the C language, therefore you can use "math.h".
The function that computes the natural logarithm from "math.h" is double log(double x);
EDIT
Since you want the difficulty to increase as a function of time, you would pass the time as the argument to log(double x). How you would use that to calculate and change the "difficulty" is an entirely different question.
If you want to change the shape of the curve, either multiply the expression by a constant, as in 2*log(x) or multiply the parameter by a constant, as in log(2*x). You will have to look at the individual curves to see what will work best for your specific application.
Since log(1.0) == 0 you probably want to do some scaling and translation. Use something like 1.0 + log(1.0 + c * time). At time zero this will give a difficulty of 1.0, and as time advances the difficulty will increase at a progressively slower pace whose rate is determined by c. Small values such as c = 0.01 will give a slow ramp-up, larger values will ramp-up faster.
#pjs gave a pretty clear answer. As to how to figure out the time: You probably want the amount of time spent actually playing, rather than elapsed time since launching the game.
So you will nee to track total time played, game after game.
I suggest you create an entry in NSUserDefaults. You can save and load double values to user defaults using the NSUserDefaults methods setDouble:forKey: and doubleForKey:
I would create an instance variable startPlayingTime, type double
When you start the game action running, capture the start time using
startPlayingTime = [NSDate timeIntervalSinceReferenceDate];
When the user passes the game/exits to the background, use
NSTimeInterval currentPlayTime = [NSDate timeIntervalSinceReferenceDate] - startPlayingTime;
Then read the total time played from user defaults, add currentPlayTime to it, and save it back to user defaults.
You can then make your game difficulty based on
difficulty = log(1+ c * totalPlayTime);
(as explained by pjs, above) and pick some appropriate value for C.
I'm trying to calculate the time interval for a given CMMotionActivity.
A CMMotionActivity is a CMLogItem subclass, and as such - has a the property: #property(readonly, nonatomic) NSTimeInterval timestamp.
This time stamp is defined as:
The time stamp is the amount of time in seconds since the phone
booted.
CMMotionActivity also has the property #property(readonly, nonatomic) NSDate *startDate.
I'm trying to figure out what is the interval for a given activity, aiming to come up with how much time the user was running or walking.
I'm using historical data (not reading events in real time).
I tried calculating the difference between motion events, but that seems wrong as I'm getting events in 5 second intervals but sometimes in much larger intervals.
Is there a way to calculate the "endDate" for a given CMMotionActivity?
I can't seem to find a way to do that in a reliable method either by observing a single event or a chain of events.
I believe a CMMotionActivity is generated every time the state of motion changes. Assuming this, given two CMMMotionActivity objects you can calculate the duration between the two events thereby determining how long the activity of stationary/walking/running/driving/uknowning was.
At least as of iOS 14, each CMMotionActivity appears to cover a period that is ended by the next CMMotionActivity record's .startDate.
Sometimes that next record will be another record with an indicated activity classification. At other times, especially if the classifier can't decide what the next activity is but knows the preceding activity has ended it will be a record with none of the activity bits set that only serves to end the preceding record.
For instance, record #1's stationary period below is ended by the unclassified record #2, whereas record #3's walking period is ended by record #4's stationary period starting:
CMMotionActivity <startDate,2021-02-22 12:01:22 +0000,confidence,2,unknown,0,stationary,1,walking,0,running,0,automotive,0,cycling,0>
CMMotionActivity <startDate,2021-02-22 12:51:17 +0000,confidence,2,unknown,0,stationary,0,walking,0,running,0,automotive,0,cycling,0>
CMMotionActivity <startDate,2021-02-22 12:51:24 +0000,confidence,2,unknown,0,stationary,0,walking,1,running,0,automotive,0,cycling,0>
CMMotionActivity <startDate,2021-02-22 12:52:08 +0000,confidence,2,unknown,0,stationary,1,walking,0,running,0,automotive,0,cycling,0>
I have to display a timer in 10th second for a sport competition. I have do this using the OnTimer event of a TTimer. the interval is set to 100. My routine display the current min:sec.10th (ex.: 02:45.7 ) correctly but it seem that my timer loose about 4 second at each minutes if I comp. to normal clock.
There is a better way to get a time accuracy timer in Delphi XE2 (or XE3) ?
You can use a timer to display the current value of the clock, but use a different approach to calculate the elapsed time.
You have to know that Windows timers are not time accurate, and even if you set it to elapse every 100 milliseconds, it can take more to fire the OnTimer event and even it can miss some intervals if for some reason elapses two or more times before your application process it.
You can, for example, use the system high-resolution performance counter to track times with nano-second accuracy.
You can also use the Delphi TStopwatch class, which encapsulates the system calls and falls back to other method (GetTickCount) if the high resolution performance counter is not available in your machine.
Take also a look at the How to Accurately Measure Elapsed Time Using High-Resolution Performance Counter delphi.about.com article.