Nstimer.Scheduledtimerwithtimeinterval in Swift Playground

NSTimer.scheduledTimerWithTimeInterval in Swift Playground

You really should not be using NSTimer these days. It's consumes a lot of resources, causes unnecessary battery drain, and the API lends itself to ugly code.

Use dispatch_after() instead:

dispatch_after(0, dispatch_get_main_queue()) { () -> Void in
for counter in 0...1000 {
var b = counter
}
}

Of course, since the timer will fire after playground does it's stuff you will need an equivalent of timer.fire() to force the code to execute immediately instead of after a 0 second delay. Here's how that works:

let printFrom1To1000 = { () -> Void in
for counter in 0...1000 {
var b = counter
}
}

dispatch_after(0, dispatch_get_main_queue(), printFrom1To1000)

printFrom1To1000()

Using NSTimer in swift playground

First of all, your onTimer method have to be declared as @objc, or NSTimer cannot find that.

As for your question, it's because you haven't started the run loop.

To do that, CFRunLoopRun() is the simplest solution I think.

import Foundation

class MyClass {

func startTimer() {
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "onTimer:", userInfo: nil, repeats: true)
}

@objc func onTimer(timer:NSTimer!) {
println("Timer here")
}
}

var anInstance = MyClass()

anInstance.startTimer()

CFRunLoopRun() // <-- HERE!

For the sake of completeness, as @MartinR metioned in the comment, you can also use XCPSetExecutionShouldContinueIndefinitely()

import Foundation
import XCPlayground
XCPSetExecutionShouldContinueIndefinitely()

class MyClass {

func startTimer() {
NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "onTimer:", userInfo: nil, repeats: true)
}

@objc func onTimer(timer:NSTimer!) {
println("Timer here")
}
}

var anInstance = MyClass()

anInstance.startTimer()

In this case the Playground runs only seconds specified in the Timeline:

screenshot

How can I use Timer (formerly NSTimer) in Swift?

This will work:

override func viewDidLoad() {
super.viewDidLoad()
// Swift block syntax (iOS 10+)
let timer = Timer(timeInterval: 0.4, repeats: true) { _ in print("Done!") }
// Swift >=3 selector syntax
let timer = Timer.scheduledTimer(timeInterval: 0.4, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
// Swift 2.2 selector syntax
let timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: #selector(MyClass.update), userInfo: nil, repeats: true)
// Swift <2.2 selector syntax
let timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
}

// must be internal or public.
@objc func update() {
// Something cool
}

For Swift 4, the method of which you want to get the selector must be exposed to Objective-C, thus @objc attribute must be added to the method declaration.

Simple NSTimer implementation issue

You have 2 problems with your code. As @glenstorey points out in his answer, you need to call the method scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:, not the init method you're calling.

EDIT:

As @DanBeauleu says in his comment to my answer, the call would look like this in Swift:

NSTimer.scheduledTimerWithTimeInterval(
1,
target: self,
selector: "Run:",
userInfo: nil,
repeats: true)

The second problem is your Run method.

You don't want a while loop. That will repeat 10 times in a tiny fraction of a second the first time the timer fires, then invalidate the timer.

Your timer method needs to be changed like this:

func Run(timer : NSTimer) 
{

if counter < 10
{
print(counter)
counter++
}
else
{
timer.invalidate()
}
}

(BTW, by strong convention, method/function names should start with a lower-case letter, so your Run function should be named run instead.)

Repeat function using NSTimer fails with target: self in Swift

Usually using uppercase to the properties name it's considered as a bad attitude, you should use swiftTimer.

These expressions are not allowed ad the top level:

var swiftTimer = NSTimer()
swiftTimer = NSTimer.scheduledTimerWithTimeInterval(1, target:self, selector: Selector("sayHello"), userInfo: nil, repeats: true)

You must put it in a function like for example:

override func viewDidLoad() {
super.viewDidLoad()
var swiftTimer = NSTimer()
swiftTimer = NSTimer.scheduledTimerWithTimeInterval(1, target:self, selector: Selector("sayHello"), userInfo: nil, repeats: true)
}

Using an NSTimer in Swift

You can create a scheduled timer which automatically adds itself to the runloop and starts firing:

Swift 2

NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "timerDidFire:", userInfo: userInfo, repeats: true)

Swift 3, 4, 5

Timer.scheduledTimer(withTimeInterval: 0.5, target: self, selector: #selector(timerDidFire(_:)), userInfo: userInfo, repeats: true)

Or, you can keep your current code, and add the timer to the runloop when you're ready for it:

Swift 2

let myTimer = NSTimer(timeInterval: 0.5, target: self, selector: "timerDidFire:", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(myTimer, forMode: NSRunLoopCommonModes)

Swift 3, 4, 5

let myTimer = Timer(timeInterval: 0.5, target: self, selector: #selector(timerDidFire(_:)), userInfo: nil, repeats: true)
RunLoop.current.add(myTimer, forMode: RunLoop.Mode.common)

How to put timeout to NSTimer in swift?

NSTimer is not suited for variable interval times. You set it up with one specified delay time and you can't change that. A more elegant solution than stopping and starting an NSTimer every time is to use dispatch_after.

Borrowing from Matt's answer :

// this makes a playground work with GCD
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

struct DispatchUtils {

static func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
}


class Alpha {

// some delay time
var currentDelay : NSTimeInterval = 2

// a delayed function
func delayThis() {

// use this instead of NSTimer
DispatchUtils.delay(currentDelay) {
print(NSDate())
// do stuffs

// change delay for the next pass
self.currentDelay += 1

// call function again
self.delayThis()
}
}
}

let a = Alpha()

a.delayThis()

Try it in a playground.
It will apply a different delay to each pass of the function.

NSTimer not firing propery

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.



Related Topics



Leave a reply



Submit