How to Debounce and Throttle Using @Observable

I want to transition from Combine to the Observation framework my question is how do I Debounce user input with the Observation framework there appears now way to do this. Any and all help is welcomed.

Replies

The expected path forward here is to combine (hey hey!) two tasks:

  • Turning an observed property into an async sequence of value changes.

  • Debouncing such a sequence.

The second task is covered by Swift Async Algorithms. The first task, however, is a challenge. The original SE-0395 proposed a general way to do this but that ran into problems during review. The proposal was recently accept with the async stuff moved into the Future Directions section.

Share and Enjoy

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

Thanks Eskimo, could you point me in a direction for the first point above ? Where do I start to turn an observed property into an async sequence of value changes ? Thanks, A

@Dionysus-Design  If you just want to debounce, you can use this code :

public actor Debouncer {
    private let duration: Duration
    private var isPending = false
    
    public init(duration: Duration) {
        self.duration = duration
    }
    
    public func sleep() async -> Bool {
        if isPending { return false }
        isPending = true
        try? await Task.sleep(for: duration)
        isPending = false
        return true
    }
}

Put this code in your Observable:

    private let debouncer = Debouncer(duration: .seconds(0.5))

    func checkEmailValidity() async {
        guard await debouncer.sleep() else { return }
        
        ... do your debounced stuff here ...
    }

And somewhere on your SwiftUI View for example:

    .onChange(of: viewModel.email) { Task { await viewModel.checkEmailValidity() } }
  • This is a more like a throttle, not a debounce. We need to reset the timer each time a new event is emitted, replace the last occurrence and then try to wait for the new timer to complete.

Add a Comment

It seems that the timer is only set on the first key stroke. How would I reset timer to zero on each stroke to better implement debounce?