Load Applescript from Object-C for accessing external software

hi there,

im trying to load an applescript from within my Cocoa-Applescript application.
It basically works, i just can’t pass the errors dictionary:

From Apple developer:

[[NSAppleScript alloc] initWithContentsOfURL:url error:&errors];

My code:

set errors to current application's NSDictionary's dictionary()
        set scriptPath to makeNS_string(((current application's NSBundle's mainBundle())'s pathForResource:"HelpersFinder" ofType:"scpt")) -- Helper returns NSstring
        set scriptUrl to current application's NSURL's fileURLWithPath_(scriptPath)
        set appleScript to ((current application's NSAppleScript's alloc())'s initWithContentsOfURL:scriptUrl |error|:errors)

It throws an error:

[format]*** -[NSAppleScript initWithContentsOfURL:error:]: value passed to argument of type
^@ must be either missing value' or reference’; assuming `missing value’.[/format]

I can get it to work by setting error to missing value, but i want possible errors to be added to the errors dictionary. What would be the right syntax?

ok, i switched to Object-C for this task.

i got it mostly working, but still i have something i am struggling with:
The function loading and executing the AppleScript receives a list of Arguments like {“123”,“456”,…}. With the code below i got everything work fine.

if ([scriptArgumentArray count])
            {
                NSAppleEventDescriptor  *arguments = [[NSAppleEventDescriptor alloc] initListDescriptor];
                NSString                *object;
                
                for (object in scriptArgumentArray) {
                    [arguments insertDescriptor:[NSAppleEventDescriptor descriptorWithString:object]
                                        atIndex:([arguments numberOfItems] + 1)]; //This +1 seems wrong... but it's not
                }
                
                [containerEvent setParamDescriptor:arguments forKeyword:keyDirectObject];
            }
}

But now i need to pass more complex AS records ore NSDictionaries like {a:“123”,b:{c:1, d:{“456”}} instead of AS lists. I basically need a recordDescriptor (or something better?) instead of a List descriptor. But i am really struggling on how to correct the code

The AS function calls are simple though like “myfunction(recordhere)”.

To get an idea an what i need to SEND to the applescript, i modified the return of the applescript to see what it looks like in object C

End of the applescript reads:

return {a:{b:"123", c:"234"}, d:""}

Obj-C receives it like this

Result(<NSAppleEventDescriptor: { 'usrf':[ 'utxt'("a"), { 'usrf':[ 'utxt'("b"), 'utxt'("123"), 'utxt'("c"), 'utxt'("234") ] }, 'utxt'("d"), 'utxt'("") ] }>)

What i need is the other way arround. How to come from the AS record to the NSAppleEventDescriptor?

The correct syntax if you don’t care about the error is:

set appleScript to ((current application's NSAppleScript's alloc())'s initWithContentsOfURL:scriptUrl |error|:(missing value))

otherwise:

set {appleScript, theError} to ((current application's NSAppleScript's alloc())'s initWithContentsOfURL:scriptUrl |error|:reference)

But you don’t really want to use NSAppleScript, either in AS or Objective-C. Use load script instead.

I’ll try that! What’s NSAppleScript for then?

For non-Cocoa-AppleScript apps.

Thank you for the advise! Works perfect!

I want to advance the stuff a little bit:
I created a class shAppleScript which shall handle init and function calls and which a can connect in IB.

The idea is to pass handler name and paramters to shAppleScript object to make it call the appropriate handlers in the loaded script.
With basic applescript this is not possible (as far as i know). But is there a way (maybe using Object-C stuff) to achieve this?

That way i would have instances to connect to. Would ease a lot of stuff.

script shApplescript
    property parent : class "NSObject"
    
    -- Properties
    property appleScript: missing value
    
    -- Functions
    on initScript_(scriptName)
        set scriptPath to ((current application's NSBundle's mainBundle()'s pathForResource:scriptName ofType:"scpt") as text)
        set appleScript to (load script (scriptpath as posix file))
    end

    on call_handler_parameters(handlerName,parameters)
        tell appleScript to handlerName(parameters)
    end


So basically i just would call



-- IB Outlet
property myScript: missing value -- Instance of shAppleScript
 
myScript's initScript_("myScript")
myScript's call_handler_parameters("DoSomethingWith","Parameters")

You can treat AppleScript classes like other classes – make a property and connect it to an instance in IB, or use alloc()'s init(). The main difference is that handlers become both class and instance methods.

The other issue you have to keep in mind is that calling methods between classes deal in Cocoa objects, so you need to coerce received parameters and results back to their AppleScript classes (if that’s what you want).

EDIT: solved… see last line

I solved it that way:

I break it down to what matters - i have two classes AppDelegate and shLib and a script in the resources folder called HelpersFinder.scpt.

AppDelegate:

-- IB Outlets
property lib: missing value -- connected to shLib

    on applicationDidFinishLaunching_(aNotification)
       -- Register Scripts        
       lib's registerScripts()
       lib's HelpersFinder's showMessage_("Hallo2") -- DOESNT WORK! 
    end

shLib:

 
property test: "TEST"

-- Register Helper Scripts
    on registerScripts()
        log "lib: registerScripts()"
        set HelpersFinder to initScript_("HelpersFinder")
        HelpersFinder's showMessage:"Hallo" -- WORKS! -> "Hallo -> TEST"
    end
    
    -- Init Script
    on initScript_(scriptName)
        log "lib: initScript_("&scriptName&")"
        set scriptPath to ((current application's NSBundle's mainBundle())'s pathForResource:scriptName ofType:"scpt") as text
        set myScript to (load script (scriptPath as posix file))
        set scriptInstance to myScript's newInstance(me)
        return scriptInstance
    end

Content of HelpersFinder.scpt

on newInstance(_parent)
	script HelpersFinder
		
		property parent : _parent
		on showMessage:userMessage
			tell application "Finder"
				display dialog userMessage & "-> " & my parent's test
			end tell
			return {a:1, b:2}
		end showMessage:
	end script
end newInstance

While in shLib the function call


HelpersFinder's showMessage:"Hallo"

works. But back in AppDelegate i can’t call

lib's HelpersFinder's showMessage_("Hallo2")

The error reads

[format]*** -[BAGenericObject showMessage:]: Can’t make «class ocid» id «data optr000000006548616C6C6F3200»
into type list, record or text. (error -1700)
[/format]

Any ideas what i am doing wrong?

EDIT: it was coercing…