AppleScriptObjC in Xcode Part 1

Yeah, sure it takes time. You know how it is though, pretty much everyone I know who wanted to learn programming also wanted to know it all in a day and be able to code actual applications in no time.
I can’t wait for more of your tutorials, they make me a bit more enthusiastic about this whole change in Applescript programming!

(However, Part 2 is waaay too vague for me, after reading 3 times I’m still not getting all of it – in comparison to part 1 you seem to assume that all your readers have gotten the hang of Objective-C classes etc which is def not the case!)

I struggle more with writing than I do with coding. :slight_smile:

Is there any part that is particularly vague or is the whole thing difficult to understand?
I don’t mind re-writing or going into more detail but I would like to work on the areas that
are the most difficult to grasp.

I know exactly what you mean, when something is “natural” to you, it’s difficult to put yourself back in a position where everything is new, where you’re just discovering.
It’s nothing personal and I hope I didn’t offend you – sorry for the way I wrote that. Don’t get me wrong, you’re doing an amazing job here, and needless to say you are a pioneer at this right now.

Well for one thing, my C experience dates back to a few years and I’ve forgotten it all. “Classes”, “Parent classes”, etc… English not being my native language, are confusing terms as I cannot remember exactly what they are, and what we need them for.
I guess it’s nothing in particular, just the whole thing is a little blurry to me.

I guess because the comments seem less clear than explaining exactly what you’re about to do, what you need to refer to and THEN showing the code for it, like you did in tutorial 1. Am I making any sense ?
Oh, a few confusing bits to me so far : what’s the difference between Cocoa and Objective-C ? Ojb-C is a programming language, so what about Cocoa ? is it a framework or does it have something to do more with the GUI side of programming ? I see both these terms used in documentation, seems like one could be used instead of the other sometimes…

I am not offended in the least. I appreciate the feedback and want to make these articles as understandable and user friendly as possible.
If they are not, I want to correct that.

The best place to start is “Introduction to The Objective-C 2.0 Programming Language.

Complete sense. Once I get through with the tutorial I am working on I will go back and make the necessary adjustments.

I found a great explanation here.
.
.
.

okay, so I follow along with the very first part, draw my connections as instructed to do in Interface Builder, and…nothing. type in the text field, click the button, nothing happens. When I look at the downloaded project, I noticed that in the downloaded project, the textView field has neither a referencing outlet or a nextKeyView, whereas in my project, it does have a referencing outlet. The text field in your project has both a nextKeyView and a referencing outlet, whereas my version has neither.

I copied the code out of the example, so I’m pretty sure I don’t have a typo, but I think there’s a step or two I’m missing. Ideas?

Zip it up and email to craigw@macscripter.net.

Thanks for the help craig, i’d have never guessed about the differences in the drag targeting.

Oh, one thing. in the initial part of setting up awake from nib you have the border and default text code as:

myTextView’s setTextContainerInset_({10.0, 10.0})
myTextField’s setStringValue_(“Default text”)

if you don’t initially notice that the ‘my’ shouldn’t be there, you can have a couple minutes of da head scratchin’

Hi Craig,

I tried to modify our small example.

I tried two things:

I inserted a beep and I tried to append the next textField value to the end of the textView like this:
Value 1
Value 2
.
.
Value n

Unfortunately I´m doing things wrong and get neither one to work.

I did this:

1 on setTextViewFromTextField_(sender)
2 beep # Why is this, it´s AppleScript, no beep comes out of the Speaker
3 set textFieldValue to textField’s stringValue()
4 set tempViewValue to textView’s stringValue() # I also tried getStringValue() and getValue()
5 textView’s setString_(tempValue & textFieldValue)
6 end setTextViewFromTextField_

The console says: -[NSTextView stringValue]: unrecognized selector sent to instance 0x200277360

Greetings, Ronald

The “beep” should be working. Try “beep 2” and see if you get different results.

This is your clue to where the issue lies. If NSTextView does not recognize the selector (method)
“stringValue” then we know that NSTextView does not have a method called “stringValue.” The
next step is to look for a method in NSTextView that we can use.

Unfortunately, there is no method in NSTextView. We have to look to its parent for a method.
NSText has a method called “setString:” and “string.” Those are the methods you want to use
for a NSTextView.

Also, if you look at the code above, we use “setString_()” to set the text of the text view.
The way key-value coding works is to remove the “set” from the beginning and lowercase
the first letter so “setString_()” becomes “string().” You never use “get” in Objective-C.

hth,

Craig

Sorry for bothering you again Craig.

I tried both:

set textViewValue to textView’s string_()
#which returns: -[NSTextView string:]: unrecognized selector sent to instance 0x200252b40

If I don’t use the underscore XCode changes string() to string {} automatically.

and

set textViewValue to textView’s stringValue_()
#which returns: -[NSTextView stringValue:]: unrecognized selector sent to instance 0x200224600

This is what makes ObjC newcomers a real hardtime. It´s almost impossible to find out where things are described in the apple documentation. It always seems to be a matter of incident to find the right place.

Greetings, Ronald

Sorry, I had forgotten that when you use a method that corresponds to an
AppleScript key word you have to surround it with “|” symbols.

The following code will take text from a text field and combine it
with the current text in a text view using a new line separator.


set theText to textField's stringValue()
set curText to textView's |string|()

tell class "NSString" of current application
	set newText to its stringWithFormat_("%@%@%@", theText, return, curText)
end tell

textView's setString_(newText)

BTW, when you first start using Objective-C, reading the documentation is
difficult but it does get easier. Start with the class, say NSTextView, and
look at all its methods. If you do not see one that sounds like what you
want then look to its parent class. Repeat until you are at NSObject or
the top level class.

AppleScriptObjC Part 5 covers reading the documentation. Have you gone
through this tutorial? If so, is there anything I can do to make it
more useful?

Regards,

Craig

Great, now it works.
What I don´t understand, why don´t we need the vertical bars in the first line ???

1 set theText to textField’s stringValue()
2 set curText to textView’s |string|()
3
4 tell class “NSString” of current application
5 set newText to its stringWithFormat_(“%@%@%@”, theText, return, curText)
6 end tell
7
8 textView’s setString_(newText)

This thread is really great Craig. Nowhere else I can put questions and and even get answers.

Where are you from? Next time I come to your place I take you to a restaurant :slight_smile:

Greetings, Ronald

Where did you get this part from?

1 tell class “NSString” of current application
2 set newText to its stringWithFormat_(“%@%@%@”, theText, return, curText)
3 end tell

OK. I know what line 3 means.
But I never thought of ‘tell class “NSString” of current application’ Where can I find this?
I actually don´t know where to look it up. I made many different searches but I always got too many occurences.

Greetings, Ronald

Hi All,

I have been reading Arron Hillgass’s book on Cocoa Programming which is excellent and also “Programming in Objective C” by Steven Kochan. Both highly recommended

Just getting familiar with the language helps tremendously. It all depends on how far you want to go. I think, with some more good examples along the way, we can recreate anything we did in ASS in a better and simpler way.

Now, if we want to extend much further into complexity, it will be necessary to actually write some class files to augment ASOC apps. I already had quite a few class files in my ASS apps, thanks to gifts from OBJ_C folks with some minor tweaking on my part. I already need more in ASS than applescript could provide.

So the more you want to learn the more you can do, and without becoming an OBJ-C programmer (not in this lifetime for me!)

Rob

Craig,

Thanks a million for the great tutorial, I thought it was awesome…

I am not an Objective C person, so I hope you’ll get a chance to make some more AppleScriptObjC. I had started several AppleScript Studio projects and they were going fairly well, and now it’s gone.

Hopefully we can get by with minimal Objective C ?

Here’s a couple of easy changes to the first tutorial script, hope it is useful for beginners.

Thanks Again,

Bill Hernandez
Plano, Texas


– tutorial_part_oneAppDelegate.applescript
– tutorial_part_one

– Created by Bill Hernandez on 10/13/09.
– Copyright 2009 mac-specialist.com. All rights reserved.

– ±--------±--------±--------±--------±--------±--------+
– NOTES :
– ±--------±--------±--------±--------±--------±--------+
– set time_stamp to my get_time_stamp(2)

– ( n ) —> format choices { 1, 2, 3 }
– ( 1 ) —> 2009.10.13
– ( 2 ) —> [ 2009.10.13 ] ( 07.14.43.PM )
– ( 3 ) —> 2009.10.13_07.15.15.PM
– ±--------±--------±--------±--------±--------±--------+

script tutorial_part_oneAppDelegate
– ±--------±--------±--------±--------±--------±--------+
– Inheritance
– ±--------±--------±--------±--------±--------±--------+
property parent : class “NSObject”
– Option double click on —> NSTextView

-- +---------+---------+---------+---------+---------+---------+
-- IBOutlets	
-- Interface Builder considers an outlet as any
-- property with "missing value" as its value
-- +---------+---------+---------+---------+---------+---------+
property aWindow : missing value
property textView : missing value
property textField : missing value

property show_message : false

-- +---------+---------+---------+---------+---------+---------+
-- IBActions (button clicks)
-- Interface Builder considers an action as any
-- single parameter method ending with an underscore 
-- +---------+---------+---------+---------+---------+---------+
on setTextViewFromTextField_(sender)
	-- USER MODIFIABLE
	set which_update_method to 1 -- choices { 1, 2 }
	
	-- date_object and current_date (NOT USED FOR NOW)
	set date_object to current date
	set current_date to (date_object as string)
	
	-- ( n ) ---> time_stamp format choices { 1, 2, 3 }
	-- ( 1 ) ---> [2009.10.13](07.15.41.PM)
	-- ( 2 ) ---> [ 2009.10.13 ] ( 07.14.43.PM )
	-- ( 3 ) ---> 2009.10.13_07.15.15.PM
	set time_stamp to my get_time_stamp(2) -- format choices { 1, 2, 3 }
	
	if (which_update_method is 1) then
		tell textField
			set textFieldValue to get stringValue()
		end tell
		
		tell textView
			set textViewValue to get |string|()
			set newText to (textViewValue as string) & return & time_stamp & "  |  " & (textFieldValue as string)
			setString_(newText)
		end tell
		
		-- THIS ALSO WORKSOUTSIDE OF THE TELL BLOCK
		-- textView's setString_((textFieldValue as string) & return & (textViewValue as string))
		
		(*
			display dialog (textViewValue as string)
			display dialog (textFieldValue as string)		
		*)
	else
		set previousText to textView's |string|()
		set currentText to textField's stringValue()
		
		tell class "NSString" of current application
			set newText to its stringWithFormat_("%@%@%@%@", previousText, return, time_stamp & "  |  ", currentText)
		end tell
		
		textView's setString_(newText)
	end if
	if (show_message) then
		display dialog (newText as string)
	end if
	
end setTextViewFromTextField_
-- +---------+---------+---------+---------+---------+---------+
on get_time_stamp(which)
	if (which is 1) then
		set returnValue to do shell script "date '+[%Y.%m.%d](%I.%M.%S.%p)'"
		---> [2009.10.13](07.15.41.PM)
	else if (which is 2) then
		set returnValue to do shell script "date '+[ %Y.%m.%d ] ( %I.%M.%S.%p )'"
		---> [ 2009.10.13 ] ( 07.14.43.PM )
	else if (which is 3) then
		set returnValue to do shell script "date '+%Y.%m.%d_%I.%M.%S.%p'"
		---> 2009.10.13_07.15.15.PM
	else
		-- which is 1 or anything else
		set returnValue to do shell script "date '+[%Y.%m.%d](%I.%M.%S.%p)'"
		---> [2009.10.13](07.15.41.PM)
	end if
	return returnValue
end get_time_stamp
-- +---------+---------+---------+---------+---------+---------+
-- Application
-- +---------+---------+---------+---------+---------+---------+

on awakeFromNib()
	set time_stamp to my get_time_stamp(2) -- format choices { 1, 2, 3 }
	
	tell textView
		setTextContainerInset_({5.0, 5.0})
		setString_(time_stamp & "  |  " & "Bill Hernandez")
	end tell
	tell textField
		setStringValue_("Default text")
	end tell
	
	-- add this as the last item in the awakeFromNib handler
	aWindow's makeFirstResponder_(textField)
	
end awakeFromNib
-- +---------+---------+---------+---------+---------+---------+	
on applicationWillFinishLaunching_(aNotification)
	-- Insert code here to initialize your application before any files are opened 
end applicationWillFinishLaunching_
-- +---------+---------+---------+---------+---------+---------+
on applicationShouldTerminate_(sender)
	-- Insert code here to do any housekeeping before your application quits 
	return current application's NSTerminateNow
end applicationShouldTerminate_
-- +---------+---------+---------+---------+---------+---------+	

end script

Model: iMac 2.6 GHz Intel
AppleScript: Xcode 3.2.1
Browser: Safari 531.9
Operating System: Mac OS X (10.6)

Craig,

How and where would you over-ride NSTextView in order to have a function stringValue(), or textValue() for the
NSTextView class it so that instead of calling :

set previousText to textView’s |string|()

You could call :

set previousText to textView’s stringValue()
or :
set previousText to textView’s textValue()

Such that :

set previousText to textView’s |string|()
set currentText to textField’s stringValue()

Would become more consistent :

set previousText to textView’s stringValue()
set currentText to textField’s stringValue()

OR :

set previousText to textView’s textValue()
set currentText to textField’s stringValue()

I could probably do something like :

on get_textValue(textViewRef)
return textViewRef’s |string|()
end get_textValue

Then call :

set previousText to get_textValue(textView)

But that would be somewhat of a kludge. Would it be easy to create a SubClass and substitute it for NSTextView. If so, how would you let the Xcode project know to use NSTextViewPlus instead of NSTextView ?

Thanks again,

Bill Hernandez
Plano, Texas

Model: iMac 2.6 GHz Intel
AppleScript: Xcode 3.2.1
Browser: Safari 531.9
Operating System: Mac OS X (10.6)

Craig,

Is it possible we might look forward to seeing you doing AppleScripgObjC at Lynda.com ?

I just got a membership…

Talk about superb…

Bill Hernandez
Plano, Texas

Hi Bill,

To add methods to any class you create a category.

Add a new Objective-C file to your project.
Name it “NSTextView+methods.”

The .h file:

The .m file:

In the AppleScriptObjC file you use:

You can add as many methods onto Objective-C class as you like. I would document your changes for future reference.

Also, avoid using the words “get” and “set” in your method names. This will bite you later on when you start using bindings. I would only use underscores in method names to conform to AppleScriptObjC standards. Meaning, don’t use them unless you have multiple parameters and the underscores are identifying them.

I have not looked into doing videos for Lynda but I have been a member for over three years now.

hth,

Craig

Craig,

Here are the .h, .m.and AppleScriptObjC files.

This works great.

I am glad you knew the answer to my questions.

Thanks Again…

Bill Hernandez
Plano, Texas

// ±--------±--------±--------±--------±--------±--------+
// [4325] ( BEGIN ) OBJECTIVE-C METHOD INTERFACE FILE
// The .h file:
// ±--------±--------±--------±--------±--------±--------+
// NSTextView+methods.h
// tutorial_part_one
//
// Created by Bill Hernandez on 10/14/09.
// Copyright 2009 mac-specialist.com. All rights reserved.
// ±--------±--------±--------±--------±--------±--------+

#import <Cocoa/Cocoa.h>

// ±--------±--------±--------±--------±--------±--------+
// This was the default text when I created the new
// ‘Objective-C Class’ File, so I commented it out
// ±--------±--------±--------±--------±--------±--------+
// @interface NSTextView_methods : NSObject {
//
// }
//
// @end
// ±--------±--------±--------±--------±--------±--------+

// ±--------±--------±--------±--------±--------±--------+
// METHOD INTERFACE TO EXTEND NSTextView by Craig Williams
//
// To add methods to any class you create a category.
//
// Add a new ‘Objective-C Class’ file to your project.
// Name it “NSTextView+methods.”
//
// Add a new method, Xcode creates two files (an interface file, and an implementation)
// ( 1 ) NSTextView+methods.h
// ( 2 ) NSTextView+methods.m
// ±--------±--------±--------±--------±--------±--------+
@interface NSTextView (methods)
-(NSString *)stringValue;
@end
// ±--------±--------±--------±--------±--------±--------+
// [4325] ( END ) OBJECTIVE-C METHOD INTERFACE FILE
// ±--------±--------±--------±--------±--------±--------+

// ±--------±--------±--------±--------±--------±--------+
// [4326] ( BEGIN ) OBJECTIVE-C METHOD IMPLEMENTATION FILE
// The .m file:
// ±--------±--------±--------±--------±--------±--------+
// NSTextView+methods.m
// tutorial_part_one
//
// Created by Bill Hernandez on 10/14/09.
// Copyright 2009 mac-specialist.com. All rights reserved.
// ±--------±--------±--------±--------±--------±--------+

#import “NSTextView+methods.h”

// ±--------±--------±--------±--------±--------±--------+
// This was the default text when I created the new
// ‘Objective-C Class’ File, so I commented it out
// ±--------±--------±--------±--------±--------±--------+
// @implementation NSTextView_methods
//
// @end
// ±--------±--------±--------±--------±--------±--------+

// ±--------±--------±--------±--------±--------±--------+
// METHOD IMPLEMENTATION TO EXTEND NSTextView by Craig Williams
//
// To add methods to any class you create a category.
//
// Add a new ‘Objective-C Class’ file to your project.
// Name it “NSTextView+methods.”
//
// Add a new method, Xcode creates two files (an interface file, and an implementation)
// ( 1 ) NSTextView+methods.h
// ( 2 ) NSTextView+methods.m
// ±--------±--------±--------±--------±--------±--------+
@implementation NSTextView (methods)
-(NSString *)stringValue
{
return [self string];
}
@end
// ±--------±--------±--------±--------±--------±--------+
// [4326] ( END ) OBJECTIVE-C METHOD IMPLEMENTATION FILE
// ±--------±--------±--------±--------±--------±--------+

– ±--------±--------±--------±--------±--------±--------+
– [4327] ( END ) AppleScriptObjC FILE
– ±--------±--------±--------±--------±--------±--------+
– tutorial_part_oneAppDelegate.applescript
– tutorial_part_one

– Created by Bill Hernandez on 10/13/09.
– Copyright 2009 mac-specialist.com. All rights reserved.

– ±--------±--------±--------±--------±--------±--------+
– NOTES :
– ±--------±--------±--------±--------±--------±--------+
– set time_stamp to my get_time_stamp(2)

– ( n ) —> format choices { 1, 2, 3 }
– ( 1 ) —> 2009.10.13
– ( 2 ) —> [ 2009.10.13 ] ( 07.14.43.PM )
– ( 3 ) —> 2009.10.13_07.15.15.PM
– ±--------±--------±--------±--------±--------±--------+

script tutorial_part_oneAppDelegate
– ±--------±--------±--------±--------±--------±--------+
– Inheritance
– ±--------±--------±--------±--------±--------±--------+
property parent : class “NSObject”
–Option DoubleClick on —> NSTextView then click on the book icon
–Option DoubleClick on —> NSText then click on the book icon

-- +---------+---------+---------+---------+---------+---------+
-- IBOutlets	
-- Interface Builder considers an outlet as any
-- property with "missing value" as its value
-- +---------+---------+---------+---------+---------+---------+
property aWindow : missing value
property textView : missing value
property textField : missing value

property show_message : false

-- +---------+---------+---------+---------+---------+---------+
-- IBActions (button clicks)
-- Interface Builder considers an action as any
-- single parameter method ending with an underscore 
-- +---------+---------+---------+---------+---------+---------+
on setTextViewFromTextField_(sender)
	-- USER MODIFIABLE
	set which_update_method to 1 -- choices { 1, 2, 3 }
	
	-- date_object and current_date (NOT USED FOR NOW)
	set date_object to current date
	set current_date to (date_object as string)
	
	-- ( n ) ---> time_stamp format choices { 1, 2, 3 }
	-- ( 1 ) ---> [2009.10.13](07.15.41.PM)
	-- ( 2 ) ---> [ 2009.10.13 ] ( 07.14.43.PM )
	-- ( 3 ) ---> 2009.10.13_07.15.15.PM
	set time_stamp to my get_time_stamp(2) -- format choices { 1, 2, 3 }
	
	if (which_update_method is 1) then
		tell textField
			set textFieldValue to get stringValue()
		end tell
		
		tell textView
			set textViewValue to get stringValue() -- USE THE NEWLY DEFINED METHOD
			set newText to (textViewValue as string) & return & time_stamp & "  |  " & (textFieldValue as string)
			setString_(newText)
		end tell
		
		
	else if (which_update_method is 2) then
		tell textField
			set textFieldValue to get stringValue()
		end tell
		
		tell textView
			set textViewValue to get |string|()
			set newText to (textViewValue as string) & return & time_stamp & "  |  " & (textFieldValue as string)
			setString_(newText)
		end tell
		
		-- THIS ALSO WORKS OUTSIDE OF THE TELL BLOCK
		-- textView's setString_((textFieldValue as string) & return & (textViewValue as string))
		
	else		---> (which_update_method is 3)
		set previousText to textView's |string|()
		set currentText to textField's stringValue()
		
		tell class "NSString" of current application
			set newText to its stringWithFormat_("%@%@%@%@", previousText, return, time_stamp & "  |  ", currentText)
		end tell
		
		textView's setString_(newText)
	end if
	
	if (show_message) then
		set x to textView's stringValue()		-- USE THE NEWLY DEFINED METHOD
		display dialog (x as string)
		
		display dialog (newText as string)
	end if
	
end setTextViewFromTextField_
-- +---------+---------+---------+---------+---------+---------+
on get_time_stamp(which)
	if (which is 1) then
		set returnValue to do shell script "date '+[%Y.%m.%d](%I.%M.%S.%p)'"
		---> [2009.10.13](07.15.41.PM)
	else if (which is 2) then
		set returnValue to do shell script "date '+[ %Y.%m.%d ] ( %I.%M.%S.%p )'"
		---> [ 2009.10.13 ] ( 07.14.43.PM )
	else if (which is 3) then
		set returnValue to do shell script "date '+%Y.%m.%d_%I.%M.%S.%p'"
		---> 2009.10.13_07.15.15.PM
	else
		-- which is 1 or anything else
		set returnValue to do shell script "date '+[%Y.%m.%d](%I.%M.%S.%p)'"
		---> [2009.10.13](07.15.41.PM)
	end if
	return returnValue
end get_time_stamp
-- +---------+---------+---------+---------+---------+---------+
-- Application
-- +---------+---------+---------+---------+---------+---------+

on awakeFromNib()
	set time_stamp to my get_time_stamp(2) -- format choices { 1, 2, 3 }
	
	tell textView
		setTextContainerInset_({5.0, 5.0})
		setString_(time_stamp & "  |  " & "Bill Hernandez")
	end tell
	tell textField
		setStringValue_("Default text")
	end tell
	
	-- add this as the last item in the awakeFromNib handler
	aWindow's makeFirstResponder_(textField)
	
end awakeFromNib
-- +---------+---------+---------+---------+---------+---------+	
on applicationWillFinishLaunching_(aNotification)
	-- Insert code here to initialize your application before any files are opened 
end applicationWillFinishLaunching_
-- +---------+---------+---------+---------+---------+---------+
on applicationShouldTerminate_(sender)
	-- Insert code here to do any housekeeping before your application quits 
	return current application's NSTerminateNow
end applicationShouldTerminate_
-- +---------+---------+---------+---------+---------+---------+	

end script
– ±--------±--------±--------±--------±--------±--------+
– [4327] ( END ) AppleScriptObjC FILE
– ±--------±--------±--------±--------±--------±--------+

on setTextViewFromTextField_(sender)
		set textFieldValue to textfield's stringValue() as string
		set textfieldhistory to textFieldValue & return & textfieldhistory
		textView's setString_(textfieldhistory)
	end setTextViewFromTextField_

:smiley: