0

I'm trying out Apple's Scripting Bridge to interact with Google Chrome. I've started with the code in https://stackoverflow.com/a/24147285

I have created .h file by using sdef and sdp commands.

I have included the file and also created the bridging header and imported chrome's header file in bridging header. But the problem is with using the header in Swift. The code is old and Swift has changed a lot.

My Swift file:

import Cocoa
import ScriptingBridge


var chromeObject = SBApplication.init(bundleIdentifier: "com.google.Chrome")! as AnyObject

print(chromeObject.closeable)

I'm getting an error saying

fatal error: unexpectedly found nil while unwrapping an Optional value

What am I doing wrong?

8
  • Possible duplicate of What does "fatal error: unexpectedly found nil while unwrapping an Optional value" mean? Commented Mar 5, 2017 at 21:09
  • Is Chrome installed? Commented Mar 5, 2017 at 21:50
  • @JoshCaswell Yes Commented Mar 5, 2017 at 21:51
  • 1
    @JoshCaswell The only command which works is chromeObject.activate() Commented Mar 5, 2017 at 21:55
  • @psh The thing with using ScriptingBridge is that the documentation is not very expressive. So that's why you are not getting. Are you sure you wanna continue using ScriptingBridge instead of directly using AppleScript? It will be easy once you get how it works but it gets tiresome if you are working with iTunes.sdef which has so many interfaces. Commented Mar 6, 2017 at 0:30

2 Answers 2

2

SBApplication returns Application object. You can see different interfaces in .h file.

@interface ChromeApplication : SBApplication
@interface ChromeWindow : SBObject <ChromeGenericMethods>
@interface ChromeApplication (ChromiumSuite)
@interface ChromeTab : SBObject <ChromeGenericMethods>
@interface ChromeBookmarkFolder : SBObject <ChromeGenericMethods>
@interface ChromeBookmarkItem : SBObject <ChromeGenericMethods>

So, SBApplication returns you ChromeApplication. You can call any properties defined inside ChromeApplication. Try it yourself. You won't get any error.

The problem is you are calling closeable which is part of ChromeWindow.

From Apple Documentation:

AppleScript and Objects

AppleScript is an object-oriented language. When you write, compile, and execute scripts, everything you work with is an object. An object is an instantiation of a class definition, which can include properties and actions. AppleScript defines classes for the objects you most commonly work with, starting with the top-level script object, which is the overall script you are working in.

..............

.....................

and the main thing,

What Is in a Script Object

When you enter AppleScript statements in script window in Script Editor, you are working in a top-level script object. All script object definitions follow the same syntax, except that a top-level script object does not have statements marking its beginning and end.

A script object can contain the following:

Property definitions (optional): A property is a labeled container in which to store a value.

An explicit run handler (optional): A run handler contains statements AppleScript executes when the script is run. (For more information, see run Handlers.)

An implicit run handler (optional): An implicit run handler consists of any statements outside of any contained handlers or script objects.

Additional handlers (optional): A handler is the equivalent of a subroutine. (For details, see About Handlers.)

Additional script objects (optional): A script object can contain nested script objects, each of which is defined just like a top-level script object, except that a nested script object is bracketed with statements that mark its beginning and end. (For details, see Script Objects.)

So, in simple terms, Application is an object which contains Window which is an object which contains Tab object.....

You need to retrieve Window object/element from Application to use closeable.

You should have SBElementArray in every interface. You need to get that.

Example,

// The application's top-level scripting object.
@interface ChromeApplication : SBApplication

- (SBElementArray<ChromeWindow *> *) 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<NSURL *> *)x;  // Open a document.
- (void) quit;  // Quit the application.
- (BOOL) exists:(id)x;  // Verify if an object exists.

@end

You should retrieve - (SBElementArray<ChromeWindow *> *) windows; to use closable. Again in windows you have tabs array, etc.

For example, In AppleScript to get URL and title of every tabs:

tell application "Google Chrome"

    set a to ""
    repeat with w in windows
        repeat with t in tab in w  // Getting tab object from window
            set a to a & linefeed & title of t & " -URL: " & URL of t
        end repeat
    end repeat

end tell

The equivalent in Swift would be:

import Cocoa
import ScriptingBridge


var chromeObject: AnyObject = SBApplication.init(bundleIdentifier: "com.google.Chrome")!

var f = chromeObject.windows() // get the windows from application
for i in f!
{
    var t = (i as AnyObject).tabs() // get the tabs from windows
    for j in t!
    {
        print(((j as AnyObject).title as String) + " -URL: " + ((j as AnyObject).url as String))
    }
}

Hope it helps!

Sign up to request clarification or add additional context in comments.

1 Comment

The Apple documentation you cite describes how the AppleScript language works. AS defines a number of standard built-in datatypes (strings, lists, etc) and supports a sort of prototype-based OOP via script objects. Whereas its Apple event bridge works totally differently: application automation is not OOP, it's RPC plus simple first-class relational queries. (ASLG is worse than useless at explaining this; Dr Cook's AppleScript paper is much better. SB makes quite a cock of it too, hence the advice to stick with AS if you can.)
-2

From the documentation for SBApplication.init?(bundleIdentifier:):

Return Value

An initialized shared instance of an SBApplication subclass that represents a target application with the bundle identifier of ident. Returns nil if no such application can be found or if the application does not have a scripting interface.

You should check that the init method returns an instance before force unwrapping, like:

if let chromeObject = SBApplication.init(bundleIdentifier: "com.google.Chrome") as? AnyObject {
    print(chromeObject.closeable)
}

3 Comments

Actually the problem is at chromeObject.closeable. It always returns nil. But when I try in Obj-c, it's working fine with some warnings.
When you print you get that error? If you have Objective C code that works, why don't you post that? And maybe we can help you translate it to Swift?
Thanks for trying to help. @SkrewEverything solved my problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.