ObjC is the language behind Apples hugely successful Cocoa libraries. ObjC is an Object Oriented language that adds Smalltalk like messaging to standard C. You can download Apples introduction to ObjC from here.
The Impromptu ObjC bridge allows you to Instantiate and bind ObjC objects as Scheme primitives as well as the ability to send ObjC messages to those primitives and receive return values. Impromptu also allows you to call back into the Scheme interpreter from within your custom ObjC code.
So, why an ObjC bridge for Impromptu? Primarily for extensibility. Providing an ObjC bridge not only provides access to Apples Cocoa libraries but it also provides the opportunity for developers to access their own Cocoa frameworks from within Impromptu. And because ObjC is a superset of C it is also possible for developers to leverage C and C++ libraries.
Even if you don't know how to program in ObjC you can still use the ObjC bridge to call into Apples Cocoa libraries, although having a basic working knowledge of ObjC is
highly advantageous.
The primary ObjC bridge functions are objc:make and objc:call as well as the predicate function objc?. objc:make is used wherever you would call [[obj alloc] init] of some type. objc:call is used to call either class or instance methods. There are also a number of functions for converting too and from scheme -> objc. These are objc:string->nsstring objc:nsstring->string, objc:number->nsnumber objc:nsnumber->number and objc:list->nsarray objc:nsarray->list. You can also print objc objects exactly as you would print any other scheme object. Also note that objc:call and objc:make will automatically convert scheme string arguments into NSString arguments (i.e. you don't need to call objc:string->nsstring before passing a scheme string to objc:call or objc:make).
(help objc:make #t)
(help objc:call #t)
(help objc? #t)
(help objc:string->nsstring #t)
(help objc:nsstring->string #t)
(help objc:number->nsnumber #t)
(help objc:nsnumber->number #t)
Let's look at a simple example to get started. First we will instantiate an NSMutableArray. Then create an NSString by calling objc:string->nsstring - turning a scheme string into an NSString. Then create an NSNumber by calling objc:number->nsnumber - turning a scheme integer or real number into an NSNumber. Add both the string and the number to the NSMutableArray by calling the NSMutableArray message "addObject:" and then pass all of the above to Impromptu's standard print function.
(define *my-nsarray* (objc:make "NSMutableArray" "init"))
(define *my-nsstring* (objc:string->nsstring "string1"))
(define *my-nsnumber* (objc:number->nsnumber 1.0))
(objc:call *my-nsarray* "addObject:" *my-nsstring*)
(objc:call *my-nsarray* "addObject:" *my-nsnumber*)
(print *my-nsstring* *my-nsnumber* *my-nsarray*)
Let's now retrieve the string back from the array and convert it back into a scheme string.
(define *scheme-string* (objc:nsstring->string (objc:call *my-nsarray* "objectAtIndex:" 0)))
(print *scheme-string*)
Let's take a look at another simple example that returns a substring starting at a given index.
(define *new-string* (objc:call *my-nsstring* "substringFromIndex:" 6))
(print *new-string*)
There are four primary types accepted as arguments by objc:call and objc:make: objc objects, integers or floats, #t or #f and scheme strings that get converted into NSStrings. Also keep in mind that you can pass 0 as in place of nil. Additionally, you can pass a scheme list that will depending on context be interpreted to be either an NSRect, NSRange, NSSize or NSPoint. NSRect is a list of four arguments, each of the others should be a list of two arguments. The next example passes a list as an NSRange.
(define *wor-string* (objc:call *my-nsstring* "substringWithRange:" '(6 3)))
(print *wor-string*)
As well as calling instance methods objc:call can call class methods by specifying the Class name (as a string) in the argument position usually reserved for the object instance. Here is an example that calls "NSFileManagers" defaultManager class method.
(define *file-manager* (objc:call "NSFileManager" "defaultManager"))
(print (objc:call *file-manager* "fileExistsAtPath:" "/tmp/myfile.txt"))
Note that "/tmp/myfile.txt" gets converted into an NSString for you. Impromptu also includes two useful helper functions that convert between an NSArray and a scheme list.
(define *mylist* (list "aaa" "bbb" *file-manager* 1 2))
(define *myarray* (objc:list->nsarray *mylist*))
(define *mylist2* (objc:nsarray->list *myarray*))
(print 'nsarray-> *myarray* 'list-> *mylist* 'list2-> *mylist2*)
OK, let's finish off by retrieving an image from the web and displaying it.
(define canvas (create-canvas))
(define url (objc:call "NSURL"
(define data (objc:call url "resourceDataUsingCache:" #f))
(define image (objc:make "NSBitmapImageRep" "initWithData:" data))
(print image)
(draw-image (now) canvas image 1.0)
Finally, note that Impromptu looks after memory management for you. Objects are retained and released by Impromptu behind the scenes on your behalf (i.e. when Scheme GC's a reference to an ObjC object that object is sent a release - retains are used behind the scenes where appropriate).
Make sure you take a look at the example 35_objc_xml_lib.scm which develops a scheme wrapper for Apples NSXML library. Future ObjC tutorials will outline how to interface with your own ObjC frameworks and NIB files.
Have Fun!
Objective-C Bridge
When Scheme just wont do!