4

I need to be able to record reaction time, from when the screen loads or the question label refreshes until the user taps a number button. I'm not finding documentation from Apple on this to be very helpful. NSDate is not accurate enough, I need to measure to milliseconds at least. mach_absolute_time seems to be favored by game designers because it is internally consistent, but it won't work for this application because I need to compare data across devices, and mach_absolute_time is CPU dependent time. This Apple Dev Q&A suggests using NanosecondsToAbsolute and DurationToAbsolute but it's in obj-c and I can't find a swift equivalent documentation.

Is there a swift version of NanosecondsToAbsolute and DurationToAbsolute that I'm just not finding? Some other way to do this consistently?

Here's the code I'm trying to add the times to:

class EmotionQuestionsViewController: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()

//mark "startTime" when view loads
    }

    @IBOutlet var questionLabel: UILabel!
    var timeResultsStack = [String]()

    var questionsStack = ["HAPPY", "ANXIOUS"]
    var questionResultsStack = [String]()
    var questionStackArrayIndex = 1

    @IBAction func RecordValueFromNumericalScaleOneToSeven(sender: UIButton) {

//mark durration time as currentTime - startTime, append to timeResultsStack

        let value = sender.currentTitle!
        questionResultsStack.append(value)

        if questionResultsStack.count < questionsStack.count{
            self.questionLabel.text = "how \(questionsStack[questionStackArrayIndex]) are you right now?"

//mark startTime when label is changed

            self.questionStackArrayIndex++

        }
        else{
            self.performSegueWithIdentifier("showResults", sender: nil)

        }
    }
Ray
  • 131
  • 3
  • 10
  • What do you mean by "NSDate isn't accurate enough, I need at least milliseconds"? NSDate gives you sub microsecond precision. NSTimeInterval is _double_. – gnasher729 Jan 06 '16 at 08:53

3 Answers3

18

As already said, the precision of NSDate() is probably good enough for your purpose. Just for the sake of completeness, mach_absolute_time() from the referenced Technical Q&A QA1398 works in Swift as well:

let t1 = mach_absolute_time()
// do something
let t2 = mach_absolute_time()

let elapsed = t2 - t1
var timeBaseInfo = mach_timebase_info_data_t()
mach_timebase_info(&timeBaseInfo)
let elapsedNano = elapsed * UInt64(timeBaseInfo.numer) / UInt64(timeBaseInfo.denom);
print(elapsedNano)

Possible advantages of this method:

  • Calling mach_absolute_time() seems to be much faster than calling NSDate().timeIntervalSinceReferenceDate, so this method might be better suited to measure extremely short intervals.
  • The value of NSDate() changes if the clock is adjusted, mach_absolute_time() does not have this problem.
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Just curious; your claim that "_the precision of `NSDate()` is probably good enough for your purpose_" sounds to me there might be a even more precise method in retrieving time / date. May I ask: what would you personally use if precision is of interest regarding retrieval of time / date? Thanks. – Unheilig Apr 12 '15 at 03:17
  • 1
    @Unheilig: I said "probably" because I don't know if there is a difference in the accuracy of both methods. I added this answer only because OP explicitly asked for mach_absolute_time. – But I have added some information about the possible advantages. – Martin R Apr 12 '15 at 07:21
3

NSDate is tied to the realtime clock on iOS and Mac devices, and has sub-millisecond accuracy.

Use NSDate's timeIntervalSinceReferenceDate method to convert an NSDate (or the current time) to a double that is the number of seconds since January 1, 2001, including fractional seconds. Once you do that you're free to do math on the doubles that you get back.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
0

You can use NSTimeInterval to measure time (much better than a timer). You just need to store two dates (two points in time) and subtract endTime - StartTime as follow:

import UIKit

class ViewController: UIViewController {

    var startTime: TimeInterval = 0
    var endTime: TimeInterval = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        startTime = Date().timeIntervalSinceReferenceDate
    }

    @IBAction func stopTimeAction(_ sender: Any) {
        endTime = Date().timeIntervalSinceReferenceDate
        print((endTime-startTime).time)
    }

}

extension TimeInterval {
    var time: String { .init(format: "%d:%02d:%02d.%03d", Int(self/3600),
                        Int((self/60).truncatingRemainder(dividingBy: 60)),
                        Int(truncatingRemainder(dividingBy: 60)),
                        Int((self*1000).truncatingRemainder(dividingBy: 1000))) }
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571