How can I run an action coming from View in a Timer inside a class in SwiftUI?

Viewed 113

I want run a Void action in my class with timer which this action comes from my View, currently it runs for first time and then throw EXC_BAD_ACCESS error. I know I am missing something in between, but cannot find it, need help.


struct ContentView: View {
    
    @StateObject var customTimer: CustomTimer = CustomTimer()

    var body: some View {
        
        VStack(spacing: 30.0) {
            
            Button("start timer") {
                
                customTimer.timerFunction(action: { print("Hello") })
                customTimer.startTimer(timeInterval: 1.0) //<<: Thread 1: EXC_BAD_ACCESS (code=1, address=0x4d555460)
     
            }
            
            Button("stop timer") {
                
                customTimer.stopTimer()
     
            }
            
        }
        .font(Font.body.bold())

    }

}

class CustomTimer: ObservableObject {
    
    private var timer: Timer = Timer()

    func startTimer(timeInterval: Double) {

        timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(timerFunction), userInfo: nil, repeats: true)
        RunLoop.current.add(timer, forMode: .common)
        timer.tolerance = 0.0

    }
    
    func stopTimer() { timer.invalidate() }

    @objc func timerFunction(action: () -> Void) { action() }

}
1 Answers

I found the issue, I should have defined a closure for this, like this:

var action: () -> Void = {}

if you see any improvement can be made please let me know as well, thanks


struct ContentView: View {
    
    @StateObject var customTimer: CustomTimer = CustomTimer()

    var body: some View {
        
        VStack(spacing: 30.0) {
            
            Button("start timer") {

                customTimer.action = { print("Hello") }
                customTimer.startTimer(timeInterval: 1.0)
     
            }
            
            Button("stop timer") {
                
                customTimer.stopTimer()
     
            }
            
        }
        .font(Font.body.bold())

    }

}

class CustomTimer: ObservableObject {
    
    private var timer: Timer = Timer()
    
    var action: () -> Void = {}

    func startTimer(timeInterval: Double) {

        timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(timerFunction), userInfo: nil, repeats: true)
        RunLoop.current.add(timer, forMode: .common)
        timer.tolerance = 0.0

    }
    
    func stopTimer() { timer.invalidate() }

    @objc private func timerFunction() { action() }

}
Related