Fix for freeze in `MenuBarExtra` and `NavigationStack` (#158)

This commit is contained in:
Wouter Hennen 2024-01-18 17:10:16 +01:00 committed by GitHub
parent 46dfe48645
commit 2dad0e446e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 24 additions and 14 deletions

View File

@ -6,14 +6,18 @@ extension Defaults {
final class Observable<Value: Serializable>: ObservableObject {
private var cancellable: AnyCancellable?
private var task: Task<Void, Never>?
private let key: Defaults.Key<Value>
let objectWillChange = ObservableObjectPublisher()
var key: Defaults.Key<Value> {
didSet {
if key != oldValue {
observe()
}
}
}
var value: Value {
get { Defaults[key] }
set {
objectWillChange.send()
Defaults[key] = newValue
}
}
@ -21,10 +25,20 @@ extension Defaults {
init(_ key: Key<Value>) {
self.key = key
observe()
}
deinit {
task?.cancel()
}
func observe() {
// We only use this on the latest OSes (as of adding this) since the backdeploy library has a lot of bugs.
if #available(macOS 13, iOS 16, tvOS 16, watchOS 9, *) {
task?.cancel()
// The `@MainActor` is important as the `.send()` method doesn't inherit the `@MainActor` from the class.
self.task = .detached(priority: .userInitiated) { @MainActor [weak self] in
task = .detached(priority: .userInitiated) { @MainActor [weak self, key] in
for await _ in Defaults.updates(key) {
guard let self else {
return
@ -34,7 +48,7 @@ extension Defaults {
}
}
} else {
self.cancellable = Defaults.publisher(key, options: [.prior])
cancellable = Defaults.publisher(key, options: [.prior])
.sink { [weak self] change in
guard change.isPrior else {
return
@ -47,10 +61,6 @@ extension Defaults {
}
}
deinit {
task?.cancel()
}
/**
Reset the key back to its default value.
*/
@ -71,8 +81,7 @@ public struct Default<Value: Defaults.Serializable>: DynamicProperty {
private let key: Defaults.Key<Value>
// Intentionally using `@ObservedObjected` over `@StateObject` so that the key can be dynamically changed.
@ObservedObject private var observable: Defaults.Observable<Value>
@StateObject private var observable: Defaults.Observable<Value>
/**
Get/set a `Defaults` item and also have the view be updated when the value changes. This is similar to `@State`.
@ -99,7 +108,7 @@ public struct Default<Value: Defaults.Serializable>: DynamicProperty {
*/
public init(_ key: Defaults.Key<Value>) {
self.key = key
self.observable = .init(key)
self._observable = .init(wrappedValue: .init(key))
}
public var wrappedValue: Value {
@ -122,6 +131,7 @@ public struct Default<Value: Defaults.Serializable>: DynamicProperty {
public var publisher: Publisher { Defaults.publisher(key) }
public mutating func update() {
observable.key = key
_observable.update()
}