electronic.alchemy :: Objective C Bridge
where the past meets the future
pike > Objective C Bridge

Objective C Bridge

Created by hww3. Last updated by hww3, 14 years ago. Version #11.

Public.ObjectiveC is a Pike module that can be used to integrate ObjectiveC objects and Pike. ObjectiveC is most commonly associated with the http://developer.apple.com development environment used in MacOS X.


Originally, I wanted to be able to integrate with the Growl system to allow me to send messages and notifications from my Pike apps. Later, I decided that it might be cool to be able to write native Cocoa apps in Pike. This is the (in-progress) result of this.

Some of the features currently available in this module:

Additionally, I recently started adding support to Pike 7.7 for a Pike framework. This should allow Pike to be embedded into an Objective C application. Some (minor) additional work needs to be done to generate the proper bundle, but the dynamic library itself is being properly created. I'll include an example of using that below, for completeness.


You can download a copy of the bridge compiled for Tiger/Intel http://hww3.riverweb.com/dist/. Simply untar it into your Pike modules directory. Note that this is an old version and many new features have been added.


And now, without any further ado, some examples:

The following code demonstrates the use of the Message framework to send an email:

Pike v7.7 release 30 running Hilfe v3.5 (Incremental Pike Frontend)
> import Public.ObjectiveC;                                                                     
> load_bundle("Message.framework");                                                             
(1) Result: 0
> Cocoa.NSMailDelivery.deliverMessage_subject_to_("whee", "test message", "hww3@riverweb.com"); 
(2) Result: 1

The following example is a demonstration of the primary reason I wrote the module. This example sends notifications to the http://www.growl.info notification system. You'll need to have Growl installed in order for this to work.

import Public.ObjectiveC;
int i;
object pool;

void create() { Public.ObjectiveC.load_bundle("Growl.framework"); program g = get_dynamic_class("GrowlApplicationBridge"); g->setGrowlDelegate_(this); }

int main() { call_out(notify, 6); return -1; } object p;

mapping registrationDictionaryForGrowl(mixed ... args) {

mapping n = ([]);

n->ApplicationName = "PGrowl"; n->AllNotifications = ({"New Announcement"}); n->DefaultNotifications = ({"New Announcement"}); n->ApplicationIcon = Cocoa.NSWorkspace.sharedWorkspace()->iconForFileType_("jpg")->TIFFRepresentation();

return n; }

void notify() { mapping n = ([]);

n->ApplicationName = "PGrowl"; n->NotificationName = "New Announcement"; n->NotificationPriority = Cocoa.NSNumber.numberWithInt_(2); n->NotificationSticky = Cocoa.NSNumber.numberWithBool_(0); n->NotificationTitle = "notification from PGrowl"; n->NotificationDescription = "whooo, it's " Calendar.now()->format_smtp() "!ngreetings from Public.ObjectiveC!"; n->NotificationIcon = Cocoa.NSWorkspace.sharedWorkspace()->iconForFileType_("jpg")->TIFFRepresentation(); n->NotificationAppIcon = Cocoa.NSWorkspace.sharedWorkspace()->iconForFileType_("jpg")->TIFFRepresentation();


// lather, rinse and repeat. call_out(notify, 5); }

Or, if you prefer, without a lot of automatic type conversions:

import Public.ObjectiveC;
int i;

void create() { Public.ObjectiveC.low_load_bundle("/System/Library/Frameworks/Growl.framework"); NSClass("GrowlApplicationBridge")->setGrowlDelegate_(this); }

int main() { call_out(notify, 6); return -1; }

object p;

object registrationDictionaryForGrowl(mixed ... args) {

object n = Cocoa.NSMutableDictionary->dictionaryWithCapacity(2);

n->setObject_forKey_("PGrowl", "ApplicationName"); n->setObject_forKey_(({"New Announcement"}), "AllNotifications"); n->setObject_forKey_(({"New Announcement"}), "DefaultNotifications"); n->setObject_forKey_(NSClass("NSWorkspace")->sharedWorkspace()->iconForFileType_("jpg")->TIFFRepresentation(), "ApplicationIcon");

return n; }

void notify() { object n = Cocoa.NSMutableDictionary->dictionaryWithCapacity(6); n->setObject_forKey_("PGrowl", "ApplicationName"); n->setObject_forKey_("New Announcement", "NotificationName"); n->setObject_forKey_(Cocoa.NSNumber->new()->initWithInt_(2), "NotificationPriority"); n->setObject_forKey_(Cocoa.NSNumber->new()->initWithBool_(0), "NotificationSticky"); n->setObject_forKey_("notification from PGrowl", "NotificationTitle"); n->setObject_forKey_("whooo, it's " Calendar.now()->format_smtp() "!ngreetings from Public.ObjectiveC!", "NotificationDescription"); n->setObject_forKey_(NSClass("NSWorkspace")->sharedWorkspace()->iconForFileType_("jpg")->TIFFRepresentation(), "NotificationIcon"); n->setObject_forKey_(NSClass("NSWorkspace")->sharedWorkspace()->iconForFileType_("jpg")->TIFFRepresentation(), "NotificationAppIcon");

NSClass("GrowlApplicationBridge")->notifyWithDictionary_(n); call_out(notify, 5); }

The following example is a simple AddressBook export application. This demonstrates iterator support in certain datatypes (like NSArrays), casting of NSStrings to Pike strings and some mixins (the AddressBook constants, for example).

import Public.ObjectiveC;

array fields = ({ Cocoa.ABAddressBook.kABLastNameProperty, Cocoa.ABAddressBook.kABFirstNameProperty, Cocoa.ABAddressBook.kABEmailProperty });

int main() { object book = Cocoa.ABAddressBook.sharedAddressBook(); object p = book->people();

foreach(p;; object person) { array row = ({}); foreach(fields;; object f) { object fv = person->valueForKey_(f); if(object_program(fv) == Cocoa.ABMultiValue) row += ({(string)fv->valueAtIndex_(0) }); else row += ({(string)fv}); } write((row*",") + "n"); } }

The next example demonstrates the use of the Pike framework in Objective-C code. This example code will start up an embedded Pike interpreter and evaluate some simple code. Note that the The new Pike framework can be generated by a make framework performed in the build directory of a recent Pike 7.7.

#import <PikeInterpreter/OCPikeInterpreter.h>
#import <Foundation/NSString.h>

int main()
  id i;
  struct svalue * sv;

// required for console mode objective c applications NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init];

// these 3 lines set up and start the interpreter. i = [OCPikeInterpreter sharedInterpreter];

// we can optionally specify an alternate master, or use the one bundled in the framework. [i setMaster: @"/usr/local/pike/7.7.30/lib/master.pike"]; [i startInterpreter];

// ok, now that we have things set up, let's use it. // first, an example of calling pike c level apis directly. f_version(0); printf("%s&#110;", Pike_sp[-1].u.string->str); pop_stack();

// next, we'll demonstrate one of the convenience functions available sv = [i evalString: @"1+2"]; printf("type: %d, value: %d&#110;", sv->type, sv->u.integer); free_svalue(sv);

// finally, we clean up. [i stopInterpreter]; [innerPool release]; return 0; }

And last, but not least, is a sample of using Public.ObjectiveC with Interface Builder to produce a native Cocoa application. This is basically a Pike port of the Currency Converter application used as a tutorial by Apple. You can follow along at the http://developer.apple.com website.

import Public.ObjectiveC;

object NSApp;

class ConverterController { inherit Cocoa.NSObject;

object exchangeRate; object dollarsToConvert; object convertedAmount;

void convert_(object action) { float x; x = exchangeRate->floatValue() * dollarsToConvert->floatValue(); convertedAmount->setFloatValue_(x); } }

int main(int argc, array argv) { NSApp = Cocoa.NSApplication.sharedApplication(); return AppKit()->NSApplicationMain(argc, argv); }

This module is alpha quality code. It may crash your Pike, though it does work pretty well with threads, both Pike and NSThreads. You can also use it to build real Cocoa applications using Pike. Currently, you have to set up the application bundle yourself, but that's a relatively easy thing to do.

The TODO List

If you're a Cocoa developer and would like to help, please get in touch!

Not categorized | RSS Feed | BackLinks

comments powered by Disqus