Search code examples
swiftapplescriptapplescript-objc

Force unwrap error using the Scripting Bridge with Chrome


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?


Solution

  • 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!