Bug fix

Hello, I have these two errors in this particular block of code: Capture of 'self' with non-sendable type 'MusicPlayer?' in a @Sendable closure and Capture of 'localUpdateProgress' with non-sendable type '(Double, Double) -> Void' in a @Sendable closure ` @MainActor func startProgressTimer(updateProgress: @escaping (Double, Double) -> Void) { timer?.invalidate() // Stop any existing timer let localUpdateProgress = updateProgress

     timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
         guard let self = self,
               let audioPlayer = self.audioPlayer,
               let currentItem = audioPlayer.currentItem else {
             print("currentItem is nil or audioPlayer is unavailable")
             return
         }

         let currentTime = currentItem.currentTime().seconds
         let duration = currentItem.duration.seconds
        localUpdateProgress(currentTime, duration)
     }
 }`

I've tried nearly every solution and can't think of one that works. Any help is greatly appreciated :)

The issue here is that:

  • The data race protection in Swift 6 requires that the compiler be able to understand the context in which code is run.

  • Timer.scheduledTimer(…) runs the timer callback on the current run loop, which isn’t a concurrency mechanisms that the Swift compiler understands.

Fixing this isn’t trivial in the general case, but there’s an easy fix in this case. You’re calling Timer.scheduledTimer(…) from a main-actor-isolated context, which means you’re on the main thread. Thus the timer will schedule its callback on the main thread’s run loop, and you just need to let the Swift compiler know that. The canonical way to do that is with MainActor.assumeIsolated(_:). For example:

self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
    MainActor.assumeIsolated {
        … the compiler now knows you’re on the main actor,
        trapping at runtime if that’s not the case …
    }
}

Pasted in below is a more complete example.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


import Foundation

@MainActor
class Test {

    var timer: Timer? = nil
    var count: Int = 0
    
    func start() {
        precondition(self.timer == nil)
        self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
            MainActor.assumeIsolated {
                self.timerDidFire()
            }
        }
    }
    
    func timerDidFire() {
        print("timerDidFire, count: \(self.count)")
        self.count += 1
        if self.count >= 5 {
            self.stop()
        }
    }
    
    func stop() {
        guard let timer = self.timer else { return }
        timer.invalidate()
        self.timer = nil
    }
}

let t = Test()
t.start()
withExtendedLifetime(t) {
    RunLoop.current.run()
}
Bug fix
 
 
Q