Ring-Jarvi
Ring-Jarvi

Reputation: 7

NSTimer not firing property

I have set up a timer class to firer when the viewDidLoad(). I want to have a timer on multiple view controllers thoughout the app. If you have a better solution to a accurate timer on multiple views please suggest.

Viewcontroller -> One of the views that needs a timer

 override func viewDidLoad() {
 super.viewDidLoad()

    func setupTimer() {

        // Setupt the timer, this will call the timerFired method every second
        var timer = NSTimer(
            timeInterval: 1.0,
            target: self,
            selector: #selector(TestTimer.timerFired()),//<- Error Code
            userInfo: nil,
            repeats: true)
         }

The Error Code: Use of instance member 'timerFired' on type 'TestTimer',did you mean to use a value of type 'TestTimer' instead?

Timer Class -> Checks start date compared to current date/time for a accurate timer

 class TestTimer: NSTimer {

var timer   = NSTimer()
// Converter changes String into NSDate
var startDate = converter("Tue, 26 Apr 2016 09:01:00 MDT")
// Function to be fired
func timerFired() {

    let now = NSDate()
    let difference = now.timeIntervalSinceDate(self.startDate)


    // Format the difference for display
    // For example, minutes & seconds
    let dateComponentsFormatter = NSDateComponentsFormatter()
    dateComponentsFormatter.stringFromTimeInterval(difference)
    print(difference)

 }
 }

Upvotes: 0

Views: 209

Answers (2)

Hamish
Hamish

Reputation: 80781

The error you're getting is pretty obscure. What it's trying to tell you is you should remove the () from the end of your timerFired in the #selector.

var timer = NSTimer(
    timeInterval: 1.0,
    target: self,
    selector: #selector(TestTimer.timerFired),
    userInfo: nil,
    repeats: true)

However, this isn't going to make your code how you want it to work – as self in the timer declaration refers to the view controller, not the timer. I would recommend you create a wrapper class for NSTimer, along with a delegate pattern in order to achieve what you want.

You should note that the documentation states that you shouldn't attempt to subclass NSTimer, so you could do something like this instead:

// the protocol that defines the timerDidFire callback method
protocol TimerDelegate:class {
    func timerDidFire(cumulativeTime:NSTimeInterval)
}

// your timer wrapper class
class TimerWrapper {

    // the underlying timer object
    weak private var _timer:NSTimer?

    // the start date of when the timer first started
    private var _startDate = NSDate()

    // the delegate used to implement the timerDidFire callback method
    weak var delegate:TimerDelegate?

    // start the timer with a given firing interval – which could be a property
    func startTimer(interval:NSTimeInterval) {

        // if timer already exists, make sure to stop it before starting another one
        if _timer != nil {
            stopTimer()
        }

        // reset start date and start new timer
        _startDate = NSDate()
        _timer = NSTimer.scheduledTimerWithTimeInterval(interval,
                                                        target: self,
                                                        selector: #selector(timerDidFire),
                                                        userInfo: nil, repeats: true)
    }

    // invalidate & deallocate the timer,
    // make sure to call this when you're done with the timer
    func stopTimer() {
        _timer?.invalidate()
        _timer = nil
    }

    // make sure to stop the timer when the wrapper gets deallocated
    deinit {
        stopTimer()
    }

    // called when the timer fires
    @objc func timerDidFire() {

        // get the change in time, from when the timer first fired to now
        let deltaTime = NSDate().timeIntervalSinceDate(_startDate)

        // do something with delta time

        // invoke the callback method
        delegate?.timerDidFire(deltaTime)
    }
}

You can then use it like this:

// your view controller class – make sure it conforms to the TimerDelegate
class ViewController: UIViewController, TimerDelegate {

    // an instance of the timer wrapper class
    let timer = TimerWrapper()

    override func viewDidLoad() {
        super.viewDidLoad()

        // set the timer delegate and start the timer – delegate should be set in viewDidLoad,
        // timer can be started whenever you need it to be started.
        timer.delegate = self
        timer.startTimer(1)
    }

    func timerDidFire(cumulativeTime: NSTimeInterval) {

        // do something with the total time

        let dateComponentsFormatter = NSDateComponentsFormatter()
        let text = dateComponentsFormatter.stringFromTimeInterval(cumulativeTime)
        label.text = text
    }
}

As far as the appropriateness of using an NSTimer here goes, as you're only using a time interval of 1 second, an NSTimer is suitable. By taking the time interval over the total timer duration, you can average out any small firing inaccuracies.

Upvotes: 1

Ahmad Ishfaq
Ahmad Ishfaq

Reputation: 914

This is how a timer is initialized

var timer = NSTimer.scheduledTimerWithTimeInterval(1.0 , target: self, selector: #selector(TestTimer.timerFired()), userInfo: nil, repeats: true)

Upvotes: 0

Related Questions