Swift NSTimer didn't work - swift

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.

Related

Cancel Swift Timer From Another Function

I am using a Swift Timer library found here. It allows you to run a timer and then stop it using the following syntax:
Timer.every(5.seconds) { (timer: Timer) in
// do something
if finished {
timer.invalidate()
}
}
I am trying to have the timer start and then have the option of the timer being cancelled from another function but I can't figure out how to reference the timer that is counting down from another function. I have tried doing something like this but it throws the following error:
Static member every cannot be used on instance of type Timer
var countTimer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
initNotificationSetupCheck()
countTimer.every(5.seconds) { //error here
}
func stopTimer() {
countTimer.invalidate()
}
What if you were to use a mixture of both, but instead of creating an instance of Timer, assign the timer from inside the closure to your member variable like:
var countTimer: Timer
Timer.each(5.seconds) { timer in
countTimer = timer
//rest of code
}
Then you can invalidate the timer outside of the closure if needed.
You can go like this
self.timer = Timer.scheduledTimer(timeInterval: 5, target: self,
selector: #selector(self.eventForTimer), userInfo: nil, repeats: true)
for cancelling timer
func cancelTimer() {
self.timer.invalidate()
}

Swift keep cli app running

Theres any way to keep app running while user doesn't kill the app? I found that this can be accomplish with RunLoop.current.run() but when hit the app in this like the app completely freeze, I've a Timer that its declared before the RunLoop and seems to be correctly initialized but the timer doesn't proc, heres a simple example
import Foundation
print("start")
let _ = Timer.init(timeInterval: 1, repeats: true) { _ in
print("proc")
}
RunLoop.current.run()
print("end")
Seems that I'm not catching how this works.
Regards
The problem is merely that you don't know how to use a Timer. It isn't enough to init it. You have to schedule it. Instead of calling init..., call Timer.scheduledTimer... and the whole thing will spring to life for you. Then you'll see that in fact your process is running.
I put this (I can't use a block-based Timer because I'm not using Sierra) and it worked fine:
import Foundation
class TimerHolder:NSObject {
var timer : Timer?
func timerFired(_:Timer) { print("fired") }
override init() {
super.init()
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self,
selector: #selector(timerFired),
userInfo: nil, repeats: true)
}
}
print("start")
_ = TimerHolder()
RunLoop.current.run()
print("end")

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

NSTimer.scheduledTimerWithTimeInterval in Swift Playground

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