From 0c4a0f55dedb6df559a32376bd64b0341a273a7a Mon Sep 17 00:00:00 2001 From: Wouter Hennen Date: Thu, 18 Jan 2024 04:12:42 +0100 Subject: [PATCH 1/5] Fix for freezes in MenuBarExtra & NavigationStack --- Sources/Defaults/Defaults.swift | 2 + Sources/Defaults/SwiftUI.swift | 73 +++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/Sources/Defaults/Defaults.swift b/Sources/Defaults/Defaults.swift index d6c3036..61fbf33 100644 --- a/Sources/Defaults/Defaults.swift +++ b/Sources/Defaults/Defaults.swift @@ -36,6 +36,8 @@ public enum Defaults { } public typealias _Defaults = Defaults + +@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) public typealias _Default = Default extension Defaults { diff --git a/Sources/Defaults/SwiftUI.swift b/Sources/Defaults/SwiftUI.swift index c6c8fb1..73bfefd 100644 --- a/Sources/Defaults/SwiftUI.swift +++ b/Sources/Defaults/SwiftUI.swift @@ -6,14 +6,18 @@ 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 oldValue != key { + observe() + } + } + } var value: Value { get { Defaults[key] } set { - objectWillChange.send() Defaults[key] = newValue } } @@ -21,35 +25,40 @@ extension Defaults { init(_ key: Key) { self.key = key - // 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, *) { - // 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 - for await _ in Defaults.updates(key) { - guard let self else { - return - } - - self.objectWillChange.send() - } - } - } else { - self.cancellable = Defaults.publisher(key, options: [.prior]) - .sink { [weak self] change in - guard change.isPrior else { - return - } - - Task { @MainActor in - self?.objectWillChange.send() - } - } - } + 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. + task = .detached(priority: .userInitiated) { @MainActor [weak self, key] in + for await _ in Defaults.updates(key) { + guard let self else { + return + } + + self.objectWillChange.send() + } + } + } else { + cancellable = Defaults.publisher(key, options: [.prior]) + .sink { [weak self] change in + guard change.isPrior else { + return + } + + Task { @MainActor in + self?.objectWillChange.send() + } + } + } + } /** Reset the key back to its default value. @@ -65,14 +74,14 @@ Access stored values from SwiftUI. This is similar to `@AppStorage` but it accepts a ``Defaults/Key`` and many more types. */ +@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) @propertyWrapper public struct Default: DynamicProperty { public typealias Publisher = AnyPublisher, Never> 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() } @@ -149,6 +159,7 @@ public struct Default: DynamicProperty { } } +@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) extension Default where Value: Equatable { /** Indicates whether the value is the same as the default value. From e9214de31c90c4020030ba14f644888da41c82e3 Mon Sep 17 00:00:00 2001 From: Wouter Hennen Date: Thu, 18 Jan 2024 04:17:21 +0100 Subject: [PATCH 2/5] Spaces -> tabs --- Sources/Defaults/SwiftUI.swift | 80 +++++++++++++++++----------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/Sources/Defaults/SwiftUI.swift b/Sources/Defaults/SwiftUI.swift index 73bfefd..173ba13 100644 --- a/Sources/Defaults/SwiftUI.swift +++ b/Sources/Defaults/SwiftUI.swift @@ -6,14 +6,14 @@ extension Defaults { final class Observable: ObservableObject { private var cancellable: AnyCancellable? private var task: Task? - - var key: Defaults.Key { - didSet { - if oldValue != key { - observe() - } - } - } + + var key: Defaults.Key { + didSet { + if oldValue != key { + observe() + } + } + } var value: Value { get { Defaults[key] } @@ -21,44 +21,44 @@ extension Defaults { Defaults[key] = newValue } } - + init(_ key: Key) { self.key = key - - observe() + + 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. - task = .detached(priority: .userInitiated) { @MainActor [weak self, key] in - for await _ in Defaults.updates(key) { - guard let self else { - return - } - - self.objectWillChange.send() - } - } - } else { - cancellable = Defaults.publisher(key, options: [.prior]) - .sink { [weak self] change in - guard change.isPrior else { - return - } - - Task { @MainActor in - self?.objectWillChange.send() - } - } - } - } + + 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. + task = .detached(priority: .userInitiated) { @MainActor [weak self, key] in + for await _ in Defaults.updates(key) { + guard let self else { + return + } + + self.objectWillChange.send() + } + } + } else { + cancellable = Defaults.publisher(key, options: [.prior]) + .sink { [weak self] change in + guard change.isPrior else { + return + } + + Task { @MainActor in + self?.objectWillChange.send() + } + } + } + } /** Reset the key back to its default value. From 97e8c359e1fcc92ca4a665c590d6683b0f66f040 Mon Sep 17 00:00:00 2001 From: Wouter Hennen Date: Thu, 18 Jan 2024 04:19:09 +0100 Subject: [PATCH 3/5] Spaces -> tabs --- Sources/Defaults/SwiftUI.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Defaults/SwiftUI.swift b/Sources/Defaults/SwiftUI.swift index 173ba13..3707cfa 100644 --- a/Sources/Defaults/SwiftUI.swift +++ b/Sources/Defaults/SwiftUI.swift @@ -108,7 +108,7 @@ public struct Default: DynamicProperty { */ public init(_ key: Defaults.Key) { self.key = key - self._observable = .init(wrappedValue: .init(key)) + self._observable = .init(wrappedValue: .init(key)) } public var wrappedValue: Value { @@ -131,7 +131,7 @@ public struct Default: DynamicProperty { public var publisher: Publisher { Defaults.publisher(key) } public mutating func update() { - observable.key = key + observable.key = key _observable.update() } From 28c91b05b84c69a48298d07d50ad4937584d4aaa Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 18 Jan 2024 16:22:51 +0700 Subject: [PATCH 4/5] Update SwiftUI.swift --- Sources/Defaults/SwiftUI.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/Defaults/SwiftUI.swift b/Sources/Defaults/SwiftUI.swift index 3707cfa..f9f1ff9 100644 --- a/Sources/Defaults/SwiftUI.swift +++ b/Sources/Defaults/SwiftUI.swift @@ -21,7 +21,7 @@ extension Defaults { Defaults[key] = newValue } } - + init(_ key: Key) { self.key = key @@ -36,13 +36,14 @@ extension Defaults { // 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. task = .detached(priority: .userInitiated) { @MainActor [weak self, key] in for await _ in Defaults.updates(key) { guard let self else { return } - + self.objectWillChange.send() } } @@ -52,7 +53,7 @@ extension Defaults { guard change.isPrior else { return } - + Task { @MainActor in self?.objectWillChange.send() } From e302050db113b3e52d176b6d9b5abbc30295988e Mon Sep 17 00:00:00 2001 From: Wouter Hennen Date: Thu, 18 Jan 2024 13:39:08 +0100 Subject: [PATCH 5/5] fixes --- Sources/Defaults/Defaults.swift | 2 -- Sources/Defaults/SwiftUI.swift | 5 +---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/Sources/Defaults/Defaults.swift b/Sources/Defaults/Defaults.swift index 61fbf33..d6c3036 100644 --- a/Sources/Defaults/Defaults.swift +++ b/Sources/Defaults/Defaults.swift @@ -36,8 +36,6 @@ public enum Defaults { } public typealias _Defaults = Defaults - -@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) public typealias _Default = Default extension Defaults { diff --git a/Sources/Defaults/SwiftUI.swift b/Sources/Defaults/SwiftUI.swift index f9f1ff9..d57146f 100644 --- a/Sources/Defaults/SwiftUI.swift +++ b/Sources/Defaults/SwiftUI.swift @@ -9,7 +9,7 @@ extension Defaults { var key: Defaults.Key { didSet { - if oldValue != key { + if key != oldValue { observe() } } @@ -75,7 +75,6 @@ Access stored values from SwiftUI. This is similar to `@AppStorage` but it accepts a ``Defaults/Key`` and many more types. */ -@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) @propertyWrapper public struct Default: DynamicProperty { public typealias Publisher = AnyPublisher, Never> @@ -160,7 +159,6 @@ public struct Default: DynamicProperty { } } -@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) extension Default where Value: Equatable { /** Indicates whether the value is the same as the default value. @@ -168,7 +166,6 @@ extension Default where Value: Equatable { public var isDefaultValue: Bool { wrappedValue == defaultValue } } -@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) extension Defaults { /** A SwiftUI `Toggle` view that is connected to a ``Defaults/Key`` with a `Bool` value.