How to call a selector-based Timer method on a Swift struct?

Viewed 2090

This is interesting: it appears you can't call a selector message on a swift struct! What I mean is this: I've got a struct and want to implement a simple Timer on it, using the variation of Timer.scheduledTimer that takes a selector: argument. The compiler tells me that the selector method being called needs an objc attribute to make the method visible to an Objective-C calling sequence, but that's not supported (yet another helpful compiler message! ;-) for non-class objects.

I thought using a struct was a better way to go in my particular case, but I guess I'm going to have to back-port it to a class unless somebody has a better idea...

1 Answers

I know this is an old question, but as others have pointed out in the comments this is not possible, directly anyways.

You can achieve creation of Swift Timers within structs by isolating the timer logic into a class within the struct. The overall plan is as follows.

  1. Isolate Timer logic into a class within the struct.
  2. Pass the containing struct (or some other class instance within the struct) as a way to communicating back timer ticks.
  3. Instantiate the logic class, and store a reference to it until needed.

Please see the code example below.

struct MyStruct{
    
    var timerLogic: TimerLogic!

    class TimerLogic{
        var structRef: MyStruct!
        var timer: Timer!

        init(_ structRef: MyStruct){
            self.structRef = structRef;
            self.timer = Timer.scheduledTimer(
                timeInterval: 2.0,
                target: self,
                selector: #selector(timerTicked),
                userInfo: nil,
                repeats: true)
        }

        func stopTimer(){
            self.timer?.invalidate()
            self.structRef = nil
        }

        @objc private func timerTicked(){
            self.structRef.timerTicked()
        }
    }

    func startTimer(){
        self.timerLogic = TimerLogic(self)
    }    

    func timerTicked(){
         print("Timer tick notified")

         // Stop at some point
         // timerLogic.stopTimer()
         // timerLogic = nil
    }
}
Related