Tuesday, September 19, 2017

#1 2008-07-22 04:00:00 pm

Craig Williams
Administrator
From:: Ft. Smith, AR
Registered: 2006-12-07
Posts: 888

Accessing Objective-C Classes Using Call Method

Introduction
Although many areas of the Cocoa framework are accessible through AppleScript in AppleScript Studio, many areas are not. You may also find using Objective-C is often easier than an equivalent AppleScript. The way we access Objective-C from AppleScript is with a call method.

The Reason For This Tutorial
As I began to use call methods I found very little information on how it is implemented. Apple has a short example here but does not go into much detail. There have been some questions and answers on the mail lists but nothing of great detail.

I am not an experienced Objective-C developer so the information below is from personal trial-and-error and examining the example application Multi-Language that Apple provides.

With that said, let's get started! Hope you enjoy!

Download the source code here.

Accessing Your Classes
In this tutorial we will cover how to access the classes and methods you write. Most of the example code I have seen covers (+)class methods but not (-)instance methods. This is not very helpful because you cannot access instance variables or instance methods from a (+)class method.


How it works
1. AppleScript sends a call method to an Objective-C class we'll call the "controller."
2. The "controller" instantiates an object of another Objective-C class that you have written and calls one of its' methods.
3. The new object performs the requested method actions and passes return values, if any, back to the "controller" which passes them on to the calling AppleScript.

This means that anything you want the Objective-C class to know about, you have to explicitly send it that information. For example, if you want your Objective-C method to set a text field on your layout to a certain value, you need to send the text field along as a parameter: {text field "textField" of window "main"}

What a call method looks like

Call Method No Parameters
This will call the method "logSomething" with no parameters. The method logSomething does not have a trailing colon because it has no parameters. It is also not required to explicitly state the class in your method call but it is probably good form to do so.

AppleScript Code:
   

call method "logSomething" of class "Controller"


Objective-C Code:
   

- (void)logSomething
   {
       NSLog(@"I have just been logged!!");
   }


Call Method With Parameters
This will call the method "appendString:withString:" with two parameters.
This method has parameters so each ends with a colon.
Parameters are supplied as a list so they are enclosed in {}.

AppleScript Code:
     

call method "appendString:withString:" of class "Controller" with parameters {"Feed me ", "Seymour!"}


Objective C Code:
   

- (NSString *)appendString:(NSString *)aString
                   withString:(NSString *)anotherString
   {
        return [NSString stringWithFormat:@"%@ %@",
                          aString, anotherString];
   }


New Project
Create a new AppleScript Studio project and call it "Call Methods" or whatever you prefer.

Create a "Controller" Class
Create a new Objective C class and name it Controller.m. Check the box that also creates the .h file. The Controller class will act as our messenger. We send messages to the Controller then the Controller contacts the appropriate class to get the job done. Once the job is completed the Controller returns the results.

The "Controller.h" File
The multi-language example provided by Apple states the best way to access instance methods in your classes is to add a category to the NSApplication class.
       

#import <Cocoa/Cocoa.h>

    @interface NSApplication (ASKAMultiLanguage)

    @end


The "Controller.h" File
Define a method in "Controller.h"
This is the method we will call from AppleScript.

   

#import <Cocoa/Cocoa.h>

    @interface NSApplication (ASKAMultiLanguage)
    - (NSString *)appendString:(NSString *)aString
                    withString:(NSString *)appendedString;
    @end


The "Controller.m" Class
Add the method to "Controller.m"

   

#import "Controller.h"
    #import "Model.h"     // << Add this import statement

    @implementation NSApplication  (ASKAMultiLanguage)

    - (NSString *)appendString:(NSString *)aString
                    withString:(NSString *)appendedString;
    {
         Model *obj = [[[Model alloc] init] autorelease];
          return [obj MODELappendString:aString 
                            withString:appendedString];
    }
    @end


Create a "Model" Class
Now that we have our "Controller" class setup we need to create the class that is going to do the work.
In our example we will call this class "Model."
Go ahead and make that class now.


The "Model.h" Class
In "Model.h" we define the method that will do the actual work. You can use the exact same method name we used in the Controller class here in the Model. I chose to prefix the name with MODEL to distinguish them but it is not necessary. Normally I use the same name in both places for simplicity and to reduce errors.

   

#import <Cocoa/Cocoa.h>

    @interface Model :  NSObject
    {

    }
    - (NSString *)MODELappendString:(NSString *)aString
                         withString:(NSString *)appendedString;
    @end


The "Model.m" Class
Let’s create our method.

   

#import "Model.h"

    @implementation Model

    - (NSString *)MODELappendString:(NSString *)aString
                         withString:(NSString *)appendedString
    {
         return [NSString stringWithFormat:@"%@ %@",
                          aString, appendedString];
    }
    @end


AppleScript
In our AppleScript file add the following in an awakeFromNib handler.

Applescript:

display dialog (call method "appendString:withString:" ¬
with parameters {"Feed me", "Seymour!"})

Now Build and Run!

http://files.macscripter.net/unscripted/_cwilliams/callmethod/TNFeedMe.png

Final result!


Final Thoughts
This is a very simple example and by itself is completely useless. What I want to convey is the process in which it is performed. Once you understand that all communication is directed to the Controller class and all information you want the Controller class to know about must be included in the parameters. Then it becomes easier to implement more complicated tasks.

Using this technique you can add as many Objective-C classes as you like and use the Controller class to talk to all of them.

Happy coding!

Craig


Filed under: call-method, Objective-C

Offline

 

#2 2008-07-23 09:23:20 am

regulus6633
Member
From:: Taulov, Denmark
Registered: 2006-11-02
Posts: 1695
Website

Re: Accessing Objective-C Classes Using Call Method

Craig, thanks for the article. That's going to help with some stuff. I never used a controller class to create instances of my classes, but I can see where that would be advantageous. So thanks for showing how to do that.

2 questions:
1. In your controller class you have the following:

@interface NSApplication (ASKAMultiLanguage)

Normally when you create a class you see something like what is in your model class '@interface Model :  NSObject', which means the class name is Model and the Model class is a subclass of NSObject. So this way to define a class is new to me. Could you explain what it means? Is it just a way to set the controller class as the controller for the application object? Also what is '(ASKAMultiLanguage)'? Is that a necessary parameter and are there other parameters that we may use in its place?

2. You use the following import statements in the controller class:

#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>

I'm never clear on what I need to import so I need to clarify in my mind why you use those 3. My basic understanding was that if I import the cocoa.h header file that I didn't need to worry about importing the foundation and appkit headers because the cocoa.h file does the importing for you. If you look in the cocoa.h file it contains the other two import statements. So my question is did you include the foundation and appkit header files for some reason that I'm not aware of?

Offline

 

#3 2008-07-23 10:16:47 am

Craig Williams
Administrator
From:: Ft. Smith, AR
Registered: 2006-12-07
Posts: 888

Re: Accessing Objective-C Classes Using Call Method

Hi Hank,

Question 1:
In the "Multi-Language" example it states the following:

"This file adds a category on the NSApplication class which 'call method' can then use to easily call the Objective-C methods defined here."


The (ASKAMultiLanguage) category allows you to use other languages in your AppleScript Studio application like Objective-C, C, C++ and Java. I have been unable to find documentation that gives more detail.

Question 2:
Thanks for pointing out the unnecessary inclusions. I have corrected the post.

Offline

 

#4 2008-09-20 10:59:19 am

khiggins92
Member
Registered: 2008-08-02
Posts: 1

Re: Accessing Objective-C Classes Using Call Method

Craig:
Thanks for your article. I have been looking for a solution all over and
this was the only one I found to supplement AppleScript with Cocoa
methods.

I have one question-- I am using Xcode 3.1 and when I downloaded and ran
the Xcode files that you posted they ran correctly and produced the "Feed me Seymour" message.

However when I typed in the various files: Controller.m, Controller.h Model.m, Model.h etc
and ran them, I noticed that the NIB file you had in your download came up as an XIB file in
my Project.

My typed version, compiled correctly however the result was an empty window titled "Window" appeared
and the "Feed me Seymour" did not display.

Transitioning from the MS .NET platform to this one is a big jump. I was wondering if
you could shed some light on what is different here.

Thanks
Ken Higgins

Offline

 

#5 2008-09-20 11:20:47 am

Martin Michel
Administrator
From:: Berlin, Germany
Registered: 2008-03-03
Posts: 701
Website

Re: Accessing Objective-C Classes Using Call Method

khiggins92 wrote:

However when I typed in the various files: Controller.m, Controller.h Model.m, Model.h etc
and ran them, I noticed that the NIB file you had in your download came up as an XIB file in
my Project.


Apple lately changed the NIB files into XIB files. The advantage of XIB files is that they are simple XML documents, which can easily be managed by tools like subversion. NIB files were packages (folders) containing multiple files, which were not easily managed by revision control systems.

See also: Interface Builder: NIB & XIB files


Plonum - Convert images to iWork Numbers documents

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)