How can we measure the time elapsed for running a function in Swift? I am trying to display the elapsed time like this: "Elapsed time is .05 seconds". Saw that in Java, we can use System.nanoTime(), are there any equivalent methods available in Swift to accomplish this?
Please have a look at the sample program:
func isPrime(_ number: Int) -> Bool {
var i = 0;
for i=2; i<number; i++ {
if number % i == 0, i != 0 {
return false
}
}
return true
}
var number = 5915587277
if isPrime(number) {
print("Prime number")
} else {
print("NOT a prime number")
}
Update
With Swift 5.7, everything below becomes obsolete. Swift 5.7 introduces the concept of a Clock which has a function designed to do exactly what is required here.
There are two concrete examples of a Clock provided: ContinuousClock and SuspendingClock. The former keeps ticking when the system is suspending and the latter does not.
The following is an example of what to do in Swift 5.7
func doSomething()
{
for i in 0 ..< 1000000
{
if (i % 10000 == 0)
{
print(i)
}
}
}
let clock = ContinuousClock()
let result = clock.measure(doSomething)
print(result) // On my laptop, prints "0.552065882 seconds"
It also allows you to measure closures directly, of course
let clock = ContinuousClock()
let result = clock.measure {
for i in 0 ..< 1000000
{
if (i % 10000 == 0)
{
print(i)
}
}
}
print(result) // "0.534663798 seconds"
Pre Swift 5.7
Here's a Swift function I wrote to measure Project Euler problems in Swift
As of Swift 3, there is now a version of Grand Central Dispatch that is "swiftified". So the correct answer is probably to use the DispatchTime API.
My function would look something like:
// Swift 3
func evaluateProblem(problemNumber: Int, problemBlock: () -> Int) -> Answer
{
print("Evaluating problem \(problemNumber)")
let start = DispatchTime.now() // <<<<<<<<<< Start time
let myGuess = problemBlock()
let end = DispatchTime.now() // <<<<<<<<<< end time
let theAnswer = self.checkAnswer(answerNum: "\(problemNumber)", guess: myGuess)
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds // <<<<< Difference in nano seconds (UInt64)
let timeInterval = Double(nanoTime) / 1_000_000_000 // Technically could overflow for long running tests
print("Time to evaluate problem \(problemNumber): \(timeInterval) seconds")
return theAnswer
}
Old answer
For Swift 1 and 2, my function uses NSDate:
// Swift 1
func evaluateProblem(problemNumber: Int, problemBlock: () -> Int) -> Answer
{
println("Evaluating problem \(problemNumber)")
let start = NSDate() // <<<<<<<<<< Start time
let myGuess = problemBlock()
let end = NSDate() // <<<<<<<<<< end time
let theAnswer = self.checkAnswer(answerNum: "\(problemNumber)", guess: myGuess)
let timeInterval: Double = end.timeIntervalSinceDate(start) // <<<<< Difference in seconds (double)
println("Time to evaluate problem \(problemNumber): \(timeInterval) seconds")
return theAnswer
}
Note that using NSdate for timing functions is discouraged: "The system time may decrease due to synchronization with external time references or due to an explicit user change of the clock.".
This is a handy timer class based on CoreFoundations CFAbsoluteTime:
import CoreFoundation
class ParkBenchTimer {
let startTime: CFAbsoluteTime
var endTime: CFAbsoluteTime?
init() {
startTime = CFAbsoluteTimeGetCurrent()
}
func stop() -> CFAbsoluteTime {
endTime = CFAbsoluteTimeGetCurrent()
return duration!
}
var duration: CFAbsoluteTime? {
if let endTime = endTime {
return endTime - startTime
} else {
return nil
}
}
}
You can use it like this:
let timer = ParkBenchTimer()
// ... a long runnig task ...
print("The task took \(timer.stop()) seconds.")
Use clock, ProcessInfo.systemUptime, or DispatchTime for simple start-up time.
There are, as far as I know, at least ten ways to measure elapsed time:
Monotonic Clock based:
ProcessInfo.systemUptime.
mach_absolute_time with mach_timebase_info as mentioned in this
answer.
clock() in POSIX standard.
times() in POSIX standard. (Too complicated since we need
to consider user-time v.s. system-time, and child processes are
involved.)
DispatchTime (a wrapper around Mach time API) as mentioned by JeremyP in accepted answer.
CACurrentMediaTime().
Wall Clock based:
(never use those for metrics: see below why)
NSDate/Date as mentioned by others.
CFAbsoluteTime as mentioned by others.
DispatchWallTime.
gettimeofday() in POSIX standard.
Option 1, 2 and 3 are elaborated below.
Option 1: Process Info API in Foundation
do {
let info = ProcessInfo.processInfo
let begin = info.systemUptime
// do something
let diff = (info.systemUptime - begin)
}
where diff:NSTimeInterval is the elapsed time by seconds.
Option 2: Mach C API
do {
var info = mach_timebase_info(numer: 0, denom: 0)
mach_timebase_info(&info)
let begin = mach_absolute_time()
// do something
let diff = Double(mach_absolute_time() - begin) * Double(info.numer) / Double(info.denom)
}
where diff:Double is the elapsed time by nano-seconds.
Option 3: POSIX clock API
do {
let begin = clock()
// do something
let diff = Double(clock() - begin) / Double(CLOCKS_PER_SEC)
}
where diff:Double is the elapsed time by seconds.
Why Not Wall-Clock Time for Elapsed Time?
In documentation of CFAbsoluteTimeGetCurrent:
Repeated calls to this function do not guarantee monotonically
increasing results.
Reason is similar to currentTimeMillis vs nanoTime in Java:
You can't use the one for the other purpose. The reason is that no
computer's clock is perfect; it always drifts and occasionally
needs to be corrected. This correction might either happen
manually, or in the case of most machines, there's a process that
runs and continually issues small corrections to the system clock
("wall clock"). These tend to happen often. Another such correction
happens whenever there is a leap second.
Here CFAbsoluteTime provides wall clock time instead of start-up
time. NSDate is wall clock time as well.
Swift 4 shortest answer:
let startingPoint = Date()
// ... intensive task
print("\(startingPoint.timeIntervalSinceNow * -1) seconds elapsed")
It will print you something like 1.02107906341553 seconds elapsed (time of course will vary depending on the task, I'm just showing this for you guys to see the decimal precision level for this measurement).
Hope it helps someone in Swift 4 from now on!
Update
If you want to have a generic way of testing portions of code, I'd suggest the next snippet:
func measureTime(for closure: #autoclosure () -> Any) {
let start = CFAbsoluteTimeGetCurrent()
closure()
let diff = CFAbsoluteTimeGetCurrent() - start
print("Took \(diff) seconds")
}
Usage
measureTime(for: <insert method signature here>)
Console log
Took xx.xxxxx seconds
Just Copy and Paste this function. Written in swift 5.
Copying JeremyP here.
func calculateTime(block : (() -> Void)) {
let start = DispatchTime.now()
block()
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
let timeInterval = Double(nanoTime) / 1_000_000_000
print("Time: \(timeInterval) seconds")
}
Use it like
calculateTime {
exampleFunc()// function whose execution time to be calculated
}
let start = NSDate()
for index in 1...10000 {
// do nothing
}
let elapsed = start.timeIntervalSinceNow
// elapsed is a negative value.
You could create a time function for measuring you calls.
I am inspired by Klaas' answer.
func time <A> (f: #autoclosure () -> A) -> (result:A, duration: String) {
let startTime = CFAbsoluteTimeGetCurrent()
let result = f()
let endTime = CFAbsoluteTimeGetCurrent()
return (result, "Elapsed time is \(endTime - startTime) seconds.")
}
This function would allow you to call it like this time (isPrime(7)) which would return a tuple containing the result and a string description of the elapsed time.
If you only wish the elapsed time you can do this time (isPrime(7)).duration
It looks like iOS 13 introduced a new API to use with DispatchTime that removes a need to calculate the difference between two timestamps manually.
distance(to:)
let start: DispatchTime = .now()
heavyTaskToMeasure()
let duration = start.distance(to: .now())
print(duration)
// prints: nanoseconds(NUMBER_OF_NANOSECONDS_BETWEEN_TWO_TIMESTAMPS)
Sadly the documentation is not provided, but after doing some tests it looks like the .nanoseconds case is always returned.
With a simple extension you could convert the DispatchTimeInterval to TimeInterval. credit
extension TimeInterval {
init?(dispatchTimeInterval: DispatchTimeInterval) {
switch dispatchTimeInterval {
case .seconds(let value):
self = Double(value)
case .milliseconds(let value):
self = Double(value) / 1_000
case .microseconds(let value):
self = Double(value) / 1_000_000
case .nanoseconds(let value):
self = Double(value) / 1_000_000_000
case .never:
return nil
}
}
}
Simple helper function for measuring execution time with closure.
func printExecutionTime(withTag tag: String, of closure: () -> ()) {
let start = CACurrentMediaTime()
closure()
print("#\(tag) - execution took \(CACurrentMediaTime() - start) seconds")
}
Usage:
printExecutionTime(withTag: "Init") {
// Do your work here
}
Result:
#Init - execution took 1.00104497105349 seconds
you can measure the nanoseconds like e.g. this:
let startDate: NSDate = NSDate()
// your long procedure
let endDate: NSDate = NSDate()
let dateComponents: NSDateComponents = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian).components(NSCalendarUnit.CalendarUnitNanosecond, fromDate: startDate, toDate: endDate, options: NSCalendarOptions(0))
println("runtime is nanosecs : \(dateComponents.nanosecond)")
I use this:
public class Stopwatch {
public init() { }
private var start_: NSTimeInterval = 0.0;
private var end_: NSTimeInterval = 0.0;
public func start() {
start_ = NSDate().timeIntervalSince1970;
}
public func stop() {
end_ = NSDate().timeIntervalSince1970;
}
public func durationSeconds() -> NSTimeInterval {
return end_ - start_;
}
}
I don't know if it's more or less accurate than previously posted. But the seconds have a lot of decimals and seem to catch small code changes in algorithms like QuickSort using swap() vs. implementing swap urself etc.
Remember to crank up your build optimizations when testing performance:
Here is my try for the simplest answer:
let startTime = Date().timeIntervalSince1970 // 1512538946.5705 seconds
// time passes (about 10 seconds)
let endTime = Date().timeIntervalSince1970 // 1512538956.57195 seconds
let elapsedTime = endTime - startTime // 10.0014500617981 seconds
Notes
startTime and endTime are of the type TimeInterval, which is just a typealias for Double, so it is easy to convert it to an Int or whatever. Time is measured in seconds with sub-millisecond precision.
See also DateInterval, which includes an actual start and end time.
Using the time since 1970 is similar to Java timestamps.
The recommend way to check elapsed time/performance is using the measure function that is available in XCTests.
It isn't reliable to write your own measure blocks, since the performance (and therefore execution/elapsed time) of a block of code is influenced by e.g. CPU caches.
The second time a function is invoked, would likely be quicker than the first time it is invoked, although it can vary a few %. Therefore 'benchmarking' by using your own closures (given all over the place here) by executing it once, can give different results than your code being executed in production by real users.
The measure function invokes your block of code several times, mimicking the performance/elapsed time of your code like it is used in production (at least gives more accurate results).
I have borrowed the idea from Klaas to create a lightweight struct to measure running and interval time:
Code Usage:
var timer = RunningTimer.init()
// Code to be timed
print("Running: \(timer) ") // Gives time interval
// Second code to be timed
print("Running: \(timer) ") // Gives final time
The stop function does not have to be called, as the print function will give the time lapsed. It may be called repeatedly to get the time lapsed.
But to stop the timer at certain point in the code use timer.stop() it may also be used to return the time in seconds: let seconds = timer.stop()
After the timer is stopped the interval timer will not, so the print("Running: \(timer) ") will give the correct time even after a few lines of code.
Following is the code for RunningTimer. It is tested for Swift 2.1:
import CoreFoundation
// Usage: var timer = RunningTimer.init()
// Start: timer.start() to restart the timer
// Stop: timer.stop() returns the time and stops the timer
// Duration: timer.duration returns the time
// May also be used with print(" \(timer) ")
struct RunningTimer: CustomStringConvertible {
var begin:CFAbsoluteTime
var end:CFAbsoluteTime
init() {
begin = CFAbsoluteTimeGetCurrent()
end = 0
}
mutating func start() {
begin = CFAbsoluteTimeGetCurrent()
end = 0
}
mutating func stop() -> Double {
if (end == 0) { end = CFAbsoluteTimeGetCurrent() }
return Double(end - begin)
}
var duration:CFAbsoluteTime {
get {
if (end == 0) { return CFAbsoluteTimeGetCurrent() - begin }
else { return end - begin }
}
}
var description:String {
let time = duration
if (time > 100) {return " \(time/60) min"}
else if (time < 1e-6) {return " \(time*1e9) ns"}
else if (time < 1e-3) {return " \(time*1e6) µs"}
else if (time < 1) {return " \(time*1000) ms"}
else {return " \(time) s"}
}
}
Wrap it up in a completion block for easy use.
public class func secElapsed(completion: () -> Void) {
let startDate: NSDate = NSDate()
completion()
let endDate: NSDate = NSDate()
let timeInterval: Double = endDate.timeIntervalSinceDate(startDate)
println("seconds: \(timeInterval)")
}
Static Swift3 class for basic function timing. It will keep track of each timer by name. Call it like this at the point you want to start measuring:
Stopwatch.start(name: "PhotoCapture")
Call this to capture and print the time elapsed:
Stopwatch.timeElapsed(name: "PhotoCapture")
This is the output: *** PhotoCapture elapsed ms: 1402.415125
There is a "useNanos" parameter if you want to use nanos.
Please feel free to change as needed.
class Stopwatch: NSObject {
private static var watches = [String:TimeInterval]()
private static func intervalFromMachTime(time: TimeInterval, useNanos: Bool) -> TimeInterval {
var info = mach_timebase_info()
guard mach_timebase_info(&info) == KERN_SUCCESS else { return -1 }
let currentTime = mach_absolute_time()
let nanos = currentTime * UInt64(info.numer) / UInt64(info.denom)
if useNanos {
return (TimeInterval(nanos) - time)
}
else {
return (TimeInterval(nanos) - time) / TimeInterval(NSEC_PER_MSEC)
}
}
static func start(name: String) {
var info = mach_timebase_info()
guard mach_timebase_info(&info) == KERN_SUCCESS else { return }
let currentTime = mach_absolute_time()
let nanos = currentTime * UInt64(info.numer) / UInt64(info.denom)
watches[name] = TimeInterval(nanos)
}
static func timeElapsed(name: String) {
return timeElapsed(name: name, useNanos: false)
}
static func timeElapsed(name: String, useNanos: Bool) {
if let start = watches[name] {
let unit = useNanos ? "nanos" : "ms"
print("*** \(name) elapsed \(unit): \(intervalFromMachTime(time: start, useNanos: useNanos))")
}
}
}
This is the snippet I came up with and it seems to work for me on my Macbook with Swift 4.
Never tested on other systems, but I thought it's worth sharing anyway.
typealias MonotonicTS = UInt64
let monotonic_now: () -> MonotonicTS = mach_absolute_time
let time_numer: UInt64
let time_denom: UInt64
do {
var time_info = mach_timebase_info(numer: 0, denom: 0)
mach_timebase_info(&time_info)
time_numer = UInt64(time_info.numer)
time_denom = UInt64(time_info.denom)
}
// returns time interval in seconds
func monotonic_diff(from: MonotonicTS, to: MonotonicTS) -> TimeInterval {
let diff = (to - from)
let nanos = Double(diff * time_numer / time_denom)
return nanos / 1_000_000_000
}
func seconds_elapsed(since: MonotonicTS) -> TimeInterval {
return monotonic_diff(from: since, to:monotonic_now())
}
Here's an example of how to use it:
let t1 = monotonic_now()
// .. some code to run ..
let elapsed = seconds_elapsed(since: t1)
print("Time elapsed: \(elapsed*1000)ms")
Another way is to do it more explicitly:
let t1 = monotonic_now()
// .. some code to run ..
let t2 = monotonic_now()
let elapsed = monotonic_diff(from: t1, to: t2)
print("Time elapsed: \(elapsed*1000)ms")
This is how I wrote it.
func measure<T>(task: () -> T) -> Double {
let startTime = CFAbsoluteTimeGetCurrent()
task()
let endTime = CFAbsoluteTimeGetCurrent()
let result = endTime - startTime
return result
}
To measure a algorithm use it like that.
let time = measure {
var array = [2,4,5,2,5,7,3,123,213,12]
array.sorted()
}
print("Block is running \(time) seconds.")
Based on Franklin Yu answer and Cœur comments
Details
Xcode 10.1 (10B61)
Swift 4.2
Solution 1
measure(_:)
Solution 2
import Foundation
class Measurer<T: Numeric> {
private let startClosure: ()->(T)
private let endClosure: (_ beginningTime: T)->(T)
init (startClosure: #escaping ()->(T), endClosure: #escaping (_ beginningTime: T)->(T)) {
self.startClosure = startClosure
self.endClosure = endClosure
}
init (getCurrentTimeClosure: #escaping ()->(T)) {
startClosure = getCurrentTimeClosure
endClosure = { beginningTime in
return getCurrentTimeClosure() - beginningTime
}
}
func measure(closure: ()->()) -> T {
let value = startClosure()
closure()
return endClosure(value)
}
}
Usage of solution 2
// Sample with ProcessInfo class
m = Measurer { ProcessInfo.processInfo.systemUptime }
time = m.measure {
_ = (1...1000).map{_ in Int(arc4random()%100)}
}
print("ProcessInfo: \(time)")
// Sample with Posix clock API
m = Measurer(startClosure: {Double(clock())}) { (Double(clock()) - $0 ) / Double(CLOCKS_PER_SEC) }
time = m.measure {
_ = (1...1000).map{_ in Int(arc4random()%100)}
}
print("POSIX: \(time)")
From Swift 5.7 (macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0), you can use ContinuousClock and the measure block, which returns a Duration object. It has components that contain the measured time in seconds or attoseconds, which is 1×10−18 of a second.
let clock = ContinuousClock()
let duration = clock.measure {
// put here what you want to measure
}
print("Duration: \(duration.components.seconds) seconds")
print("Duration: \(duration.components.attoseconds) attoseconds")
Related
I have this function which can successfully convert a counter variable into a beautifully formatted minutes and seconds string. How can I extend this to a format displaying MM:SS:milliseconds ?
I thought perhaps to do let milliseconds = Int(Double(seconds / 60) * 100) and append it to the return. Any tips?
Note: my timer should be publishing with enough granularity #State var timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect()
func formatMmSs(counter: Double) -> String {
let minutes = Int(counter) / 60 % 60
let seconds = Int(counter) % 60
return String(format: "%02i:%02i", minutes, seconds)
}
You just need to multiply your counter (number of seconds) times 1000,
truncating remainder dividing by 1000 and coerce the result to integer:
func formatMmSsMl(counter: Double) -> String {
let minutes = Int((counter/60).truncatingRemainder(dividingBy: 60))
let seconds = Int(counter.truncatingRemainder(dividingBy: 60))
let milliseconds = Int((counter*1000).truncatingRemainder(dividingBy: 1000))
return String(format: "%02d:%02d.%03d", minutes, seconds, milliseconds)
}
Or implemented as a computed property for BinaryFloatingPoint types:
extension BinaryFloatingPoint {
var intValue: Int { Int(self) }
var minutesSecondsMilliseconds: String {
String(format: "%02d:%02d.%03d",
(self / 60).truncatingRemainder(dividingBy: 60).intValue,
truncatingRemainder(dividingBy: 60).intValue,
(self * 1000).truncatingRemainder(dividingBy: 1000).intValue)
}
}
You can also custom format the floating point. No need to coerce to integer as follow:
extension TimeInterval {
var minutesSecondsMilliseconds: String {
String(format: "%02.0f:%02.0f.%03.0f",
(self / 60).truncatingRemainder(dividingBy: 60),
truncatingRemainder(dividingBy: 60),
(self * 1000).truncatingRemainder(dividingBy: 1000).rounded(.down))
}
}
123.45678.minutesSecondsMilliseconds // "02:03.456"
This is keep using your approach also following Leo approach,
func formatMmSs(counter: Double) -> String {
let minutes = Int(counter) / 60 % 60
let seconds = Int(counter) % 60
let milliseconds = Int(counter*1000) % 1000
return String(format: "%02d:%02d:%03d", minutes, seconds, milliseconds)
}
The currentFrame (ARFrame) of ARSession has a timestamp attribute of type TimeInterval which represents the uptime at the moment the frame has been captured.
I need to convert this TimeInterval to the current time domain of the device.
If my assumption about timestamp is correct, adding the kernel BootTime and timestamp together would give me the correct date.
Problem: Adding the kernel BootTime and timestamp together gives me an Date that is not correct. (depending on the device`s last boot time up to 2 days variance)
Current Code:
func kernelBootTime() -> Date {
var mib = [CTL_KERN, KERN_BOOTTIME]
var bootTime = timeval()
var bootTimeSize = MemoryLayout<timeval>.stride
if sysctl(&mib, UInt32(mib.count), &bootTime, &bootTimeSize, nil, 0) != 0 {
fatalError("Could not get boot time, errno: \(errno)")
}
return Date(timeIntervalSince1970: Double(bootTime.tv_sec) + Double(bootTime.tv_usec) / 1_000_000.0)
}
public func checkTime(_ session: ARSession) {
guard let frame = session.currentFrame else { return }
print(Date(timeInterval: frame.timestamp, since: kernelBootTime()))
}
I found the solution.
var refDate = Date.now - ProcessInfo.processInfo.systemUptime
gives you the date of the last device restart
public func checkTime(_ session: ARSession) {
guard let frame = session.currentFrame else { return }
print(Date(timeInterval: frame.timestamp, since: refDate))
}
prints the exact time when the image was taken. (in UTC time)
If I want to evaluate time performance of a few algos using Date() or Dispatch() how can I create a function that does this?
For example this binary search algo. How can I pass it as a closure parameter and have the closure do all of the time performance measuring using any of the Swift time keeping methods below? Please answer with an example of a closure. Thanks.
let startingPoint = Date()
let startingPoint = Dispatch().now
func binarySearchForValue(searchValue: Int, array: [Int]) -> Bool {
var leftIndex = 0
var rightIndex = array.count - 1
while leftIndex <= rightIndex {
let middleIndex = (leftIndex + rightIndex) / 2
let middleValue = array[middleIndex]
if middleValue == searchValue {
return true
}
if searchValue < middleValue {
rightIndex = middleIndex - 1
}
if searchValue > middleValue {
leftIndex = middleIndex + 1
}
}
return false
}
Since you may want to measure different functions, it probably makes sense to capture the arguments for the function in the closure instead of including their types in the signature. But I did use a generic type for the return value. I hope that this is what you're after:
func measure<R>(_ label: String, operation: () -> R) -> R {
let start = DispatchTime.now()
let result = operation()
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
let timeInterval = Double(nanoTime) / 1_000_000_000
print(String(format: "%#: %.9f s", label, timeInterval))
return result
}
Here's how you use it:
let result = measure("search") { binarySearchForValue(searchValue: 3, array: [1, 3, 8]) }
print(result) // that's the result of the function that was measured, not the measurement
measure("some function") { functionWithoutReturnValue() }
If the function has no return value, R will be (), so that should work too. Just don't assign the result to anything (see the example above). If you want to do something with the measurement other than printing it to the console, you can do that, too. But you didn't specify that in your question, so I went with print.
Not sure what exactly you are after here and this solution below will only fit one specific function signature to test
func testBench(search: Int, array: [Int], testCase test: (Int, [Int]) -> Bool) {
let start = DispatchTime.now()
test(search, array)
let end = DispatchTime.now()
print("\(start) - \(end)")
}
called like this
testBench(search: 3, array: [6,7,5,3]) {binarySearchForValue(searchValue: $0, array: $1)}
You should use XCTest to measure the performance ... It gives you proper stats for your method in terms of performance
i.e.
func testMyCodesPerformance() {
measureBlock {
someClass.doSomethingFancy()
}
}
You can do lot more using XCTest measureBlock for performance testing
I can't find a way to implement a wait function, I'm using swiftforwindows and no examples online have been able to solve it so far. It's Swift 4.2
The class is basically an array that when a function is called each index on the array gets a constant value deducted. the tick function is what is being called. I'm new to Swift.
class resProj {
var list = [1,1,1,1]
var projReq = [100,200,300,50]
var completed = false
func tick(){
for count in 0..<projReq.count{
if projReq[count] <= list[count]{
projReq[count] = 0
}
else if projReq[count] > list[count]{
projReq[count] -= list[count]
}
}
print(projReq)
}
init(
mathsP mathsIn: Int,
scienceP sciecnceIn: Int,
enginerP enginerIn: Int,
businessP businessIn: Int) {
self.list [0] = mathsIn
self.list [1] = sciecnceIn
self.list [2] = enginerIn
self.list [3] = businessIn
}
}
var spaceElev = resProj(
mathsP: 10,
scienceP: 20,
enginerP: 30,
businessP: 5)
var x = false
while x == false{
//wait function here pls//
print("tick", terminator:"?")
let y = readLine()
if y == "y"{
spaceElev.tick()
}
else{
print("gotta put y")
}
var templist = spaceElev.projReq
var templistcount = 0
templistcount = templist.count
for loop in 0..<templistcount{
if templist[loop] == 0{
templistcount -= 1
}
}
if templistcount == 0 {
x = true
print("project completed")
}
}
}
Where it says //wait function here pls// I would like to make the program wait for 1 second.
There are a lot of way to do this but most common way is create a completion function. For example:
func doSth(_ someParameter: String, _ completion: ()->()) {
print(someParameter)
// After your code is finish call completion
completion()
}
And when you call (there is two way to call):
doSth("Done") {
print("You can be sure that this block will work after your func finish")
}
or you can simply create another func and send it as a parameter.
You can also use DispatchQueue:
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
// put your func here...
}
You can simple use the UNIX-Functin func sleep(_: UInt32) -> UInt32.
In your case use sleep(1) to wait one second.
You could use Grand Central Dispatch or perform.
GCD solution:
let delayInSeconds = 1
DispatchQueue.main.asyncAfter(deadline: .now() + delayInSeconds) {
print("tick", terminator:"?")
}
If you want to learn more about Grand Central Dispatch (GCD) I suggest you read through this:
Grand Central Dispatch - Wikipedia
Grand Central Dispatch Tutorial - Ray Wenderlich
Perform solution:
Create a function like this:
#objc func delayedFunc() {
//write the code here that you want to execute with a one second delay
}
Then call this where you want the delayed function to execute:
let delayInSeconds = 1
perform(#selector(delayedFunc), with: nil, afterDelay: delayInSeconds)
You can use the RunLoop class:
func wait(for interval: TimeInterval) {
RunLoop.current.run(until: Date() + interval)
}
I have a function that does not need to be called any more than every 10 secs. Every time I invoke the function, I reset the timer to 10 secs.
class MyClass {
var timer:Timer?
func resetTimer() {
self.timer?.invalidate()
self.timer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false) {
(timer) -> Void in
self.performAction()
}
}
func performAction() {
// perform action, then
self.resetTimer()
}
}
I would like to test that calling performAction() manually resets the timer to 10 secs, but I can't seem to find any good way to do it. Stubbing resetTimer() feels like the test wouldn't really be telling me enough about the functionality. Am I missing something?
XCTest:
func testTimerResets() {
let myObject = MyClass()
myObject.resetTimer()
myObject.performAction()
// Test that my timer has been reset.
}
Thanks!
If you want to wait for the timer to fire, you'll still need to use expectations (or Xcode 9's new asynchronous testing API).
The question is what precisely you're trying to test. You presumably don't want to just test that the timer fired, but rather you want to test what the timer's handler is actually doing. (Presumably you have a timer in order to perform something meaningful, so that's what we should be testing.)
WWDC 2017 video Engineering for Testability offers a nice framework to be thinking about how to design code for unit tests , which need:
control over inputs;
visibility to outputs; and
no hidden state.
So, what are the inputs to your test? And, more importantly, what is the output. What assertions do you want to test for in your unit test?
The video also shows a few practical examples of how one might refactor code to achieve this structure through judicious use of:
protocols and parameterization; and
separating logic and effects.
It's hard to advise further without knowing what the timer is actually doing. Perhaps you can edit your question and clarify.
Good that you found a solution, but answering the question in title;
To test if timer actually works (i.e. runs and calls callback), we can do something like:
import XCTest
#testable import MyApp
class MyClassTest: XCTestCase {
func testStartTimer_shouldTriggerCallbackOnTime() throws {
let exp = expectation(description: "Wait for timer to complete")
// Dummy.
let instance: MyClass! = MyClass()
instance.delay = 2000; // Mili-sec equal 2 seconds.
instance.callback = { _ in
exp.fulfill();
}
// Actual test.
instance.startTimer();
// With pause till completed (sleeps 5 seconds maximum,
// else resumes as soon as "exp.fulfill()" is called).
if XCTWaiter.wait(for: [exp], timeout: 5.0) != .completed {
XCTFail("Timer didn't finish in time.")
}
}
}
When having a class like:
public class MyClass {
public var delay: Int = 0;
public var callback: ((timer: Timer) -> Void)?
public func startTimer() {
let myTimer = Timer(timeInterval: Double(self.delay) / 1000.0, repeats: false) {
[weak self] timer in
guard let that = self else {
return
}
that.callback?(timer)
}
RunLoop.main.add(myTimer, forMode: .common)
}
}
First, I would say I don't know how your object was working when you don't any member called refreshTimer.
class MyClass {
private var timer:Timer?
public var starting:Int = -1 // to keep track of starting time of execution
public var ending:Int = -1 // to keep track of ending time
init() {}
func invoke() {
// timer would be executed every 10s
timer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(performAction), userInfo: nil, repeats: true)
starting = getSeconds()
print("time init:: \(starting) second")
}
#objc func performAction() {
print("performing action ... ")
/*
say that the starting time was 55s, after 10s, we would get 05 seconds, which is correct. However for testing purpose if we get a number from 1 to 9 we'll add 60s. This analogy works because ending depends on starting time
*/
ending = (1...9).contains(getSeconds()) ? getSeconds() + 60 : getSeconds()
print("time end:: \(ending) seconds")
resetTimer()
}
private func resetTimer() {
print("timer is been reseted")
timer?.invalidate()
invoke()
}
private func getSeconds()-> Int {
let seconds = Calendar.current.component(.second, from: Date())
return seconds
}
public func fullStop() {
print("Full Stop here")
timer?.invalidate()
}
}
Testing (explanation in the comments)
let testObj = MyClass()
// at init both starting && ending should be -1
XCTAssertEqual(testObj.starting, -1)
XCTAssertEqual(testObj.ending, -1)
testObj.invoke()
// after invoking, the first member to be changed is starting
let startTime = testObj.starting
XCTAssertNotEqual(startTime, -1)
/*
- at first run, ending is still -1
- let's for wait 10 seconds
- you should use async method, XCTWaiter and expectation here
- this is just to give you a perspective or way of structuring your solution
*/
DispatchQueue.main.asyncAfter(deadline: .now() + 10 ) {
let startTimeCopy = startTime
let endingTime = testObj.ending
XCTAssertNotEqual(endingTime, -1)
// take the difference between start and end
let diff = endingTime - startTime
print("diff \(diff)")
// no matter the time, diff should be 10
XCTAssertEqual(diff, 10)
testObj.fullStop()
}
this is not the best of way of doing it, however it gives you view or a flow on how you should achieve this :)
I ended up storing the original Timer's fireDate, then checking to see that after the action was performed the new fireDate was set to something later than the original fireDate.
func testTimerResets() {
let myObject = MyClass()
myObject.resetTimer()
let oldFireDate = myObject.timer!.fireDate
myObject.performAction()
// If timer did not reset, these will be equal
XCTAssertGreaterThan(myObject.timer!.fireDate, oldFireDate)
}