Skip to content

Commit

Permalink
Fix for freeze in MenuBarExtra and NavigationStack (#158)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wouter01 committed Jan 18, 2024
1 parent 46dfe48 commit 2dad0e4
Showing 1 changed file with 24 additions and 14 deletions.
38 changes: 24 additions & 14 deletions Sources/Defaults/SwiftUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,39 @@ 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
}
}

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
Expand All @@ -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
Expand All @@ -47,10 +61,6 @@ extension Defaults {
}
}

deinit {
task?.cancel()
}

/**
Reset the key back to its default value.
*/
Expand All @@ -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`.
Expand All @@ -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 {
Expand All @@ -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()
}

Expand Down

0 comments on commit 2dad0e4

Please sign in to comment.