NSTimer.scheduledTimerWithTimeInterval in Swift Playground - swift

All the examples I've seen on using the "NSTimer.scheduledTimerWithTimeInterval" within Swift show using the "target: self" parameter, but unfortunately this doesn't work in Swift Playgrounds directly.
Playground execution failed: <EXPR>:42:13: error: use of unresolved
identifier 'self'
target: self,
Here's an example referenced above that results in the error:
func printFrom1To1000() {
for counter in 0...1000 {
var a = counter
}
}
var timer = NSTimer.scheduledTimerWithTimeInterval(0,
target: self,
selector: Selector("printFrom1To1000"),
userInfo: nil,
repeats: false
)
timer.fire()

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()

To get this to run directly within a Swift Playground, you need to embed the printFrom1To1000 function within a class and then set an instance of that class to the "target:" parameter instead of using "self".
Here's a full working example:
class myClass: NSTimer{
func printFrom1To1000() {
for counter in 0...1000 {
var b = counter
}
}
}
let myClassInstance = myClass()
var timer = NSTimer.scheduledTimerWithTimeInterval(0,
target: myClassInstance,
selector: Selector("printFrom1To1000"),
userInfo: nil,
repeats: false
)
timer.fire()

If you already have an object you are referencing (i.e., updating a label), you can extend that type and use that function as the Selector. I find this easier than creating a whole new class and instantiating a new object from it.
extension SKLabelNode {
func updateMe() {
count++
label.text = "\(count)"
}
}
var timer = NSTimer.scheduledTimerWithTimeInterval(0.25,
target: label,
selector: Selector("updateMe"),
userInfo: nil,
repeats: true)
timer.fire()

Related

Timer isn’t performed in the case of sub-task

I’m trying to implement a function using timer and have found timer is not performed in case that it is called through callback function of “URLSession.dataTask”.
In below case, “callee” function is called.
class TimerClass {
func caller() {
timer = Timer.scheduledTimer(timeInterval: 0.1,
target: self,
selector: #selector(callee),
userInfo: nil,
repeats: false)
}
func callee() {
print(“OK”)
}
}
class AnyClass {
func any() {
let timer:TimerClass=TimerClass()
timer.caller()
}
}
But below “callee” is not called. (I’ve confirmed “caller” function is performed)
class TimerClass {
func caller() {
timer = Timer.scheduledTimer(timeInterval: 0.1,
target: self,
selector: #selector(callee),
userInfo: nil,
repeats: false)
}
func callee() {
print(“OK”)
}
}
class AnyClass {
func any() {
func cb(data:Data?, response:URLResponse?, err:Error?) {
let timer:TimerClass=TimerClass()
timer.caller()
}
let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: cb)
.
.
.
}
}
I think maybe because it was performed by sub-task.
Can anyone let me know how do I correct the code?
Check the reference of the Timer class:
Use the scheduledTimer(timeInterval:invocation:repeats:) or scheduledTimer(timeInterval:target:selector:userInfo:repeats:) class
method to create the timer and schedule it on the current run loop in
the default mode.
Use the init(timeInterval:invocation:repeats:) or init(timeInterval:target:selector:userInfo:repeats:) class method to
create the timer object without scheduling it on a run loop. (After
creating it, you must add the timer to a run loop manually by calling
the add(_:forMode:) method of the corresponding RunLoop object.)
So, if you want to schedule the timer in the main RunLoop, you can write something like this:
DispatchQueue.main.async {
Timer.scheduledTimer(
timeInterval: 0.1,
target: self,
selector: #selector(self.callee),
userInfo: nil,
repeats: false
)
}
Not using the Timer class, but this seems to be better:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.callee()
}
(UPDATED) It is clearly stated that the RunLoop class is generally not considered to be thread-safe. You should not use the old code (hidden in the edit history), even if it seemingly works in some limited conditions.

Swift Selector is never called for NSTimer

I have the following instance method in my Swift class Sentence which makes a call to a NSTimer which calls the class instance method as its Selector. When I run the program without breakpoints, it gets to the first NSTimer successfully but then stalls at NSTimer. When I add a breakpoint to see if sentenceDidFinish is ever called, I see that it never is, proving it stops at the first NSTimer.
class Sentence : NSObject {
//init() etc.
func playEvent(eventIndex : Int){
if (eventIndex < 2){
let currEvent = self.eventArray[eventIndex]
currEvent.startEvent()
let nextIndex = eventIndex + 1
print("Play Event event id is ", eventIndex)
NSTimer.scheduledTimerWithTimeInterval(currEvent.duration, target: self, selector: Selector("playEvent:"), userInfo: NSNumber(integer: nextIndex), repeats: false)
}
else if (eventIndex==2){
self.eventArray[eventIndex].startEvent()
print("Play Event event id is ", eventIndex)
NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("sentenceDidFinish"), userInfo: nil, repeats: false)
}
else{
//DO Nothing
}
}
func sentenceDidFinish(){
//foo
//bar
}
}
Here is the full .swift file:
https://gist.github.com/anonymous/e0839eae1d77e1e4b671
When you call playEvent: with the timer, the argument passed will be the timer itself, not the integer. But in the declaration for eventIndex you are acting as if it will be the integer.
Try adding a method like this:
func handleTimer(timer: NSTimer) {
playEvent(timer.userInfo as! Int)
}
Then call the first timer like this:
NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "handleTimer:", userInfo: NSNumber(integer: nextIndex), repeats: false)
The forced casting (as!) will crash if userInfo isn't castable to Int. Safer, but more verbose would look like:
func handleTimer(timer: NSTimer) {
guard let index = timer.userInfo as? Int else { return }
playEvent(index)
}

Swift NSTimer didn't work

I make a "Command Line Tool", and I need use NSTimer.
I start the timer, but it doesn't work...
import Foundation
class ct : NSObject {
func start() {
var timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
}
func update() {
println("test timer");
}
}
var a = ct();
a.start()
while(true) { sleep(10000000) }
NSTimer needs a run loop to work properly, a CLI doesn't have/need one by default.
Call
CFRunLoopRun()
to start the run loop and
CFRunLoopStop(CFRunLoopGetCurrent())
to stop it and don't forget to return appropriate return values.

Cannot create Struct with CGRect based on super.view.frame [duplicate]

Just a simple task, but I'm in trouble. Trying to make a different way but it fails.
How to init NSTimer with declared previously variable? Neither var nor let helps.
The initial value of a property (in your case: timer) cannot depend on another property of the class (in your case: interval).
Therefore you have to move the assigment timer = NSTimer(interval, ...) into a method of the
class, e.g. into viewDidLoad. As a consequence, timer has to be defined as an
optional or implicitly unwrapped optional.
Note also that Selector(...) takes a literal string as argument, not the method itself.
So this should work:
class ViewController: UIViewController {
var interval : NSTimeInterval = 1.0
var timer : NSTimer!
func timerRedraw() {
}
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer(timeInterval: interval, target: self, selector: Selector("timerRedraw"), userInfo: nil, repeats: true)
// ...
}
// Other methods ...
}
Try:
var interval:NSTimeInterval = 1.0
var timer = NSTimer.scheduledTimerWithTimeInterval(interval, target: self, selector: "timerRedraw:", userInfo: nil, repeats: true)
pro-tip and hopefully an appreciated FYI: Swift functions should also start with lower case letters (i.e. "timerRedraw").

Swift dictionary with String key and UIImageView as value [duplicate]

Just a simple task, but I'm in trouble. Trying to make a different way but it fails.
How to init NSTimer with declared previously variable? Neither var nor let helps.
The initial value of a property (in your case: timer) cannot depend on another property of the class (in your case: interval).
Therefore you have to move the assigment timer = NSTimer(interval, ...) into a method of the
class, e.g. into viewDidLoad. As a consequence, timer has to be defined as an
optional or implicitly unwrapped optional.
Note also that Selector(...) takes a literal string as argument, not the method itself.
So this should work:
class ViewController: UIViewController {
var interval : NSTimeInterval = 1.0
var timer : NSTimer!
func timerRedraw() {
}
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer(timeInterval: interval, target: self, selector: Selector("timerRedraw"), userInfo: nil, repeats: true)
// ...
}
// Other methods ...
}
Try:
var interval:NSTimeInterval = 1.0
var timer = NSTimer.scheduledTimerWithTimeInterval(interval, target: self, selector: "timerRedraw:", userInfo: nil, repeats: true)
pro-tip and hopefully an appreciated FYI: Swift functions should also start with lower case letters (i.e. "timerRedraw").