ScriptingBridge Framework, Scripting Definitions, & AppleScriptObjC

Hi all,

I’m not fully understanding how to utilize application scripting definitions in conjunction with the ScriptingBridge framework and AppleScriptObjC. Using Terminal.app as an example…

I understand how to do things such as the following:


set terminalApp to current application's SBApplication's applicationWithBundleIdentifier_("com.apple.Terminal") -- connect to the app
terminalApp's doScript_in_("say hello", null) -- execute script in a new window
set allTerminalWindows to terminalApp's |windows|() -- get all windows
set activeWindow to item (count items of allTerminalWindows) of allTerminalWindows -- get last window in array

But I am struggling with how to successfully use some of the other definitions. For instance, I can’t seem to get a Terminal window to close using this:


activeWindow's closeSaving_savingIn_(false, null)

--OR--

activeWindow's closeSaving_savingIn_((terminalApp's TerminalSaveOptionsNo), null)

I’ve looked through the Apple documentation on using ScriptingBridge, and browsed around some forums for examples, but am coming up empty-handed. I thought I was on to something with SBApplication’s classForScriptingClass, but hit a wall again. Could someone explain how to work with the multiple objects found in the class declaration and also the enum arrays? Thanks in advance, and here’s the Terminal.app definitions for reference…


/*
 * Terminal.h
 */

#import <AppKit/AppKit.h>
#import <ScriptingBridge/ScriptingBridge.h>


@class TerminalApplication, TerminalWindow, TerminalSettingsSet, TerminalTab;

enum TerminalSaveOptions {
	TerminalSaveOptionsYes = 'yes ' /* Save the file. */,
	TerminalSaveOptionsNo = 'no  ' /* Do not save the file. */,
	TerminalSaveOptionsAsk = 'ask ' /* Ask the user whether or not to save the file. */
};
typedef enum TerminalSaveOptions TerminalSaveOptions;

enum TerminalPrintingErrorHandling {
	TerminalPrintingErrorHandlingStandard = 'lwst' /* Standard PostScript error handling */,
	TerminalPrintingErrorHandlingDetailed = 'lwdt' /* print a detailed report of PostScript errors */
};
typedef enum TerminalPrintingErrorHandling TerminalPrintingErrorHandling;



/*
 * Standard Suite
 */

// The application˜s top-level scripting object.
@interface TerminalApplication : SBApplication

- (SBElementArray *) windows;

@property (copy, readonly) NSString *name;  // The name of the application.
@property (readonly) BOOL frontmost;  // Is this the frontmost (active) application?
@property (copy, readonly) NSString *version;  // The version of the application.

- (void) open:(NSArray *)x;  // Open a document.
- (void) print:(id)x withProperties:(NSDictionary *)withProperties printDialog:(BOOL)printDialog;  // Print a document.
- (void) quitSaving:(TerminalSaveOptions)saving;  // Quit the application.
- (TerminalTab *) doScript:(NSString *)x in:(id)in_;  // Runs a UNIX shell script or command.

@end

// A window.
@interface TerminalWindow : SBObject

- (SBElementArray *) tabs;

@property (copy, readonly) NSString *name;  // The full title of the window.
- (NSInteger) id;  // The unique identifier of the window.
@property NSInteger index;  // The index of the window, ordered front to back.
@property NSRect bounds;  // The bounding rectangle of the window.
@property (readonly) BOOL closeable;  // Whether the window has a close box.
@property (readonly) BOOL miniaturizable;  // Whether the window can be minimized.
@property BOOL miniaturized;  // Whether the window is currently minimized.
@property (readonly) BOOL resizable;  // Whether the window can be resized.
@property BOOL visible;  // Whether the window is currently visible.
@property (readonly) BOOL zoomable;  // Whether the window can be zoomed.
@property BOOL zoomed;  // Whether the window is currently zoomed.
@property BOOL frontmost;  // Whether the window is currently the frontmost Terminal window.
@property (copy) TerminalTab *selectedTab;
@property NSPoint position;  // The position of the window, relative to the upper left corner of the screen.
@property NSPoint origin;  // The position of the window, relative to the lower left corner of the screen.
@property NSPoint size;  // The width and height of the window
@property NSRect frame;  // The bounding rectangle, relative to the lower left corner of the screen.

- (void) closeSaving:(TerminalSaveOptions)saving savingIn:(NSURL *)savingIn;  // Close a document.
- (void) saveIn:(NSURL *)in_;  // Save a document.
- (void) printWithProperties:(NSDictionary *)withProperties printDialog:(BOOL)printDialog;  // Print a document.
- (void) delete;  // Delete an object.
- (void) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties;  // Copy object(s) and put the copies at a new location.
- (BOOL) exists;  // Verify if an object exists.
- (void) moveTo:(SBObject *)to;  // Move object(s) to a new location.

@end



/*
 * Terminal Suite
 */

@interface TerminalApplication (TerminalSuite)

- (SBElementArray *) settingsSets;

@property (copy) TerminalSettingsSet *defaultSettings;  // The settings set used for new windows.
@property (copy) TerminalSettingsSet *startupSettings;  // The settings set used for the window created on application startup.

@end

// A set of settings.
@interface TerminalSettingsSet : SBObject

- (NSInteger) id;  // The unique identifier of the settings set.
@property (copy) NSString *name;  // The name of the settings set.
@property NSInteger numberOfRows;  // The number of rows displayed in the tab.
@property NSInteger numberOfColumns;  // The number of columns displayed in the tab.
@property (copy) NSColor *cursorColor;  // The cursor color for the tab.
@property (copy) NSColor *backgroundColor;  // The background color for the tab.
@property (copy) NSColor *normalTextColor;  // The normal text color for the tab.
@property (copy) NSColor *boldTextColor;  // The bold text color for the tab.
@property (copy) NSString *fontName;  // The name of the font used to display the tab's contents.
@property NSInteger fontSize;  // The size of the font used to display the tab's contents.
@property BOOL fontAntialiasing;  // Whether the font used to display the tab's contents is antialiased.
@property (copy) NSArray *cleanCommands;  // The processes which will be ignored when checking whether a tab can be closed without showing a prompt.
@property BOOL titleDisplaysDeviceName;  // Whether the title contains the device name.
@property BOOL titleDisplaysShellPath;  // Whether the title contains the shell path.
@property BOOL titleDisplaysWindowSize;  // Whether the title contains the tab's size, in rows and columns.
@property BOOL titleDisplaysSettingsName;  // Whether the title contains the settings name.
@property BOOL titleDisplaysCustomTitle;  // Whether the title contains a custom title.
@property (copy) NSString *customTitle;  // The tab's custom title.

- (void) closeSaving:(TerminalSaveOptions)saving savingIn:(NSURL *)savingIn;  // Close a document.
- (void) saveIn:(NSURL *)in_;  // Save a document.
- (void) printWithProperties:(NSDictionary *)withProperties printDialog:(BOOL)printDialog;  // Print a document.
- (void) delete;  // Delete an object.
- (void) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties;  // Copy object(s) and put the copies at a new location.
- (BOOL) exists;  // Verify if an object exists.
- (void) moveTo:(SBObject *)to;  // Move object(s) to a new location.

@end

// A tab.
@interface TerminalTab : SBObject

@property NSInteger numberOfRows;  // The number of rows displayed in the tab.
@property NSInteger numberOfColumns;  // The number of columns displayed in the tab.
@property (copy, readonly) NSString *contents;  // The currently visible contents of the tab.
@property (copy, readonly) NSString *history;  // The contents of the entire scrolling buffer of the tab.
@property (readonly) BOOL busy;  // Whether the tab is busy running a process.
@property (copy, readonly) NSArray *processes;  // The processes currently running in the tab.
@property BOOL selected;  // Whether the tab is selected.
@property BOOL titleDisplaysCustomTitle;  // Whether the title contains a custom title.
@property (copy) NSString *customTitle;  // The tab's custom title.
@property (copy, readonly) NSString *tty;  // The tab's TTY device.
@property (copy) TerminalSettingsSet *currentSettings;  // The set of settings which control the tab's behavior and appearance.
@property (copy) NSColor *cursorColor;  // The cursor color for the tab.
@property (copy) NSColor *backgroundColor;  // The background color for the tab.
@property (copy) NSColor *normalTextColor;  // The normal text color for the tab.
@property (copy) NSColor *boldTextColor;  // The bold text color for the tab.
@property (copy) NSArray *cleanCommands;  // The processes which will be ignored when checking whether a tab can be closed without showing a prompt.
@property BOOL titleDisplaysDeviceName;  // Whether the title contains the device name.
@property BOOL titleDisplaysShellPath;  // Whether the title contains the shell path.
@property BOOL titleDisplaysWindowSize;  // Whether the title contains the tab's size, in rows and columns.
@property BOOL titleDisplaysFileName;  // Whether the title contains the file name.
@property (copy) NSString *fontName;  // The name of the font used to display the tab's contents.
@property NSInteger fontSize;  // The size of the font used to display the tab's contents.
@property BOOL fontAntialiasing;  // Whether the font used to display the tab's contents is antialiased.

- (void) closeSaving:(TerminalSaveOptions)saving savingIn:(NSURL *)savingIn;  // Close a document.
- (void) saveIn:(NSURL *)in_;  // Save a document.
- (void) printWithProperties:(NSDictionary *)withProperties printDialog:(BOOL)printDialog;  // Print a document.
- (void) delete;  // Delete an object.
- (void) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties;  // Copy object(s) and put the copies at a new location.
- (BOOL) exists;  // Verify if an object exists.
- (void) moveTo:(SBObject *)to;  // Move object(s) to a new location.

@end

You don’t. You use application scripting definitions in conjunction with the ScriptingBridge framework, or you use AppleScriptObjC.

ScriptingBridge.framework is for when you want to script an app fromObjective-C; if you’re using AppleScriptObjC, you can script apps directly via standard AppleScript, without the complications (and limitations) of ScriptingBridge.framework.

I get the irony of an AppleScriptObjC app using the ScriptingBridge to control another application :slight_smile:

However, in a sandboxed app, isn’t this framework the only way to achieve sending events to another app? I would write in pure Objective-C (or Swift) if I could. I’m just not there yet, though.

Maybe I’m not understanding the purpose of the ScriptingBridge framework… I thought it provided an alternative to using a com.apple.security.temporary-exception.apple-events entitlement in an app destined for the Mac App Store? Apple doesn’t even seem to require you to add comments regarding the com.apple.security.scripting-targets entitlement during submission.

Not that I’m aware of.

Weird. There’s a handful of apps in the Mac App Store that are iTunes/Spotify/Pandora controllers, and have been updated recently (post-sandboxing requirement going into effect). I can’t imagine how they would accomplish this sort of thing without the ScriptingBridge framework.

Here’s an doc from Apple describing the replacement of a temporary exception with a scripting target entitlement:
https://developer.apple.com/library/mac/qa/qa1802/_index.html

Why would you think that? AppleScript uses the Apple Event Manager to send Apple events to other processes. Scripting Bridge uses the Apple Event Manager to send Apple events to other processes. The only difference is that AppleScript speaks Apple events properly and Scripting Bridge doesn’t, which has no bearing on entitlements and sandboxing.

All entitlements do is tell the OS which Apple events it should allow out of your process’s sandbox and which to block. The sandbox doesn’t know or care which language or AE bridge your application uses because all the sandbox is concerned with is the Apple events coming out of the Apple Event Manager.

From your link:

Stick to AppleScript. Also, you only need to declare entitlements in your app if you’re going to sandbox it.

I’m obviously missing something here.

In a sandboxed app (with the proper entitlements set up), the following code executes with no issues:


set iTunesApp to current application's SBApplication's applicationWithBundleIdentifier_("com.apple.iTunes")
if ((iTunesApp's isRunning()) as integer) is 1 then set currentTrack to iTunesApp's |currentTrack|()
log currentTrack

Whereas, this code throws a Sandbox ‘deny’ message in the system log:


tell application "iTunes"
	if player state is playing then
		set currentTrack to current track
		log currentTrack
	end if
end tell

Entitlements:

Did you try:


tell application "com.apple.itunes"
   ...
end tell

This causes the compile/build to fail, unfortunately:


        tell application "com.apple.itunes"
            if player state is playing then
                set currentTrack to current track
                log currentTrack
            end if
        end tell

And while this does compile/build:


        tell application id "com.apple.itunes"
            if player state is playing then
                set currentTrack to current track
                log currentTrack
            end if
        end tell

It still throws a Sandbox deny message in the sys log.

You’re right there: I should’ve written a by-id reference, not by-name. Arbitrary brainfart.

Something weird is going on. Sandboxing should not break AppleScript as long as the entitlements are correct. Might it be something daft like case-sensitivity? Apple does seem to like uppercasing chars in its bundle IDs; does tell application id "com.apple.iTunes" work?

So, you all were correct, and I was obviously really confused. I think some of my confusion stemmed from the fact that several of the commands I was trying to use were not executing properly when writing in pure AppleScript, and I was getting a Sandbox-deny message in system log. I suppose I was under the impression that I needed to use the ScriptingBridge because many of the commands I was trying to use were working when using the Objective-C methods, but not when using pure AppleScript terminology.

To be honest, I’ve run into a number of odd issues, wherein, some things work in “pure” AppleScript terms, and other things I can only get working in the Objective-C terms. I found a post over on StackOverflow which contained the following excerpt:

From: http://stackoverflow.com/questions/12971306/how-to-add-a-track-to-an-itunes-playlist-using-python-and-scripting-bridge

Not sure if it has anything to do with my experience, but thanks to hhas & Shane for taking the time to explain and help despite my ignorance.