Swift keep cli app running - swift

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

Related

Scheduled Timer won't fire

I am trying out Swift as a language for CLI tool, which is supposed to serve as a simple web crawler.
In my main file I create an instance of APIFetcher class. In the initialiser of APIFetcher I instantiate an instance of Timer with the given time interval. Once I call startQuerying method, it adds Timer to the main run loop - at this point I would expect performTask method would be invoked, but it isn't. What am I doing wrong?
#available(OSX 10.12, *)
public init(with interval: TimeInterval) {
self.timer = Timer(timeInterval: interval, repeats: true) { _ in
self.performTask()
}
}
deinit {
self.timer?.invalidate()
self.timer = nil
}
public func startQuerying(_ url: URL) {
guard let unwrappedTimer = self.timer else { return }
RunLoop.main.add(unwrappedTimer, forMode: .defaultRunLoopMode)
}
func performTask() {
print("Performed scheduled task")
}
Thanks vadian you are right, I added the timer to run loop, but never actually started it. This fixes the whole issue:
RunLoop.main.add(unwrappedTimer, forMode: .defaultRunLoopMode)
RunLoop.main.run()
Also, see When Would You Use a Run Loop? documentation

Swift 3 Linux with Perfect: Add a scheduled timer with interval to the runLoop

I'm trying to make an application in Swift on my Ubuntu (Ubuntu 15.10 wily, Swift swift-3.0.1-RELEASE) using the Perfect library.
I would like to have a function called every X second. For that, I'm using the Timer class of the Foundation module:
class MyTimer {
init() {
var timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(MyTimer.onTimer(timer:)), userInfo: nil, repeats: true)
}
#objc func onTimer(timer: Timer) {
print("MyTimer.onTimer")
}
}
Despite finding several solutions with this code, the compilation failed:
$> swift build
Compile Swift Module 'my-app' (7 sources)
/home/.../Sources/MyTimer.swift:8:16: error: method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C
#objc func onTimer(timer: Timer) {
Another compilation error if I'm extending my class from NSObject or if I removed the argument timer:
$> swift build
Compile Swift Module 'my-app' (7 sources)
/home/.../Sources/MyTimer.swift:6:83: error: '#selector' can only be used with the Objective-C runtime
var timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(MyTimer.onTimer), userInfo: nil, repeats: true)
I tried to use the other declaration which do not use selectors:
class MyTimer {
init() {
print("MyTimer.init")
var timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {
timer in
print("MyTimer.onTimer")
}
}
}
The compilation works, but my second print is never called. I also tried to manually add my timer to the current RunLoop:
class MyTimer {
init() {
print("MyTimer.init")
var timer = Timer(timeInterval: 1, repeats: true) {
timer in
print("MyTimer.onTimer")
}
RunLoop.current.add(timer, forMode: .defaultRunLoopMode)
// timer.fire()
}
}
Never called again (and timer.fire() only call one time my function). And finally:
class MyTimer {
init() {
print("MyTimer.init")
let timer = Timer(timeInterval: 1, repeats: true) {
timer in
print("MyTimer.onTimer")
}
RunLoop.current.add(timer, forMode: .defaultRunLoopMode)
RunLoop.current.run(until: Date(timeIntervalSinceNow: 4.0))
}
}
My message "MyTimer.onTimer" is printed 5 times, but my server (using the Perfect library) is started only at the end:
$> swift build && ./.build/debug/my-app 8400
Compile Swift Module 'my-app' (7 sources)
Linking ./.build/debug/my-app
MyTimer.init
MyTimer.onTimer
MyTimer.onTimer
MyTimer.onTimer
MyTimer.onTimer
MyTimer.onTimer
[INFO] Starting HTTP server on 0.0.0.0:8181
I do not know anymore what to try. It can be a problem with the Perfect library, but I can not find anything to solve my worries. I can maybe run a new thread, and start my timer in it, but it gets a bit complex?
If you are using Perfect seriously, please don’t use Foundation stuff. Try Perfect Threading: http://www.perfect.org/docs/thread.html
import PerfectThread
#if os(Linux)
import GlibC
#else
import Darwin
#endif
Threading.dispatch {
sleep(10) // wait for 10 seconds
doSomething()
}//end threading
it is safe and simple
very typical server side coding

Swift NSTimer Pause Resume

I am programming with Swift, and I have a problem.
I am writing code using a timer using NSTimer, and I have succeeded in stopping the timer. I created a button to restart the timer, but I do not know how to enter the code.
import UIKit
class ViewController: UIViewController {
/*Timer1*/
var landmarkTime = 0
var landmarkNSTimer = Timer()
/*Timer3*/
func landmarkTimer() {
landmarkTime += 1
}
/*PauseButton*/
#IBAction func PauseButton(_ sender: Any) {
landmarkNSTimer.invalidate()
}
/*ResumeButton*/
#IBAction func ResumeButton(_ sender: Any) {
/*I want to restart the timer in this part, but I do not know what code to put in.*/
}
override func viewDidLoad() {
super.viewDidLoad()
/*Timer2*/
landmarkNSTimer = Timer(timeInterval: 1, target: self, selector: #selector(ViewController.landmarkTimer), userInfo: nil, repeats: true)
RunLoop.main.add(landmarkNSTimer, forMode: RunLoopMode.commonModes)
}
}
/* ResumeButton */ This is a problem. I typed /* Timer2 */ in this part, but I have an unknown problem and am looking for another code. Tell the new code to be entered in /* ResumeButton */ so that the timer can be restarted.
You didn't pause the timer, you stopped it. If you read the documentation for invalidate, it says [emphasis added]:
Stops the receiver from ever firing again and requests its removal from its run loop.
Timers don't pause; it's simply not part of their API. If you want a pause/resume behavior, there are a variety of ways to get it, including:
Create a new timer when resuming.
Set the fireDate to Date.distantFuture when pausing, then set it to some time in the near future when resuming.
Use a boolean variable to indicate the paused state, and have your timer callback check that variable when the timer fires. If paused == true, do nothing.

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 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.