Search code examples
swifttimerswift3nstimercountdown

Simple MVC Timer- Swift


I know this is a super basic question, I am trying to refactor my code atm and am running into trouble with the timer. All I am trying to do is just get a simple countdown timer to print to the console

Here is my code snippet right now

VC------

var seconds = 60

override func viewDidLoad() {
    super.viewDidLoad()
    self.modelInstance.gameTimerMethod(timeParam: self.seconds)     
}

MODEL-----

func gameTimerMethod(timeParam : Int)  {
    gameTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(decreaseGameTimer(secondsParam:)), userInfo: timeParam, repeats: true)
    print (timeParam)
}

func decreaseGameTimer(secondsParam: Int) {
    var printNumber = secondsParam
    printNumber -= 1
    print (printNumber)
}

Ideally what I want this to do in the console is simply countdown from 60 for now at a rate of one number per second. Although the console is logging at a rate of one number per second, it is for some reason repeatedly logging the number 105553117734207, instead of a 60 seconds countdown.

Thanks!


Solution

  • The value passed to decreaseGameTimer is the Timer instance. It has a property userInfo that has a reference to the object passed to the Timer constructor.

    Create a class to hold your count, and then pass an instance of that class to the timer's userInfo:

    class Model {
    
        class TimerInfo {
            var count: Int
    
            // Pointer to function or closure to call when value changes
            var callback: ((Int) -> Void)?
    
            init(start: Int, callback: @escaping (Int) -> Void) {
                count = start
                self.callback = callback
            }
    
            deinit {
                print("TimerInfo deinit")
            }
        }
    
        @objc func decreaseGameTimer(_ timer: Timer) {
            if let userInfo = timer.userInfo as? TimerInfo {
                userInfo.count -= 1
    
                // call callback with new value
                userInfo.callback?(userInfo.count)
    
                print(userInfo.count)
                if userInfo.count == 0 {
                    print("done")
                    timer.invalidate()
                }
            }
        }
    
        func gameTimerMethod(timeParam: Int, callback: @escaping (Int) -> Void) {
            _ = Timer.scheduledTimer(timeInterval: 1, target: self,
                                     selector: #selector(decreaseGameTimer),
                                     userInfo: TimerInfo(start: timeParam, callback: callback),
                                     repeats: true)
        }
    }
    

    Then you'd call it like this:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        // The closure that follows the gameTimerMethod call is using trailing
        // closure syntax.  It gets passed to gameTimerMethod as the second
        // parameter named callback.  This closure will get called every
        // second until the timer finishes.
    
        self.modelInstance.gameTimerMethod(timeParam: self.seconds) { value in
            self.label.text = "\(value)"
        } 
    
        // Just for fun, lets run a second one at the same time
        self.modelInstance.gameTimerMethod(timeParam: 10) { value in
            self.label2.text = "\(value)"
        }   
    }