I'm trying to get the selected text when the user presses a global shortcut. My app should read the text which is currently selected no matter if the currently focused app is chrome, safari or Microsoft Word.
I'm trying to do this using an AppleScript which does this using a hack with the clipboard. This doesn't work in all applications though.
func readSelectedText() {
let script = """
-- Back up clipboard contents:
set savedClipboard to the clipboard
-- Copy selected text to clipboard:
tell application "System Events" to keystroke "c" using {command down}
delay 1 -- Without this, the clipboard may have stale data.
set theSelectedText to the clipboard
delay 1 -- Without this delay, may restore clipboard before pasting.
-- Restore clipboard:
set the clipboard to savedClipboard
return theSelectedText
"""
var error: NSDictionary?
if let appleScript = NSAppleScript(source: script), let output = appleScript.executeAndReturnError(&error).stringValue {
print("Selected text:", output)
} else {
print("AppleScriptError: \(String(describing: error))")
}
}
Is there a robust way to do this in any app?
You can use the accessibility APIs to do this:
import ApplicationServices
import Cocoa
func getSelectedText() -> String? {
let systemWideElement = AXUIElementCreateSystemWide()
var selectedTextValue: AnyObject?
let errorCode = AXUIElementCopyAttributeValue(systemWideElement, kAXFocusedUIElementAttribute as CFString, &selectedTextValue)
if errorCode == .success {
let selectedTextElement = selectedTextValue as! AXUIElement
var selectedText: AnyObject?
let textErrorCode = AXUIElementCopyAttributeValue(selectedTextElement, kAXSelectedTextAttribute as CFString, &selectedText)
if textErrorCode == .success, let selectedTextString = selectedText as? String {
return selectedTextString
} else {
return nil
}
} else {
return nil
}
}
This requires setting Privacy - AppleEvents Sending Usage Description
in Info.plist and setting com.apple.security.temporary-exception.apple-events
to have com.apple.systemevents
.