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?

Community
  • 1
  • 1
ssh
  • 491
  • 1
  • 8
  • 19
  • Possible duplicate of [What does "fatal error: unexpectedly found nil while unwrapping an Optional value" mean?](http://stackoverflow.com/questions/32170456/what-does-fatal-error-unexpectedly-found-nil-while-unwrapping-an-optional-valu) – JAL Mar 05 '17 at 21:09
  • Is Chrome installed? – jscs Mar 05 '17 at 21:50
  • @JoshCaswell Yes – ssh Mar 05 '17 at 21:51
  • 1
    @JoshCaswell The only command which works is `chromeObject.activate()` – ssh Mar 05 '17 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. – SkrewEverything Mar 06 '17 at 00:30
  • @SkrewEverything I don't know why but I'm not at all comfortable working with AppleScript. I just want to do it in Swift. Please help me! – ssh Mar 06 '17 at 03:08
  • It's highly recommended to use `AppleScriptObjC`. It makes things a lot easier. – vadian Mar 06 '17 at 14:03
  • @vadian I don't know obj-c and I didn't find any tutorial. That's why I'm using `ScriptingBridge`. The explanation given by @SkrewEverything solved my problem. – ssh Mar 06 '17 at 17:41

2 Answers2

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!

SkrewEverything
  • 2,393
  • 1
  • 19
  • 50
  • 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](http://www.cs.utexas.edu/~wcook/Drafts/2006/ashopl.pdf) is much better. SB makes quite a cock of it too, hence the advice to stick with AS if you can.) – foo Mar 06 '17 at 23:07
-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)
}
Dave Weston
  • 6,527
  • 1
  • 29
  • 44
  • 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. – ssh Mar 05 '17 at 23:15
  • 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? – Dave Weston Mar 05 '17 at 23:20
  • Thanks for trying to help. @SkrewEverything solved my problem. – ssh Mar 06 '17 at 13:36