Created
March 25, 2025 01:26
-
-
Save niw/d6d14f6e4bb28538451b1734aee9df70 to your computer and use it in GitHub Desktop.
Experimental `onChange(of:initial:perform:)`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import Observation | |
func onChange<Value>( | |
of value: @autoclosure () -> Value, | |
initial: Bool = false, | |
perform: (_ oldValue: Value, _ newValue: Value) -> Void, | |
_: isolated Actor = #isolation | |
) async throws { | |
let (stream, continuation) = AsyncThrowingStream.makeStream(of: Void.self) | |
var iterator = stream.makeAsyncIterator() | |
var oldValue: Value? | |
repeat { | |
let newValue = withObservationTracking { | |
value() | |
} onChange: { | |
continuation.yield() | |
} | |
if let _oldValue = oldValue { | |
perform(_oldValue, newValue) | |
} else { | |
oldValue = newValue | |
if initial { | |
// NOTE: This behavior same as `onChange(of:initial:perform:)` on SwiftUI `View`. | |
perform(newValue, newValue) | |
} | |
} | |
oldValue = newValue | |
if Task.isCancelled { | |
// This is only finishing point, by cancelling Task. | |
continuation.finish(throwing: CancellationError()) | |
} | |
} while try await iterator.next() != nil | |
// Should not reach here because previous `try` throws with `CancellationError` | |
// is the only finishing point. | |
fatalError("Should not reach here.") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment