keep calm, most probably this question is not a duplicate.
I have already tried all solutions in stack overflow, but nothing worked for me.
I have a client thread that send and receive a "keepalive" string (a ping) continually from another host. If it doesn't receive the "keepalive" within the KeepAliveMax, it closes the streams and say good bye (in theory, but this yet work in progress).
Now my problem is that I used a NSTimer to call the updateKeepAlive function, but it is never invoked..I don't understand why :(
I have tried also to set the NSTimer RunLoop manually, but it not works
The follow is the part of code where the selector function should be initialized and called every second:
public class Client: NSObject, NSStreamDelegate {
var serverAddress: CFString
let serverPort: UInt32 = 50000
private var inputStream: NSInputStream!
private var outputStream: NSOutputStream!
private var connecting:Bool
private var byteRead:Int
private var byteWrite:Int
private let keepAliveMax:Double = 5000
private var keepAliveTime:Double
private var connected:Bool
var timer: NSTimer?
init(ip:String) {
serverAddress = ip
connecting = false
connected = false
byteRead = 0
byteWrite = 0
keepAliveTime = 0
super.init()
let thread = NSThread(target:self, selector:#selector(connect), object:nil)
thread.start()
print("thread is started and now I can continue with other tasks..")
}
func connect() {
connecting = true
while connecting {
print("connecting...")
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(nil, self.serverAddress, self.serverPort, &readStream, &writeStream)
// Documentation suggests readStream and writeStream can be assumed to
// be non-nil. If you believe otherwise, you can test if either is nil
// and implement whatever error-handling you wish.
self.inputStream = readStream!.takeRetainedValue()
self.outputStream = writeStream!.takeRetainedValue()
self.inputStream.delegate = self
self.outputStream.delegate = self
self.inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
self.outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
self.inputStream.open()
self.outputStream.open()
// send handshake
byteWrite = writeLine("handshake")
print("written: \(byteWrite) for handshake")
// wait to receive handshake
print("waintig for handshake...")
if readLine() == "handshake" {
connected = true
print("Client: connection estabilished correctly")
// close the waiting popup and start with SendBox...
// in progress...
// send keepAlive
byteWrite = writeLine("keepalive")
print("written: \(byteWrite) for keepalive")
//======================== THIS NOT WORK PROPERLY ============================================================
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(Client.updateKeepAlive), userInfo: nil, repeats: true)
/*
timer = NSTimer(timeInterval: 1, target: self, selector: #selector(Client.updateKeepAlive), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(timer!, forMode: NSRunLoopCommonModes)
*/
//============================================================================================================
keepAliveTime = NSDate().timeIntervalSince1970 * 1000
print("Client: Timer started")
while self.inputStream.streamStatus != NSStreamStatus.Closed ||
self.outputStream.streamStatus != NSStreamStatus.Closed
{
print("Client: under the while of keepAlive");
if readLine() == "keepalive"
{
keepAliveTime = NSDate().timeIntervalSince1970 * 1000
writeLine("keepalive")
print("Client: keepalive received");
}
else
{
print("Client: not keepalive: ");
// close streams...... work in progress
break
}
sleep(1)
}
}
else{
print("wrong handshake")
}
print("closing streams..")
connecting = false
self.inputStream.close()
self.outputStream.close()
self.timer?.invalidate()
}
}
And the follow is the updateKeepAlive function:
func updateKeepAlive(){
print("in updateKeepalive function") // <---- NEVER PRINTED
/* in progress .....
........./*
}
In a new thread begining, there is no input sources or timers are attached to the run loop. So the run loop while not be active. You can call run function after call scheduledTimerWithTimeInterval.
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(TimerTest.updateKeepAlive), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().run()
Related
I have this problem for a few days now and I don't get what I am doing wrong.
My application is basically just creating some timers. I need to stop them and create new ones. But at the moment stopping them doesn't work.
self.timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target:self, selector: "timerDidEnd:", userInfo: "Notification fired", repeats: false)
That's my timer
func timerDidEnd(timer:NSTimer){
createUnrepeatedAlarmWithUpdateInterval()
}
Because my timer didn't want to stop I am currently using the unrepeated timer and start it myself after it stopped.
func stopAlarm() {
if self.timer != nil {
self.timer!.invalidate()
}
self.timer = nil
self.timer = NSTimer()
}
And that's how I stop my timer.
alarmManager.stopAlarm()
alarmManager.createUnrepeatedAlarmWithUpdateInterval()
I call the stopAlarm() function before creating a new timer.
I really don't know what I am doing wrong so I appreciate every answer :)
class AlarmManager: ViewController{
private var timer : NSTimer?
private var unrepeatedTimer : NSTimer?
private let notificationManager = NotificationManager()
private var current = NSThread()
private let settingsViewController = SettingsViewController()
func createRepeatedAlarmWithUpdateInterval(){
var timeInterval:NSTimeInterval = settingsViewController.getUpdateIntervalSettings()
if timer == nil{
timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
target: self,
selector: "repeatedTimerDidEnd:",
userInfo: "Notification fired",
repeats: true)
}
}
func repeatedTimerDidEnd(repeatedTimer:NSTimer){
ConnectionManager.sharedInstance.loadTrainings(settingsViewController.getServerSettings())
createUnrepeatedAlarm(10)
}
func createUnrepeatedAlarm(timeInterval:Double){
unrepeatedTimer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
target: self,
selector: "unrepeatedTimerDidEnd:",
userInfo: "Notification fired",
repeats: false)
}
func unrepeatedTimerDidEnd(unrepeatedTimer:NSTimer){
notificationManager.createNotification(self, reminderType: NotificationManager.ITEMRATINGREMINDER)
notificationManager.createNotification(self, reminderType: NotificationManager.ITEMREMINDER)
print("UnrepeatedAlarm ended")
}
func stopAlarm(){
print("StopAlarm triggered")
if (timer != nil)
{
print("stoptimer executed")
timer!.invalidate()
timer = nil
}
if (unrepeatedTimer != nil)
{
unrepeatedTimer!.invalidate()
unrepeatedTimer = nil
}
}
}
Thats the whole code of this class. Maybe that helps :D
The usual way to start and stop a timer safely is
var timer : Timer?
func startTimer()
{
if timer == nil {
timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
}
}
func stopTimer()
{
timer?.invalidate()
timer = nil
}
startTimer() starts the timer only if it's nil and stopTimer() stops it only if it's not nil.
You have only to take care of stopping the timer before creating/starting a new one.
Make sure you're calling invalidate on the same thread as the timer.
From the documentation:
Special Considerations
You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.
https://developer.apple.com/documentation/foundation/nstimer/1415405-invalidate?language=objc
Something that's not really covered by the previous answers is that you should be careful your timer isn't scheduled multiple times.
If you schedule a timer multiple times without first invalidating it, it'll end up scheduled on multiple run loops, and invalidating it then becomes nigh impossible.
For me, it happened when calling my scheduleTimer() function in separate functions in my view controller's life cycle (viewWillAppear, viewDidAppear, ...)
So in short, if you aren't sure (or you cannot guarantee) your Timer is only scheduled once, just always invalidate it first.
I have tried every possible solution found but not able to resolve that at the end I have set repeat "false" while initialising timer like below
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.methodname), userInfo: nil, repeats: false)
And need to add above line in my selector method for whatever the condition for which I wanted to repeat the time.
For example:-
My requirement is I want to repeatedly call some method until one condition satisfied. So instead of adding repeats true I set it false as repeat true does not invalidate timer in my case.
I have added below in my viewdidload method
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.method), userInfo: nil, repeats: false)
in selector function I added below code
#objc func method{
if condition not matched{
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.method), userInfo: nil, repeats: false)
}
else{
// once you are here your timer invalidate automatically
}
}
Hope this will solve your problem
For Swift 5 Xcode 12.4 there is example to use timer:
class MyController: UIViewController {
private id: Float;
func setValue(_ value: Float, withAnimation: Bool) {
let step: Float = value / 200
var current: Float = withAnimation ? 0.0 : value
let _ = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: withAnimation) { timer in
DispatchQueue.main.async {
self.id = current
current += step
if current > value || withAnimation == false {
self.id = current
timer.invalidate()
}
}
}
}
}
I try to create a socket as shown below:
socket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0))
and I implement the func:
func socket(socket : GCDAsyncSocket, didConnectToHost host:String, port p:UInt16){
pingTimer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "sendPing:", userInfo: nil, repeats: true)
}
func sendPing(){
print("will send ping...")
}
But the sendPing will never be called, and if I try this:
socket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
the deletegateQueue is socket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue()), it will work fine.
The UI updating is use main queue, so I want to do socket connection with another type queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) seems to be a good one.
How can I make it work well?
I tried write small program base on torch/flash in iPhones. Now i want add SOS signal but i have no idea how should do this. In this code when i start program will switch on and off my LED every 0.2 sec. But i don't know how to do this in SOS signal. And when user click SOS ON and click SOS OFF led should be off immediately. Do i need running some Thread ? or on NSTimer ?
class Sos {
var timer1 = NSTimer()
var timer2 = NSTimer()
var volume: Float = 0.1
let flashLight = FlashLight()
func start() {
self.timer1 = NSTimer.scheduledTimerWithTimeInterval(0.2,
target: self,
selector: Selector("switchON"),
userInfo: nil,
repeats: true)
self.timer2 = NSTimer.scheduledTimerWithTimeInterval(0.4,
target: self,
selector: Selector("switchOFF"),
userInfo: nil,
repeats: true)
}
func stop() {
timer1.invalidate()
timer2.invalidate()
flashLight.switchOFF()
}
#objc func switchON() {
flashLight.switchON(self.volume)
}
#objc func switchOFF() {
flashLight.switchOFF()
}
deinit {
self.timer1.invalidate()
self.timer2.invalidate()
}
}
Many ways to achieve it. Create sequence of time intervals and alternate between on/off for example. Comments in code.
///
/// SOS sequence: ...---...
///
/// . short
/// - long
///
class SOS {
/// Short signal duration (LED on)
private static let shortInterval = 0.2
/// Long signal duration (LED on)
private static let longInterval = 0.4
/// Pause between signals (LED off)
private static let pauseInterval = 0.2
/// Pause between the whole SOS sequences (LED off)
private static let sequencePauseInterval = 2.0
/**
When the SOS sequence is started flashlight is on. Thus
the first time interval is for the short signal. Then pause,
then short, ...
See `timerTick()`, it alternates flashlight status (on/off) based
on the current index in this sequence.
*/
private let sequenceIntervals = [
shortInterval, pauseInterval, shortInterval, pauseInterval, shortInterval, pauseInterval,
longInterval, pauseInterval, longInterval, pauseInterval, longInterval, pauseInterval,
shortInterval, pauseInterval, shortInterval, pauseInterval, shortInterval, sequencePauseInterval
]
/// Current index in the SOS `sequence`
private var index: Int = 0
/// Non repeatable timer, because time interval varies
private weak var timer: NSTimer?
/**
Put your `Flashlight()` calls inside this function.
- parameter on: pass `true` to turn it on or `false` to turn it off
*/
private func turnFlashlight(on on: Bool) {
// if on == true -> turn it on
// if on == false -> turn it off
print(on ? "ON" : "OFF")
}
private func scheduleTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(sequenceIntervals[index],
target: self, selector: "timerTick",
userInfo: nil, repeats: false)
}
#objc private func timerTick() {
// Increase sequence index, at the end?
if ++index == sequenceIntervals.count {
// Start from the beginning
index = 0
}
// Alternate flashlight status based on current index
// index % 2 == 0 -> is index even number? 0, 2, 4, 6, ...
turnFlashlight(on: index % 2 == 0)
scheduleTimer()
}
func start() {
index = 0
turnFlashlight(on: true)
scheduleTimer()
}
func stop() {
timer?.invalidate()
turnFlashlight(on: false)
}
deinit {
timer?.invalidate()
}
}
I would like to connect to the Internet. If after 5 secs I haven't connected (using Reachability) I want to abort. Is this possible ?
NSTimer with a callback for 1 second and a counter. If 5 attempts fail, 5 seconds has passed.
Something like:
var timerCounter = 0
var timer : NSTimer?
func shouldAttemptConnection() {
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "attemptConnection", userInfo: nil, repeats: true)
self.attemptConnection()
}
func attemptConnection() {
if Reachability.isReachable() {
// Handle success code
} else if ++timerCounter >= 5 {
// Handle timeout code
} else {
// Try again
return
}
self.invalidateTimer()
}
func invalidateTimer() {
timer?.invalidate()
timer = nil
timerCounter = 0
}
I've made a flappy bird clone, but when trying to implement pause functionality, I've come across issues with my NSTimer, which is spawning the pipes.
var timer = NSTimer.scheduledTimerWithTimeInterval(moveSpeed, target: self, selector: Selector("makePipes"), userInfo: nil, repeats: true)
As NSTimers can't be paused, within the makePipes() function I've implemented a simple if-statement, checking whether the game is paused or not, and if it is, not spawning new pipes. However, the gap between each series of pipes is then inconsistent, due to the timer still firing during the paused state.
Also, when transitioning to the game state from the menu, the timer fires off-time, creating the first two pipes in quick succession.
Is there any alternative to the NSTimer to handle this functionality?
You can stop the timer calling the func invalidate() when your game is paused, and then restart it when your game is un-paused.
Update:
You can add a second timer that fires at the difference from the next fire and the time of pause, the second timer should fire the first timer and then reset the first timer to the initial time.
Steps:
Add lets say a timer that fires every 2 seconds
When game is paused, calculate the time interval from the timer.fireDate and timeOfPause, that should be intervalTillNextTrigger
Add a second afterPauseTimer that triggers at intervallTillNextTrigger and should not repeat
When the afterPauseTimer is called, trigger the first timer with timer.fire(), invalidate timer, because timer.fire() will not interrupt it's regular firing schedule, add timer again with 2 seconds firing interval and invalidate afterPauseTimer.
see code below:
//
// ViewController.swift
// swft ios
//
// Created by Marius Fanu on 30/12/14.
// Copyright (c) 2014 Marius Fanu. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
var timer: NSTimer!
var isPaused = false
var isAfterPause = false
var intervalTillNextTrigger: NSTimeInterval = 0
var afterPauseTimer: NSTimer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timer = NSTimer(timeInterval: 2, target: self, selector: Selector("timerTriggerd"), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSDefaultRunLoopMode)
timer.fire()
}
#IBAction func pauseButtonPressed(sender: UIButton) {
var now = NSDate()
println("now = \(now)")
if isPaused == true {
if isAfterPause {
isAfterPause = false
afterPauseTimer = NSTimer(timeInterval: intervalTillNextTrigger, target: self, selector: Selector("timerAfterIntervalTrigger"), userInfo: nil, repeats: false)
NSRunLoop.mainRunLoop().addTimer(afterPauseTimer, forMode: NSDefaultRunLoopMode)
}
timer = NSTimer(timeInterval: 2, target: self, selector: Selector("timerTriggerd"), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSDefaultRunLoopMode)
}
else {
isAfterPause = true
intervalTillNextTrigger = timer.fireDate.timeIntervalSinceDate(now)
println("till next trigger \(intervalTillNextTrigger)")
timer.invalidate()
timer = nil
}
isPaused = !isPaused
}
func timerTriggerd() {
NSLog("Triggerd!")
}
func timerAfterIntervalTrigger() {
println("reset timer")
timer.fire()
timer.invalidate()
timer = nil
timer = NSTimer(timeInterval: 2, target: self, selector: Selector("timerTriggerd"), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSDefaultRunLoopMode)
afterPauseTimer.invalidate()
afterPauseTimer = nil
}
}