From 2dad0e446e0b2acffbe65846030ec2de81995061 Mon Sep 17 00:00:00 2001 From: Wouter Hennen <62355975+Wouter01@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:10:16 +0100 Subject: [PATCH] Fix for freeze in `MenuBarExtra` and `NavigationStack` (#158) --- Sources/Defaults/SwiftUI.swift | 38 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/Sources/Defaults/SwiftUI.swift b/Sources/Defaults/SwiftUI.swift index 4e58ce5..0fc7287 100644 --- a/Sources/Defaults/SwiftUI.swift +++ b/Sources/Defaults/SwiftUI.swift @@ -6,25 +6,39 @@ extension Defaults { final class Observable: ObservableObject { private var cancellable: AnyCancellable? private var task: Task? - private let key: Defaults.Key - - let objectWillChange = ObservableObjectPublisher() + + var key: Defaults.Key { + didSet { + if key != oldValue { + observe() + } + } + } var value: Value { get { Defaults[key] } set { - objectWillChange.send() Defaults[key] = newValue } } init(_ key: Key) { 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: DynamicProperty { private let key: Defaults.Key - // Intentionally using `@ObservedObjected` over `@StateObject` so that the key can be dynamically changed. - @ObservedObject private var observable: Defaults.Observable + @StateObject private var observable: Defaults.Observable /** 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: DynamicProperty { */ public init(_ key: Defaults.Key) { self.key = key - self.observable = .init(key) + self._observable = .init(wrappedValue: .init(key)) } public var wrappedValue: Value { @@ -122,6 +131,7 @@ public struct Default: DynamicProperty { public var publisher: Publisher { Defaults.publisher(key) } public mutating func update() { + observable.key = key _observable.update() }