Skip to content

Instantly share code, notes, and snippets.

@mattmassicotte
Created March 14, 2024 10:44
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mattmassicotte/2737e6de5398d890e6de468fc0f51d76 to your computer and use it in GitHub Desktop.
Save mattmassicotte/2737e6de5398d890e6de468fc0f51d76 to your computer and use it in GitHub Desktop.
AsyncChanges
import SwiftUI
import AsyncAlgorithms
struct AsyncChanges<V>: ViewModifier where V : Equatable, V: Sendable {
typealias Element = (oldValue: V, newValue: V)
typealias Action = (AsyncStream<Element>) async -> Void
@State private var streamPair = AsyncStream<Element>.makeStream()
private let action: Action
private let value: V
init(of value: V, initial: Bool, action: @escaping Action) {
self.action = action
self.value = value
}
func body(content: Content) -> some View {
content
.onChange(of: value, initial: true) { oldValue, newValue in
streamPair.continuation.yield((oldValue, newValue))
}
.task {
await action(streamPair.stream)
}
}
}
extension View {
public func asyncChanges<V>(
of value: V,
initial: Bool = false,
action: @escaping (AsyncStream<(oldValue: V, newValue: V)>) async -> Void
) -> some View where V: Equatable, V: Sendable {
modifier(AsyncChanges<V>(of: value, initial: initial, action: action))
}
}
struct ContentView: View {
@State private var username = ""
var body: some View {
TextField("Username", text: self.$username)
.asyncChanges(of: username) { sequence in
for await value in sequence.debounce(for: .seconds(0.25)) {
print("debounced value: \(value.newValue)")
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment