Skip to content
This repository was archived by the owner on Nov 17, 2025. It is now read-only.

Commit 7f1eea2

Browse files
steipeteclaude
andcommitted
Complete KeyboardShortcuts concurrency fixes for Swift 6
- Add @mainactor annotations to remaining methods accessing isolated properties - Fix notification observers to use main queue for thread safety - Update async stream handlers to use Task { @mainactor } for proper isolation - All concurrency-safety errors resolved for Swift 6 strict concurrency mode 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent b69b8a0 commit 7f1eea2

2 files changed

Lines changed: 27 additions & 7 deletions

File tree

LocalPackages/KeyboardShortcuts/Sources/KeyboardShortcuts/CarbonKeyboardShortcuts.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
import Carbon.HIToolbox
33

44
private func carbonKeyboardShortcutsEventHandler(eventHandlerCall: EventHandlerCallRef?, event: EventRef?, userData: UnsafeMutableRawPointer?) -> OSStatus {
5-
MainActor.assumeIsolated {
6-
CarbonKeyboardShortcuts.handleEvent(event)
7-
}
5+
// Need to handle the event synchronously since this is a Carbon callback
6+
return CarbonKeyboardShortcuts.handleEventSynchronously(event)
87
}
98

109
enum CarbonKeyboardShortcuts {
@@ -229,6 +228,13 @@ enum CarbonKeyboardShortcuts {
229228
}
230229
}
231230

231+
// Synchronous wrapper for Carbon callback
232+
fileprivate static func handleEventSynchronously(_ event: EventRef?) -> OSStatus {
233+
MainActor.assumeIsolated {
234+
handleEvent(event)
235+
}
236+
}
237+
232238
@MainActor
233239
fileprivate static func handleEvent(_ event: EventRef?) -> OSStatus {
234240
guard let event else {

LocalPackages/KeyboardShortcuts/Sources/KeyboardShortcuts/KeyboardShortcuts.swift

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ public enum KeyboardShortcuts {
138138
/**
139139
Unregister the given shortcut if it has no handlers.
140140
*/
141+
@MainActor
141142
private static func unregisterIfNeeded(_ shortcut: Shortcut) {
142143
guard !shortcutsForHandlers.contains(shortcut) else {
143144
return
@@ -149,6 +150,7 @@ public enum KeyboardShortcuts {
149150
/**
150151
Unregister the shortcut for the given name if it has no handlers.
151152
*/
153+
@MainActor
152154
private static func unregisterShortcutIfNeeded(for name: Name) {
153155
guard let shortcut = name.shortcut else {
154156
return
@@ -165,16 +167,17 @@ public enum KeyboardShortcuts {
165167
// TODO: Should remove user defaults too.
166168
}
167169

170+
@MainActor
168171
static func initialize() {
169172
guard !isInitialized else {
170173
return
171174
}
172175

173-
openMenuObserver = NotificationCenter.default.addObserver(forName: NSMenu.didBeginTrackingNotification, object: nil, queue: nil) { _ in
176+
openMenuObserver = NotificationCenter.default.addObserver(forName: NSMenu.didBeginTrackingNotification, object: nil, queue: .main) { _ in
174177
isMenuOpen = true
175178
}
176179

177-
closeMenuObserver = NotificationCenter.default.addObserver(forName: NSMenu.didEndTrackingNotification, object: nil, queue: nil) { _ in
180+
closeMenuObserver = NotificationCenter.default.addObserver(forName: NSMenu.didEndTrackingNotification, object: nil, queue: .main) { _ in
178181
isMenuOpen = false
179182
}
180183

@@ -188,6 +191,7 @@ public enum KeyboardShortcuts {
188191

189192
- Note: This method does not affect listeners using ``events(for:)``.
190193
*/
194+
@MainActor
191195
public static func removeAllHandlers() {
192196
let shortcutsToUnregister = shortcutsForLegacyHandlers.subtracting(shortcutsForStreamHandlers)
193197

@@ -208,6 +212,7 @@ public enum KeyboardShortcuts {
208212

209213
- Note: This method does not affect listeners using ``events(for:)``.
210214
*/
215+
@MainActor
211216
public static func removeHandler(for name: Name) {
212217
legacyKeyDownHandlers[name] = nil
213218
legacyKeyUpHandlers[name] = nil
@@ -234,6 +239,7 @@ public enum KeyboardShortcuts {
234239

235240
- Tip: Use ``disable(_:)-(Name...)`` and ``enable(_:)-(Name...)`` to change the status.
236241
*/
242+
@MainActor
237243
public static func isEnabled(for name: Name) -> Bool {
238244
guard
239245
isEnabled,
@@ -248,6 +254,7 @@ public enum KeyboardShortcuts {
248254
/**
249255
Disable the keyboard shortcut for one or more names.
250256
*/
257+
@MainActor
251258
public static func disable(_ names: [Name]) {
252259
for name in names {
253260
guard let shortcut = getShortcut(for: name) else {
@@ -261,13 +268,15 @@ public enum KeyboardShortcuts {
261268
/**
262269
Disable the keyboard shortcut for one or more names.
263270
*/
271+
@MainActor
264272
public static func disable(_ names: Name...) {
265273
disable(names)
266274
}
267275

268276
/**
269277
Enable the keyboard shortcut for one or more names.
270278
*/
279+
@MainActor
271280
public static func enable(_ names: [Name]) {
272281
for name in names {
273282
guard let shortcut = getShortcut(for: name) else {
@@ -281,6 +290,7 @@ public enum KeyboardShortcuts {
281290
/**
282291
Enable the keyboard shortcut for one or more names.
283292
*/
293+
@MainActor
284294
public static func enable(_ names: Name...) {
285295
enable(names)
286296
}
@@ -308,6 +318,7 @@ public enum KeyboardShortcuts {
308318
}
309319
```
310320
*/
321+
@MainActor
311322
public static func reset(_ names: [Name]) {
312323
for name in names {
313324
setShortcut(name.defaultShortcut, for: name)
@@ -335,6 +346,7 @@ public enum KeyboardShortcuts {
335346
}
336347
```
337348
*/
349+
@MainActor
338350
public static func reset(_ names: Name...) {
339351
reset(names)
340352
}
@@ -360,6 +372,7 @@ public enum KeyboardShortcuts {
360372
}
361373
```
362374
*/
375+
@MainActor
363376
public static func resetAll() {
364377
reset(allNames.toArray())
365378
}
@@ -515,6 +528,7 @@ public enum KeyboardShortcuts {
515528
private static func userDefaultsKey(for shortcutName: Name) -> String { "\(userDefaultsPrefix)\(shortcutName.rawValue)"
516529
}
517530

531+
@MainActor
518532
static func userDefaultsDidChange(name: Name) {
519533
// TODO: Use proper UserDefaults observation instead of this.
520534
NotificationCenter.default.post(name: .shortcutByNameDidChange, object: nil, userInfo: ["name": name])
@@ -601,7 +615,7 @@ extension KeyboardShortcuts {
601615
AsyncStream { continuation in
602616
let id = UUID()
603617

604-
DispatchQueue.main.async {
618+
Task { @MainActor in
605619
streamKeyDownHandlers[name, default: [:]][id] = {
606620
continuation.yield(.keyDown)
607621
}
@@ -614,7 +628,7 @@ extension KeyboardShortcuts {
614628
}
615629

616630
continuation.onTermination = { _ in
617-
DispatchQueue.main.async {
631+
Task { @MainActor in
618632
streamKeyDownHandlers[name]?[id] = nil
619633
streamKeyUpHandlers[name]?[id] = nil
620634

0 commit comments

Comments
 (0)