1

I have used AppleScript to get selected text from third party app. Which is working fine in osx 10.13 but stopped working in osx 10.14.

From searching, got one suggestion to add "NSAppleEventsUsageDescription" in info.plist, but that is also not working for me.

let latestApp = "Safari"

        //Write script to activate the app and get the selected text to our app
        let script = """
        tell application \"\(latestApp)\"
        activate
        end tell
        tell application \"System Events\"
        tell process \"\(latestApp)\"
        keystroke \"c\" using {command down}
        delay 0.1
        set myData to (the clipboard) as text
        return myData
        end tell
        end tell
        """
        let scriptObject = NSAppleScript.init(source: script)
        let errorDict: AutoreleasingUnsafeMutablePointer<NSDictionary?>? = nil
        var returnDescription:NSAppleEventDescriptor? = nil
        returnDescription = scriptObject?.executeAndReturnError(errorDict)
        if( returnDescription != nil ){
            if( kAENullEvent != returnDescription?.descriptorType ){ //successful execution
                if( cAEList == returnDescription?.descriptorType ){
                    print("return  value")
                }else{
                    print("Returned string : \(String(describing: returnDescription?.stringValue))")
                    let selectedStr = returnDescription?.stringValue!
                    if( (selectedStr?.count)! > 0 ){
                        print("selectedStr is :\(String(describing: selectedStr))")
                    }
                }
            }

        }else{
            print("Error is : \(String(describing: errorDict))")

        }

It works perfectly in os 10.12 & 10.13 & ScriptEditor also.

enter image description here

Krishna Maru
  • 164
  • 13
  • See [Reading currently playing track in macOS using ScriptingBridge not working](https://stackoverflow.com/questions/55868521/reading-currently-playing-track-in-macos-using-scriptingbridge-not-working) – Willeke Apr 30 '19 at 13:44
  • @Willeke, i added "NSAppleEventsUsageDescription" to info.plist, set "App Sandbox" No in entitlements , Capabilities also, app sandbox is off. but still it doesn't work. If i select any text in Safari and run appleScript from my application (by interacting with button i set appleScript in), Safari app do get activated, but i am not getting selected data. – Krishna Maru May 01 '19 at 04:34
  • i have also used accessibility in my app, so as i run my app from Xcode now, Xcode is added in "security & Privacy" -> "Accessibility" – Krishna Maru May 01 '19 at 05:16
  • A new security feature in Mojave revolves around a system dialogue that prompts you to grant access to apps that want to control other apps. i am not getting any such dialog also. i am only getting one dialog box at starting for the accessibility permission to give permission to my app in accessibility in system preference. – Krishna Maru May 01 '19 at 05:38
  • Please show actual code in context. What is latestApp? What do you do with script? Provide an MCVE. – matt May 01 '19 at 12:06
  • @matt, i have updated the question with code, which i run. In latestApp, i dynamically pass App name (ie. Safari,Notes, Pages etc) – Krishna Maru May 01 '19 at 12:13
  • Have you gotten the AppleScript part to work from within Script Editor? – matt May 01 '19 at 12:22
  • yes, it do work. i attached the screenshot. And it was working perfectly for me in osx 10.12 & 10.13. its not working in 10.14 only – Krishna Maru May 01 '19 at 12:26
  • I think there is something missing related to permission, but i m not getting what. As mojave has some security changes, and if we run appleScript we are support to get permission popup first. But that popup only doesn't appear for me. – Krishna Maru May 01 '19 at 12:30
  • Under Security & Privacy -> Accessibility, is System Events checked? – matt May 01 '19 at 14:04

2 Answers2

4

Since you are telling "Safari" to activate, having "System Events" to tell process "Safari"... Is not necessary. Simply using "System Events" to keystroke "c" using {command down} accomplishes the same thing. It's not a huge deal but eliminating unnecessary lines of code here and there, makes navigating through code easier and cleaner. In addition, without adding an additional delay 0.3 before the keystroke "c" using {command down} command, returned an empty clipboard on my system 50% of the time.

This AppleScript code works for me using the latest version of macOS Mojave.

tell application "Safari" to activate
delay 0.2 -- Adjust As Needed
tell application "System Events" to keystroke "c" using {command down}
set myData to (the clipboard) as text

Since clipboard commands are handled by Standard Additions and not System Events (as mentioned by @user3439894 in his comment), removing set myData to (the clipboard) as text from the System Events tell block, allowed me to successfully remove the delay 0.1 command.

OR OPTION 2

Actually, on second thought, if you are looking to only use this in Safari, this following one line of AppleScript code will do what you need.

You must enable the Allow JavaScript from Apple Events option in Safari's Develop menu to use do JavaScript.

tell application "Safari" to set myData to (do JavaScript "''+document.getSelection()" in document 1)

I have only addressed the AppleScript part because @matt thoroughly covered every other issue in his post.

wch1zpink
  • 3,026
  • 1
  • 8
  • 19
  • 2
    I'd also point out that `set myData to (the clipboard) as text` and `return myData` really do not belong with the `tell application "System Events"` _block_ as they are part of **StandardAdditions**, not **System Events**. As to the `delay` _command_ before **System Events** preforms a `keystroke` _command_, in this case I'd use a `repeat` _loop_ within the **System Events** `tell` _block_, e.g.: `repeat until (name of every process whose frontmost is true) = {"Safari"}` followed by a `delay 0.01` and close the _loop_ with `end repeat`. – user3439894 May 01 '19 at 17:32
  • 1
    I know @user3439894 has given you some very good, sensible critiques. The script may not be perfect, but nonetheless, this is probably one of my favourite ever answers I've read from you. You seem to be conscientious here in troubleshooting the OP's scripting issues, which—as you say—may not be life-or-death, but it's really good to see you attending to the minutiae and passing the ethos on with regards to producing cleaner, leaner code and simple programming tips. `delay 0.3` may not be the gold standard here but you reasoned it out to the OP and made his script better. +1 for all that. – CJK May 02 '19 at 10:13
  • @user3439894 @CJK My first version of the code actually did use a `repeat until` loop rather than a `delay .3`. I chose not to use it because more than 50% of the time it returned an empty clipboard, whereas `delay .3` proved successful 100% of the time (on my system, while having 8 different Desktop Spaces active with 6 other applications running). – wch1zpink May 02 '19 at 14:54
  • @wch1zpink, Which goes to show once again that UI Scripting is unreliable and should be avoided unless there is just no other way to get the job done! :) – user3439894 May 02 '19 at 15:11
  • Don't forget to mention that to use **JaveScript** in Option 2 of your answer that **Allow JavaScript from Apple Events** on the, hidden by default, **Develop** menu in **Safari** must be checked. – user3439894 May 02 '19 at 15:59
2

You say "it worked perfectly" in previous systems. I find that difficult to believe, since almost everything about your code is wrong. I corrected your code and got your script to work, with very little difficulty.

I'll try to describe what I did.

To prepare the ground, I ran a version of your script in Script Editor (removing the backslashes and string interpolation of course):

tell application "Safari"
    activate
end tell
tell application "System Events"
    tell process "Safari.app"
        keystroke "c" using {command down}
        delay 0.1
        set myData to (the clipboard) as text
        return myData
    end tell
end tell

The script didn't run at first, but a dialog sent me to System Preferences -> Security & Privacy -> Accessibility, where I checked Script Editor and System Events.

enter image description here

Now I was ready to create the app. My app is called AnotherAppleScriptExample. In its entitlements, sandboxing is NO.

enter image description here

In its Info.plist is this entry:

enter image description here

My version of your code (fixing the various Swift mistakes) is this:

        let app = "Safari.app"
        let script = """
        tell application "\(app)"
            activate
        end tell
        tell application "System Events"
            tell process "\(app)"
                keystroke "c" using {command down}
                delay 0.1
                set myData to (the clipboard) as text
                return myData
            end tell
        end tell
        """
        if let scriptObject = NSAppleScript(source: script) {
            var error: NSDictionary? = nil
            let result = scriptObject.executeAndReturnError(&error)
            if( kAENullEvent != result.descriptorType ){
                print(result.stringValue as Any)
            }
        }

I ran the app. I got two dialogs. First this:

enter image description here

I clicked OK. Then this:

enter image description here

I clicked Open System Preferences. In System Preferences, I checked my app (now both System Events and my app are checked):

enter image description here

Now the ground is fully prepared. I quit the app and ran it again. The script worked correctly, printing the selection in Safari.

matt
  • 515,959
  • 87
  • 875
  • 1,141