Applescript to Swift Language (application control) - macos

I wasn't sure what to call the title, but I'm working on an Applescript that pulls/pushes information from iTunes and save it. During the development of my Applescript I figured that if I'm going to learn a language, I'm going learn Swift as I want to develop more advanced programs down the road. Even though Applescript is as useful, and what I'm doing is working in Applescript... I wanted to take the next step in Learning to develop programs and scripts using Swift. I want to be able to replicate what I was able to do in Applescript using Swift.
What I wanted to know is if there is a tutorial/guide on how to read data from the accessibility inspector/etc and control programs like applescript is capable of doing, etc...
To give you a background on what I was able to do in Applescript, it's a really basic version of Batch Apple ID Creator. I work for a small school district that is now using a MDM to manage the i-devices. Well, with the MDM, each device needs to have it's own apple ID and manually creating 500+ apple ID is not as much fun as it sounds. These are some one the steps that I recreated in Applescript.
Check to see if iTunes is running
Check to see if there is a current user logged in.
Click "Sign Out" under menu bar if current user is logged in.
Goto "Create Apple ID..."
etc.
I took the current script that is up on that Git repo and made some changes to it so it works with the latest version of iTunes (12.1.2). After making some changes, I figures I wanted to learn and started rewriting the script to fully understand how it works and to learn Applescript syntax.
Let me know if you need more information about what I'm trying to do.
Throdne

I am investigating this sort of thing myself. What amazes me is that you can use Swift as a scripting language. I've written several short scripts that use the simple 'shebang' line #!/usr/bin/swift
To access AppleScript functionality you almost have to call AppleScript (through Cocoa) from your Swift code.
Here's an example I found elsewhere:
Swift Code
let myAppleScript = "..."
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: myAppleScript) {
if let output: NSAppleEventDescriptor = scriptObject.executeAndReturnError(
&error) {
println(output.stringValue)
} else if (error != nil) {
println("error: \(error)")
}
}
try searching for 'call AppleScript from Swift' (or Cocoa) or 'call AppleEvents from Swift' (or Cocoa).

Each language has a specific purpose. AppleScript's strength is interprocess scripting through AppleEvents, while Swift's strength is programming large full-featured applications. With AppleScript, you can get the contents of a webpage from Safari using JavaScript, parse it with shell script, and send it in an e-mail with Mail. Most languages can't do such a thing as easily as you can do it in AppleScript (Swift included). They are complementary. You can do amazing things by learning both, but they each have their own limitations and strengths.

Related

Programmatically enable (or disable) AppleScript support for an application

I have an App Store app in which the free version is not scriptable, but the premium version is. AppleScript support is one of the key differences. I know the App Store reviewers are pushing more and more towards free + in-app-purchase, which will help declutter the App Store. Fine, I'll play ball.
Now I need to do something programmatically that I've always just worked into the build.
Is there a way to disable AppleScript if my OSAScriptingDefinition and NSAppleScriptEnabled are set in my Info.plist? This would still allow people to open the dictionary, and maybe they'd like what they see and consider activating the upgrade. Or,
Is there a way to enable AppleScript after the fact? Obviously with code-signing, I can't do things like modify Info.plist, or add my SDEF to the bundle later. But maybe if the SDEF were in a non-standard location, I could load it from the bundle and tell the system about it manually.
Does the SDEF have to live in my bundle? If not, I'm not sure how to point to the user's Application Support directory in the sandbox. I've also considered xinclude an SDEF I can install after the fact, but again, the SDEF and plist require actual directory paths and not functions.
I've tried a couple of things such as attempting to set NSScriptSuiteRegistry's singleton to nil, to no effect.
Because OSAScriptingDefinition and NSAppleScriptEnabled enable "automatic" support, surely there must be a manual way to make them to effect if not in the plist, and hopefully with a public API.
Any ideas here? Thanks!
A few points, for orientation:
All AppleScript commands are subclasses of NSScriptCommand
All AppleScript objects are represented by subclasses of
NSScriptObjectSpecifier
The scriptability of an app is controlled by its shared instance of NSScriptSuiteRegistry
This gives you a few options. You could try, for instance, overriding NSScriptSuiteRegistry setSharedScriptSuiteRegistry: and setting it to nil for the free version. You could also write a category on NSScriptCommand and/or NSScriptObjectSpecifier that does a version check. That would give you fine-grained control: you could call it from any methods that handle a script command or returns a script object, and decide on the fly which you want to allow and which you want to block; maybe even pop up a 'Pay for Full AppleScript Access' dialog.
CocoaScripting is a black box and not very adaptable. Simplest (kludgy) solution would be to wait until CS has installed its Apple event handlers then call -[NSAppleEventManager setEventHandler:andSelector:forEventClass:andEventID:] to replace those with a dummy handler that always sends back a "requires in-app purchase" error. (Don’t replace the standard open, quit, etc. handlers obviously.)

How to let osascript not build applescript code which will not hit at runtime?

We have a script to send email using Microsoft outlook or Apple mail application. It will dynamically load the default email from system preference (maybe user input also), and using it to decide which mail client to use.
So the code is as following:
if (mailClientStr contains "outlook")
tell application id "com.microsoft.outlook"
-- <<< there will be error if there is no outlook installed
-- <<< even else branch will be run.
...
end tell
else
tell application id "com.apple.mail"
...
end tell
end if
On an machine which doesn't have outlook installed, and the mailClientStr will be "com.apple.mail", but this script cannot be run by osascript
It complains Can’t get application id "com.microsoft.outlook" even the first branch will not be executed. My understanding is osascript will need to access Outlook apple script interface when load and compile this script (before run it).
I can separate the outlook related code into a separate script, but because there is a lot of data to passing, it will be complex, so I don't want this workaround.
So does there any solution from the apple script language side?
From the AppleScript Language Guide:
Entering Script Information in Raw Format
You can enter double angle brackets, or chevrons («»), directly into a script by typing Option-Backslash and Shift-Option-Backslash. You might want to do this if you’re working on a script that needs to use terminology that isn’t available on your current machine—for example, if you’re working at home and don’t have the latest dictionary for a scriptable application you are developing, but you know the codes for a supported term.
You can also use AppleScript to display the underlying codes for a script, using the following steps:
Create a script using standard terms compiled against an available application or scripting addition.
Save the script as text and quit Script Editor.
Remove the application or scripting addition from the computer.
Open the script again and compile it.
When AppleScript asks you to locate the application or scripting addition, cancel the dialog.
Script Editor can compile the script, but displays chevron format for any terms that rely on a missing dictionary

Toggle enable/disable for Automator app

I've got a monstrously large Automator application designed to be used by others without advanced-level computer skills. Basically, at a couple of points, I'd like users to be prompted to enable or disable the action that follows. The closest answer I've seen is:
automator enable/disable/delete action in run applescript
This is promising - I'm guessing this applescript method is the way forward. But this works for Automator workflows, not applications. I know so little about Applescript - even if it is possible, I wouldn't know how to do it. Anybody have any ideas?
I suggest breaking up your huge workflow into smaller pieces.
One workflow can call other workflows, but workflows don't have a conditional statement. There is no way to do an if-statement, a conditional for a workflow.
Automator actions such as "Run Shell Script" and "Run AppleScript" both have conditionals in them (if-statements).
AppleScript allows the ability to call upon applications to get things done, and allows you to display simple dialogs.
Shell scripts allow you to go deep inside of OS X, harnessing the power of Unix to work for you.
Both AppleScript and shell scripting would be useful for you to add to your knowledge of Automator.
AppleScript was actually designed to work with Mac applications. Look at the application:
/Applications/Utilities/AppleScript Editor.app
Beginner's tutorial on AppleScript:
Beginner's tutorial on the Mac's Unix command-line:
-- Kaydell
kaydell#yahoo.com
http://learnbymac.com

Use Automator as an Applescript learning tool?

I am trying to teach myself AppleScript. Is Automator based on AppleScript? It occurs to me that if there is a way to view the AppleScript "guts" of an Automator action, that would be a great learning tool. But is that possible?
Thanks!
The best way to learn AppleScript is indeed to learn by example. MacScripter is the best place to start, without any doubt. Checkout the tutorials section and follow threads. Most problems you come across are already answered at MacScripter.
Automator is not based on AppleScript, it uses ScriptingBridge and mostly private API's. ScriptingBridge is an alternative to AppleScript for Cocoa developers (Objective-C), the syntax differs however greatly from AppleScript.
In conclusion, Automator is certainly not the place to start learning AppleScript.
Go to the link below and look at the "AppleScript Tutorial for Beginners" series. It's how I learned. Also you have a folder full of applescripts if you want to see a variety of code. Find it here: /Library/Scripts/. Also, under the help menu of AppleScript Editor is the "Applescript language guide". Finally, you will have to learn the terminology of the scriptable applications, so in AppleScript Editor file menu choose "Open Dictionary..." and choose an application in the list to see its applescript terms.
http://macscripter.net/viewtopic.php?id=25631

AppleScript Editor record doesn't work

I have opened the AppleScript Editor and pressed Record button.
Then I run TextEdit, create a file and put some text there.
When I click the Stop button in AppleScript Editor, nothing was recorded, the window is blank.
What is the problem?
You can use the Record feature of the Automator to record the UI interaction steps needed to do the relevant workflow. Then you can then literally select and copy the recorded steps in automator and paste them into a new Applescript Editor window. This will give you applescript which may or may not work. You'll probably want/need to edit the resulting script, but at least it should help give an idea what is needed to achieve your workflow programatically. This method is usable regardless of whether or not the target application has an applescript dictionary or supports the AppleScript Editor Record button, as it is the interaction with the underlying UI elements which is recorded.
Steps:
Open Automator
Start a new "Workflow"
Start recording
Perform whatever steps you require with your app (in this case typing into textedit)
Stop recording
This will create a list of actions in Automator like:
Select all these and copy (CMD+c)
Open the Applescript Editor app
Paste (CMD+v). The result will be valid applescript to perform the actions you just recorded:
Note that as is generally the case with UI automation, the automator records steps exactly and the script plays them back exactly. This my not be exactly what you want - e.g. if a different application were active, the text could get typed in there instead. The generated applescript should be used as a guide to the final applescript.
The problem is that applications need to explicitly support AppleScript recording in order for it to work, but almost no applications actually do. Finder still supports it a bit, and maybe a couple other apps (BBEdit comes to mind), but for the most part, AppleScript recording has been pretty useless for quite some time.
Not all apps are recordable (in fact, only a small handful are). Recordablity is something each app needs to implement, and I guess TextEdit isn't recordable.

Resources