Skip to content

Instantly share code, notes, and snippets.

@davbeck
Last active November 23, 2023 03:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davbeck/dcd8b6eae11a4400a867be82da29ffd7 to your computer and use it in GitHub Desktop.
Save davbeck/dcd8b6eae11a4400a867be82da29ffd7 to your computer and use it in GitHub Desktop.
import ConcurrencyExtras
private struct ObservationTrackingState: Sendable {
var isCancelled: Bool = false
var updateTaskID: UUID?
}
public func withObservationTracking<T>(_ apply: @escaping () -> T) -> AsyncThrowingStream<T, any Error> {
let (stream, continuation) = AsyncThrowingStream.makeStream(of: T.self)
let lock = LockIsolated(ObservationTrackingState())
continuation.onTermination = { _ in
lock.withValue {
$0.isCancelled = true
}
}
@Sendable func update() {
guard
lock.withValue({ state -> Bool in
guard !state.isCancelled else { return false }
state.updateTaskID = nil
return true
})
else { return }
let value = withObservationTracking {
apply()
} onChange: {
let taskID = UUID()
guard
lock.withValue({ state -> Bool in
guard !state.isCancelled else { return false }
state.updateTaskID = taskID
return true
})
else { return }
RunLoop.current.perform {
guard lock.updateTaskID == taskID else { return }
update()
}
}
continuation.yield(value)
}
update()
return stream
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment