Skip to content

Commit eb68608

Browse files
authored
Fix .setShortcut() crashing when called from a NSBackgroundActivityScheduler task (#9)
1 parent edae643 commit eb68608

File tree

3 files changed

+40
-7
lines changed

3 files changed

+40
-7
lines changed

Sources/KeyboardShortcuts/NSMenuItem++.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,14 @@ extension NSMenuItem {
5656
keyEquivalentModifierMask = shortcut.modifiers
5757
}
5858

59-
set()
59+
// `TISCopyCurrentASCIICapableKeyboardLayoutInputSource` works on a background thread, but crashes when used in a `NSBackgroundActivityScheduler` task, so we ensure it's not run in that queue.
60+
if DispatchQueue.isCurrentQueueNSBackgroundActivitySchedulerQueue {
61+
DispatchQueue.main.async {
62+
set()
63+
}
64+
} else {
65+
set()
66+
}
6067

6168
AssociatedKeys.observer[self] = NotificationCenter.default.addObserver(forName: .shortcutByNameDidChange, object: nil, queue: nil) { notification in
6269
guard

Sources/KeyboardShortcuts/Shortcut.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ private var keyToKeyEquivalentString: [KeyboardShortcuts.Key: String] = [
170170

171171
extension KeyboardShortcuts.Shortcut {
172172
fileprivate func keyToCharacter() -> String? {
173+
// `TISCopyCurrentASCIICapableKeyboardLayoutInputSource` works on a background thread, but crashes when used in a `NSBackgroundActivityScheduler` task, so we guard against that. It only crashes when running from Xcode, not in release builds, but it's probably safest to not call it from a `NSBackgroundActivityScheduler` no matter what.
174+
assert(!DispatchQueue.isCurrentQueueNSBackgroundActivitySchedulerQueue, "This method cannot be used in a `NSBackgroundActivityScheduler` task")
175+
173176
// Some characters cannot be automatically translated.
174177
if
175178
let key = key,
@@ -178,14 +181,19 @@ extension KeyboardShortcuts.Shortcut {
178181
return character
179182
}
180183

181-
let source = TISCopyCurrentASCIICapableKeyboardLayoutInputSource().takeUnretainedValue()
182-
let layoutData = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData)
183-
let dataRef = unsafeBitCast(layoutData, to: CFData.self)
184+
guard
185+
let source = TISCopyCurrentASCIICapableKeyboardLayoutInputSource()?.takeRetainedValue(),
186+
let layoutDataPointer = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData)
187+
else {
188+
return nil
189+
}
190+
191+
let dataRef = unsafeBitCast(layoutDataPointer, to: CFData.self)
184192
let keyLayout = unsafeBitCast(CFDataGetBytePtr(dataRef), to: UnsafePointer<CoreServices.UCKeyboardLayout>.self)
185193
var deadKeyState: UInt32 = 0
186-
let maxCharacters = 256
194+
let maxLength = 4
187195
var length = 0
188-
var characters = [UniChar](repeating: 0, count: maxCharacters)
196+
var characters = [UniChar](repeating: 0, count: maxLength)
189197

190198
let error = CoreServices.UCKeyTranslate(
191199
keyLayout,
@@ -195,7 +203,7 @@ extension KeyboardShortcuts.Shortcut {
195203
UInt32(LMGetKbdType()),
196204
OptionBits(CoreServices.kUCKeyTranslateNoDeadKeysBit),
197205
&deadKeyState,
198-
maxCharacters,
206+
maxLength,
199207
&length,
200208
&characters
201209
)

Sources/KeyboardShortcuts/util.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,3 +328,21 @@ final class ObjectAssociation<T: Any> {
328328
}
329329
}
330330
}
331+
332+
333+
extension DispatchQueue {
334+
/**
335+
Label of the current dispatch queue.
336+
337+
- Important: Only meant for debugging purposes.
338+
339+
```
340+
DispatchQueue.currentQueueLabel
341+
//=> "com.apple.main-thread"
342+
```
343+
*/
344+
static var currentQueueLabel: String { String(cString: __dispatch_queue_get_label(nil)) }
345+
346+
/// Whether the current queue is a `NSBackgroundActivityScheduler` task.
347+
static var isCurrentQueueNSBackgroundActivitySchedulerQueue: Bool { currentQueueLabel.hasPrefix("com.apple.xpc.activity.") }
348+
}

0 commit comments

Comments
 (0)