Hemanth Hemanth - 3 months ago 12
iOS Question

How to stop NSTimer from going to Selector and extend it in another method?

I'm reading RFID tags from a BLE Device. If I read one particular RFID value I don't want to read the same value for next one second. For this i'm saving that value in an array and deleting it when timer completes one second.

{

func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?){

let rfid = (characteristic.value?.description.capitalizedString)!

if readRfids.containsObject(rfid){
if timer.valid{
self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.timeEnd(_:)), userInfo: nil, repeats: false)
}
}else{
timer.invalidate()
}

if !timer.valid && !readRfids.containsObject(rfid){
readRfids.addObject(rfid)
self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.timeEnd(_:)), userInfo: nil, repeats: false)
}


}

func timeEnd(sender: Selector){
if !timer.valid{
readRfids.removeLastObject()
}else{
print(timer.fireDate)
}
}


The problem i'm facing is the timeEnd(_:) is being called multiple times after one second, and readRfids.removeLastObject() is never being called.
My BLE device sends notification more than 10times in one second. I want to read value if it is different rfid and extend the timer for one more second if same rfid is read. timer.fireDate is starting to print after one second and print times equal to notifications from BLE device.

Answer

You are scheduling multiple timers, since you are saying if timer.valid then schedule a timer. Also, I think that with a small change you can make your code a ignore multiple items for a second

Something like:

var readRfids = [String:Double]()
var timer: NSTimer

Somewhere like `init` or `viewDidLoad`

    self.timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: #selector(self.timeEnd(_:)), userInfo: nil, repeats: true)

func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?){

    if let rfid = (characteristic.value?.description.capitalizedString) {
        if self.readRfids[rfid] == nil {
            //Not seen recently, so process this object
        }
        self.readRfids[rfid] = 1.0
    } 
}

func timeEnd(sender: Selector){
    for (rfid, remaining) in self.readRfids {
        remaining = remaining - 0.1
        if (remaining > 0) {
            self.readRfids[rfid] = remaining
        } else {
            self.readRfids[rfid] = nil
        }
    }
} 

When an item is seen, it is placed in a dictionary with a count of 1. Every tenth of a second the timer ticks and decrements each of the counts. When the count is 0 the item is removed from the dictionary. This ensures that each item only remains in the dictionary for 1 second since it was seen.

Comments