2

I want to update the userInfo of the timer in the selector function every time the timer fires.

userInfo:

var timerDic  = ["count": 0]

Timer:

Init:     let timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector:     Selector("cont_read_USB:"), userInfo: timerDic, repeats: true)

selector function:

public func cont_read_USB(timer: NSTimer)
{
  if var count = timer.userInfo?["count"] as? Int
  {
     count = count + 1

     timer.userInfo["count"] = count
  }
}

I get an error on the last line:

'AnyObject?' does not have a member named 'subscript'

What is wrong here? In Objective_C this task worked with a NSMutableDictionary as userInfo

Pang
  • 9,564
  • 146
  • 81
  • 122
heimi
  • 499
  • 7
  • 16

2 Answers2

5

To make this work, declare timerDic as an NSMutableDictionary:

var timerDic:NSMutableDictionary = ["count": 0]

Then in your cont_read_USB function:

if let timerDic = timer.userInfo as? NSMutableDictionary {
    if let count = timerDic["count"] as? Int {
        timerDic["count"] = count + 1
    }
}

Discussion:

  • Swift dictionaries are value types, so if you want to be able to update it you have to pass an object. By using an NSMutableDictionary you get an object type that is passed by reference, and it can be modified since it is a mutable dictionary.

Complete example for Swift 4+:

If you don't want to use an NSMutableDictionary, you can create your own class. Here is a complete example using a custom class:

import UIKit

class CustomTimerInfo {
    var count = 0
}

class ViewController: UIViewController {

    var myTimerInfo = CustomTimerInfo()

    override func viewDidLoad() {
        super.viewDidLoad()

        _ = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(update), userInfo: myTimerInfo, repeats: true)
    }

    @objc func update(_ timer: Timer) {
        guard let timerInfo = timer.userInfo as? CustomTimerInfo else { return }

        timerInfo.count += 1
        print(timerInfo.count)
    }

}

When you run this in the simulator, the count that is printed increases every second.

vacawama
  • 150,663
  • 30
  • 266
  • 294
  • @AdrianBartholomew, I just tried this in an app with Xcode 9.2 (latest) and Swift 4 and it worked. I had to use `Timer` instead of `NSTimer` and add `@objc` to the timer update function, but it worked. What symptoms are you seeing? – vacawama Feb 26 '18 at 11:50
  • If I pass a bool in the userInfo, it's always false when it calls the function. – Chewie The Chorkie Oct 26 '18 at 20:12
  • @ChewieTheChorkie, are you passing just a straight Bool, or is it the property of a class? Does the timer routine change the Bool? – vacawama Oct 26 '18 at 20:20
  • I didn't want to also make that class property, so I just passed the straight Bool. – Chewie The Chorkie Oct 26 '18 at 20:31
  • 1
    @ChewieTheChorkie, a Bool is a value type, so it will always just be the initial value. You need to pass an object that wraps your Bool. – vacawama Oct 26 '18 at 20:41
1

NSTimer.userInfo is of type AnyObject so you need to cast it to your target object:

public func cont_read_USB(timer: NSTimer)
{
    if var td = timer.userInfo as? Dictionary<String,Int> {
        if var count = td["count"] {
            count += 1
            td["count"] = count
        }
    }
}
zisoft
  • 22,770
  • 10
  • 62
  • 73
  • But note that this will modify only the local `td` dictionary, not the `timerDic` property (so the count would be zero on *every* call) – Martin R Nov 02 '14 at 11:55
  • @MartinR: You are right, passing a reference to the `timerDic` property would be needed here. Better to modify the property object itself. – zisoft Nov 02 '14 at 12:32