How to call C and Objective C code from ApplescriptObjC

I know this question must be awful embarrassing for me to ask but for all the examples I find I just can´t figure out the central idea because of the specific implementation in the examples.

So the short question with an simple example is this: If I have a function written in C or Objective C I would like to return the result to an ASOC project, how do I do that? I have figured out I need to make a .h and a .m/.c file to the project but what do I write to those files and how do I call the functions? And yes I use the wrong terminology and thats probably a huge contributor to me not getting this:)

Say I have this simple piece of code:

[code]#import <Foundation/Foundation.h>

int add(int a,int b){

int c=0;
c = a+b;
return c;

}

int main(int argc, const char * argv[]) {

//@autoreleasepool

{
    int first = 1;
    int second = 2;
    int result = add(first, second);
    NSLog(@"%i",result);
    
}

}[/code]
and instead of calling the add function in the code I would like to call the function from a ASOC project using the two variables “first” and “second” from the ASOC project and store it there in a variable, what would I put in the .h file, in the .m file and how would I call the function from ASOC?

Well, it’s not a short reply.

First, no need to make a separate C file (.c) in your project, you can embed the c code inside an Objective C method and except for a few exceptions it should work.

The simplest method is to create a blue Object cube in your MainMenu.xib file, set its class to your newly added objective C class (in the identity inspector on the right, third tab). Then in your ASOC file you create a property like this:

property myObjCClass : missing value

and connect it in the XIB file by dragging with a right-click from the ASOC blue cube (add one and set its class to your ASOC file, as before), and choose the myObjCClass value in the outlets you are offered.

Then in your ASOC code you can call an ObjC method like this:

set paramOne to 234
set paramTwo to "test"
set returnValue to myObjCClass's methodWithParameterOne_parameterTwo_(paramOne, paramTwo)
set returnValue to returnValue as integer -- or string, or real, or whatever your ObjC method returns and exists in the AS world

Always found it is best to coerce to an AS value in a second step instead of all at once on the first line.

The other way around is also possible (an ObjC App with an ASOC class, calling ASOC methods from the ObjC code), but it is a bit different then what I have described here.

This should get you started.

Browser: Safari 534.51.22
Operating System: Mac OS X (10.8)

As for embedding C inside an Objective C method (I believe ASOC cannot talk to C, only ObjC), here’s one way to do it:

- (int) addA:(int)a toB:(int)b {
        int c = a+b;
        return c;
}

or this:

- (NSString *) createUUID {
        CFUUIDRef uniqueIDCF = CFUUIDCreate(NULL);
        NSString* uniqueID = (NSString*)CFBridgingRelease(CFUUIDCreateString(NULL, uniqueIDCF));
        CFRelease(uniqueIDCF);
        return uniqueID; // we put the result inside an NSString, because a CFStringRef would not work I believe. The bridge between ASOC and ObjC only works with ObjC basic objects (NSString, NSNumber, int, float, etc.)
}

Browser: Safari 534.51.22
Operating System: Mac OS X (10.8)

Thanks for the help. It is much appreciated. :slight_smile:

Its 5 am here so I only hope your example code makes more sense to me later today :stuck_out_tongue:

I would prefer to have the non-Applescript code in separate files:

My project is at its core an Applescript that 1) take some data from several websites, 2) do some statistical analysis to the data and 3) store the values to an CSV file. Or rather the Applescript do 1) and 3) and is already over 500 lines long and the intermediate 2) is done by (so far) seven separate C/ObjC helper functions (that so far has´t been “linked up to” the applescript), each between 100 and 1000 lines long, some found and some written by myself. I would very much like to have all the statistical manipulation separate from the Applescript logic or I will lose all track of what does when how where when I need to edit the code.

I think you don’t have a choice here :slight_smile: it wouldn’t work otherwise…

If you follow the logic I explained before you should be fine. I usually create apps in objective c and add an AppleScript file if necessary. I use as much as possible C and objective c and switch to AppleScript when needed only, which means when I need to control other apps. But although they are very large it works really well. There’s a few caveats and limitations, but once you know them it’s fine.

Browser: Safari 9537.53
Operating System: Mac OS X (10.8)

After two days of reading your post, doing a lot of searches on variations of “call objective c functions from applescriptobjc”, reading chapter 16 of AppleScriptObjC Explored" several times and basically being as stuck as before I am sorry to say you have greatly overestimated my abilities :stuck_out_tongue: I am a statistician going into this with a basic knowledge of applescript trying to learn enough to create a program to do a very specific task in real time that I haven´t been able to do sufficiently fast in R, SPSS etc.

When I described the state of my project it was to illustrate what I DO have: A great working ASOC project and a bunch of different Objective C and C code that do some statistical manipulation and works great (as in “give the the desired output when fed the right parameters really REALLY fast”). And what I don´t have: Any clue on how to glue the two together.

What I find around is a lot on how to use some of very specific and advanced objective C (translating date formats, picking colors etc) and not a whole lot on how to do link “pure” mathematical manipulation done in objective C/C up with the main script part of of a ASOC project

Mostly to illustrate my inexperience here are just one of the things I don´t get from your help (not a question but an illustration of my knowledge):

[b]create a blue Object cube in your MainMenu.xib file, set its class to your newly added objective C class

How and where do I add what so I get an objective C class?

””””””

If anyone would use the time I have created the worlds smallest ASOC program that adds two integers

https://dl.dropboxusercontent.com/u/1624676/math.zip

this is the project.

https://dl.dropboxusercontent.com/u/1624676/math%20xcode.zip

If instead of doing the adding in AS I would like to use

int adding(int valuea,int valueb){
int resultat;
resultat = valuea+valueb;
return resultat}

how would I implement that? If I knew that I am pretty sure I would be able to string it all together…

First, you need to make a new Objective-C class. That’s New → File → Cocoa Class. You get an .h file and an .m file. Add a method to the .m file. After the line"@implementation YourClassName", insert the method – for example:

- (NSNumber *)addThis:(NSNumber *)aNum toThat:(NSNumber *)otherNum
{
int a = [aNum intValue];
int b = [otherNum intValue];
int c = a + b;
return [NSNumber numberWithInt:c];
}

You need to use objects (such as NSNumbers) rather than primitive types (int, etc).

Now add the blue cube as in chapter 16, and connect it to a property.

Shane is correct, I was mistaken. You need to use NSNumber objects to talk to ObjC from ASOC. Was late at night when I replied… :slight_smile:

ASOC, although simple in comparison to ObjC, is not so easy for beginners, even people who know AppleScript. You may need some help I believe, more than what we can offer here. You might want to post a request for help by an experienced person, I think there is a forum for that already on this site. Just a suggestion. :wink:

Browser: Safari 600.2.5
Operating System: Mac OS X (10.8)

SUCCESS!

I think this is just the right guidance I needed to understand how the plumbings work. I got my simple example to work and that should work for my large project as well.

Thank you.

So, after spending much of friday swearing, as code that once worked didn’t (â„¢!)

So, here we go.

As others have minted, add a .m and .h file to your project. I’ve called these cocoa methods.m and .h

in your cocoa methods.h:

#import <Foundation/Foundation.h>
@interface miscMethods : NSObject

@end

and in your cocoa methods.m


#import "cocoamethods.h"

@implementation miscMethods

+(void)logstringdonwait:(NSString *)passedString

{
    usleep(10000000);
    NSLog(@"This is the passed string '%@'", passedString);
}

@end

Now, my applescript:

property NSThread : class "NSThread"
property miscMethods : class "miscMethods"

script AppDelegate
	property parent : class "NSObject"
   
	property theWindow : missing value
	
	on applicationWillFinishLaunching_(aNotification)
        set myString to current application's NSString's stringWithString:"bob"
        log "calling cocoa"
        current application's miscMethods's logstringdonwait_(myString)
        -- set myThread to NSThread's detachNewThreadSelector:"logstringdonwait:" toTarget:(current application's miscMethods) withObject:myString
        delay 5
        log "Still waiting for cocoa call"
 
    end applicationWillFinishLaunching_
	
	on applicationShouldTerminate_(sender)
		return current application's NSTerminateNow
	end applicationShouldTerminate_
	
end script

You’ll notice two lines:

 current application's miscMethods's logstringdonwait_(myString)
-- set myThread to NSThread's detachNewThreadSelector:"logstringdonwait:" toTarget:(current application's miscMethods) withObject:myString

You can call your method in two ways.

  1. If you want to wait for the method to finish and return, use current application’s miscMethods’s logstringdonwait_(myString)
  2. If you don’t care about the response, and want to continue what you were doing, use set myThread to NSThread’s detachNewThreadSelector:“logstringdonwait:” toTarget:(current application’s miscMethods) withObject:myString

play around with commenting out each line and you see the difference when things are logged

Let me pick a few nits…

It really is a good idea to follow Cocoa convention on method naming, and it also reduces the chances of typing errors. Try something like logStringDontWait:

The second of these is redundant, and the first is not really a good habit. It’s harmless enough here, but if you try to do it an ASObjC-based script outside Xcode, you’re encouraging errors when you save. Better to get into safe habits, IMO.

Generally, dispatching AS code to another thread is a bad idea. Unless you create a separate instance of AppleScript, all threads go through the one application instance anyway. That can only happen one thread at a time, so when you fire off code on a new thread, any existing running code is put on hold wherever it’s at, and then restarted after the new thread has completed its code. You gain nothing, but more importantly you risk things happening in a different order than you expect, and that can cause all sorts of problems.

If you want an Objective-C method to be run asynchronously, do it in the Objective-C code using NSOperationQueue or Grand Central Dispatch, or even performSelector:onThread:withObject:waitUntilDone:.

Noted: :slight_smile: