Skip to content

Commit ef67329

Browse files
committed
Fix UserDefaultsObserver
1 parent c2fdc1a commit ef67329

File tree

6 files changed

+102
-105
lines changed

6 files changed

+102
-105
lines changed

Core/Sources/Service/GUI/RealtimeSuggestionIndicatorController.swift

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -79,21 +79,12 @@ final class RealtimeSuggestionIndicatorController {
7979
}
8080
}
8181

82-
class UserDefaultsObserver: NSObject {
83-
var onChange: (() -> Void)?
84-
85-
override func observeValue(
86-
forKeyPath keyPath: String?,
87-
of object: Any?,
88-
change: [NSKeyValueChangeKey: Any]?,
89-
context: UnsafeMutableRawPointer?
90-
) {
91-
onChange?()
92-
}
93-
}
94-
9582
private let viewModel = IndicatorContentViewModel()
96-
private var userDefaultsObserver = UserDefaultsObserver()
83+
private var userDefaultsObserver = UserDefaultsObserver(
84+
object: UserDefaults.shared,
85+
forKeyPaths: [UserDefaultPreferenceKeys().realtimeSuggestionToggle.key],
86+
context: nil
87+
)
9788
private var windowChangeObservationTask: Task<Void, Error>?
9889
private var activeApplicationMonitorTask: Task<Void, Error>?
9990
private var editorObservationTask: Task<Void, Error>?
@@ -126,7 +117,7 @@ final class RealtimeSuggestionIndicatorController {
126117

127118
nonisolated init() {
128119
if ProcessInfo.processInfo.environment["IS_UNIT_TEST"] == "YES" { return }
129-
120+
130121
Task { @MainActor in
131122
observeEditorChangeIfNeeded()
132123
activeApplicationMonitorTask = Task { [weak self] in
@@ -152,17 +143,11 @@ final class RealtimeSuggestionIndicatorController {
152143

153144
Task { @MainActor in
154145
userDefaultsObserver.onChange = { [weak self] in
155-
Task { [weak self] in
146+
Task { @MainActor [weak self] in
156147
await self?.updateIndicatorVisibility()
157148
self?.updateIndicatorLocation()
158149
}
159150
}
160-
UserDefaults.shared.addObserver(
161-
userDefaultsObserver,
162-
forKeyPath: UserDefaultPreferenceKeys().realtimeSuggestionToggle.key,
163-
options: .new,
164-
context: nil
165-
)
166151
}
167152
}
168153

@@ -297,3 +282,4 @@ final class RealtimeSuggestionIndicatorController {
297282
viewModel.endPrefetch()
298283
}
299284
}
285+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Foundation
2+
3+
final class UserDefaultsObserver: NSObject {
4+
var onChange: (() -> Void)?
5+
private weak var object: NSObject?
6+
private let keyPaths: [String]
7+
8+
init(object: NSObject, forKeyPaths keyPaths: [String], context: UnsafeMutableRawPointer?) {
9+
self.object = object
10+
self.keyPaths = keyPaths
11+
super.init()
12+
for keyPath in keyPaths {
13+
object.addObserver(self, forKeyPath: keyPath, options: .new, context: context)
14+
}
15+
}
16+
17+
deinit {
18+
for keyPath in keyPaths {
19+
object?.removeObserver(self, forKeyPath: keyPath)
20+
}
21+
}
22+
23+
override func observeValue(
24+
forKeyPath keyPath: String?,
25+
of object: Any?,
26+
change: [NSKeyValueChangeKey: Any]?,
27+
context: UnsafeMutableRawPointer?
28+
) {
29+
onChange?()
30+
}
31+
}

Core/Sources/Service/Workspace.swift

Lines changed: 10 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,6 @@ final class Filespace {
6666

6767
@ServiceActor
6868
final class Workspace {
69-
class UserDefaultsObserver: NSObject {
70-
var onChange: (() -> Void)?
71-
72-
override func observeValue(
73-
forKeyPath keyPath: String?,
74-
of object: Any?,
75-
change: [NSKeyValueChangeKey: Any]?,
76-
context: UnsafeMutableRawPointer?
77-
) {
78-
onChange?()
79-
}
80-
}
81-
8269
struct SuggestionFeatureDisabledError: Error, LocalizedError {
8370
var errorDescription: String? {
8471
"Suggestion feature is disabled for this project."
@@ -97,7 +84,12 @@ final class Workspace {
9784
}
9885

9986
var realtimeSuggestionRequests = Set<Task<Void, Error>>()
100-
let userDefaultsObserver = UserDefaultsObserver()
87+
let userDefaultsObserver = UserDefaultsObserver(
88+
object: UserDefaults.shared, forKeyPaths: [
89+
UserDefaultPreferenceKeys().suggestionFeatureEnabledProjectList.key,
90+
UserDefaultPreferenceKeys().disableSuggestionFeatureGlobally.key,
91+
], context: nil
92+
)
10193

10294
private var _copilotSuggestionService: CopilotSuggestionServiceType?
10395

@@ -132,40 +124,12 @@ final class Workspace {
132124
return true
133125
}
134126

135-
deinit {
136-
UserDefaults.shared.removeObserver(
137-
userDefaultsObserver,
138-
forKeyPath: UserDefaultPreferenceKeys().suggestionFeatureEnabledProjectList.key
139-
)
140-
141-
UserDefaults.shared.removeObserver(
142-
userDefaultsObserver,
143-
forKeyPath: UserDefaultPreferenceKeys().disableSuggestionFeatureGlobally.key
144-
)
145-
}
146-
147127
private init(projectRootURL: URL) {
148128
self.projectRootURL = projectRootURL
149129

150-
Task {
151-
userDefaultsObserver.onChange = { [weak self] in
152-
guard let self else { return }
153-
_ = self.copilotSuggestionService
154-
}
155-
156-
UserDefaults.shared.addObserver(
157-
userDefaultsObserver,
158-
forKeyPath: UserDefaultPreferenceKeys().suggestionFeatureEnabledProjectList.key,
159-
options: .new,
160-
context: nil
161-
)
162-
163-
UserDefaults.shared.addObserver(
164-
userDefaultsObserver,
165-
forKeyPath: UserDefaultPreferenceKeys().disableSuggestionFeatureGlobally.key,
166-
options: .new,
167-
context: nil
168-
)
130+
userDefaultsObserver.onChange = { [weak self] in
131+
guard let self else { return }
132+
_ = self.copilotSuggestionService
169133
}
170134
}
171135

@@ -377,7 +341,7 @@ extension Workspace {
377341
}
378342
}
379343
}
380-
344+
381345
func isFilespaceExpired(fileURL: URL, availableTabs: Set<String>) -> Bool {
382346
let filename = fileURL.lastPathComponent
383347
if availableTabs.contains(filename) { return false }

Core/Sources/SuggestionWidget/SuggestionWidgetController.swift

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,6 @@ import SwiftUI
99

1010
@MainActor
1111
public final class SuggestionWidgetController: NSObject {
12-
class UserDefaultsObserver: NSObject {
13-
var onChange: (() -> Void)?
14-
15-
override func observeValue(
16-
forKeyPath keyPath: String?,
17-
of object: Any?,
18-
change: [NSKeyValueChangeKey: Any]?,
19-
context: UnsafeMutableRawPointer?
20-
) {
21-
onChange?()
22-
}
23-
}
24-
2512
private lazy var widgetWindow = {
2613
let it = CanBecomeKeyWindow(
2714
contentRect: .zero,
@@ -119,8 +106,20 @@ public final class SuggestionWidgetController: NSObject {
119106
let suggestionPanelViewModel = SuggestionPanelViewModel()
120107
let chatWindowViewModel = ChatWindowViewModel()
121108

122-
private var presentationModeChangeObserver = UserDefaultsObserver()
123-
private var colorSchemeChangeObserver = UserDefaultsObserver()
109+
private var presentationModeChangeObserver = UserDefaultsObserver(
110+
object: UserDefaults.shared,
111+
forKeyPaths: [
112+
UserDefaultPreferenceKeys().suggestionPresentationMode.key,
113+
], context: nil
114+
)
115+
private var colorSchemeChangeObserver = UserDefaultsObserver(
116+
object: UserDefaults.shared, forKeyPaths: [
117+
UserDefaultPreferenceKeys().widgetColorScheme.key,
118+
], context: nil
119+
)
120+
private var systemColorSchemeChangeObserver = UserDefaultsObserver(
121+
object: UserDefaults.standard, forKeyPaths: ["AppleInterfaceStyle"], context: nil
122+
)
124123
private var windowChangeObservationTask: Task<Void, Error>?
125124
private var activeApplicationMonitorTask: Task<Void, Error>?
126125
private var sourceEditorMonitorTask: Task<Void, Error>?
@@ -175,13 +174,6 @@ public final class SuggestionWidgetController: NSObject {
175174
guard let self else { return }
176175
self.updateWindowLocation()
177176
}
178-
179-
UserDefaults.shared.addObserver(
180-
presentationModeChangeObserver,
181-
forKeyPath: UserDefaultPreferenceKeys().suggestionPresentationMode.key,
182-
options: .new,
183-
context: nil
184-
)
185177
}
186178

187179
Task { @MainActor in
@@ -222,20 +214,9 @@ public final class SuggestionWidgetController: NSObject {
222214
colorSchemeChangeObserver.onChange = {
223215
updateColorScheme()
224216
}
225-
226-
UserDefaults.shared.addObserver(
227-
colorSchemeChangeObserver,
228-
forKeyPath: UserDefaultPreferenceKeys().widgetColorScheme.key,
229-
options: .new,
230-
context: nil
231-
)
232-
233-
UserDefaults.standard.addObserver(
234-
colorSchemeChangeObserver,
235-
forKeyPath: "AppleInterfaceStyle",
236-
options: .new,
237-
context: nil
238-
)
217+
systemColorSchemeChangeObserver.onChange = {
218+
updateColorScheme()
219+
}
239220
}
240221
}
241222
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Foundation
2+
3+
final class UserDefaultsObserver: NSObject {
4+
var onChange: (() -> Void)?
5+
private weak var object: NSObject?
6+
private let keyPaths: [String]
7+
8+
init(object: NSObject, forKeyPaths keyPaths: [String], context: UnsafeMutableRawPointer?) {
9+
self.object = object
10+
self.keyPaths = keyPaths
11+
super.init()
12+
for keyPath in keyPaths {
13+
object.addObserver(self, forKeyPath: keyPath, options: .new, context: context)
14+
}
15+
}
16+
17+
deinit {
18+
for keyPath in keyPaths {
19+
object?.removeObserver(self, forKeyPath: keyPath)
20+
}
21+
}
22+
23+
override func observeValue(
24+
forKeyPath keyPath: String?,
25+
of object: Any?,
26+
change: [NSKeyValueChangeKey: Any]?,
27+
context: UnsafeMutableRawPointer?
28+
) {
29+
onChange?()
30+
}
31+
}

ExtensionService/AppDelegate.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,13 +219,17 @@ private class UserDefaultsObserver: NSObject {
219219
) {
220220
onChange?(keyPath)
221221
}
222+
223+
deinit {
224+
removeObserver(self, forKeyPath: UserDefaultPreferenceKeys().realtimeSuggestionToggle.key)
225+
}
222226

223227
override init() {
224228
super.init()
225229
observe(keyPath: UserDefaultPreferenceKeys().realtimeSuggestionToggle.key)
226230
}
227231

228-
func observe(keyPath: String) {
232+
private func observe(keyPath: String) {
229233
UserDefaults.shared.addObserver(self, forKeyPath: keyPath, options: .new, context: nil)
230234
}
231235
}

0 commit comments

Comments
 (0)