Loading Objective C Frameworks and Bundles

Watson1978 edited this page Apr 9, 2012 · 8 revisions

MacRuby has native support for loading Objective-C frameworks and bundles. At this time, the only requisite is that the framework or bundle be compiled with support for the Objective-C GC (-fobjc-gc).

Frameworks

Loading frameworks is as easy as calling Kernel#framework and passing the name of the framework as a parameter. MacRuby will check in the standard places for frameworks (e.g. /Library/Frameworks, /System/Library/Frameworks, etc.); if the framework is not located in one of the standard locations you can still pass the absolute path to the framework.

framework "Cocoa"
framework "Foundation.framework"
framework "/Users/mrada/Developer/SecretProject/TCMPortMapper.framework"

Once the framework is loaded, all Objective-C classes and methods defined in the framework will be available. However, constants will not be loaded unless the framework includes a bridge support definitions file.

Bundles

Bundles can be loaded using Kernel#require just as if they were any other ruby C extension. In this case, MacRuby will treat the bundle as if it were a C extension and try to initialize it by calling the void Init_BUNDLE() function, where BUNDLE is the name of the bundle being loaded. In normal Ruby C extensions, this function would be used to define classes, methods, etc. in the runtime, but can be an empty function for Objective-C bundles.

Like frameworks, once the bundle is loaded, Objective-C classes defined in the bundle will be immediately available, but bridge support is needed for constants.

You may write a Objective-C as following.

// OSInfo.m
#import <Foundation/Foundation.h>

@class OSInfo;

@interface OSInfo : NSObject
+ (int)version;
@end

@implementation OSInfo
+ (int)version
{
    SInt32 version = 0;
    Gestalt(gestaltSystemVersion, &version);
    return version;
}
@end

void Init_OSInfo(void) {} // Entry point for bundle

You can create a bundle easily with extconf.rb

# extconf.rb
require "mkmf"
$CFLAGS << ' -fobjc-gc '  # use a GC 
create_makefile("OSInfo") # Specify a bundle name which means BUNDLE

Let's create a bundle and use it!

$ macruby extconf.rb 
creating Makefile

$ make
/usr/bin/gcc -dynamic -bundle -undefined suppress -flat_namespace -arch x86_64 -o OSInfo.bundle OSInfo.o -L. -L/Library/Frameworks/MacRuby.framework/Versions/0.12/usr/lib -arch x86_64   -lmacruby

$ macirb 
irb(main):001:0> require 'OSInfo'
=> true
irb(main):003:0> OSInfo.version.to_s(16)
=> "1073"  # Means that MacRuby runs under OSX 10.7.3

Bridge Support

Bridge Support is an FFI interface that MacRuby uses to provide a native interface for some C functions, structures and constants. Bridge support files are included with all OS X system frameworks and will be loaded automatically when you load the framework. For non-system frameworks, bridge support will be loaded if it is included, but you can still generate and load the bridge support files yourself if they were not included.

The gen_bridge_metadata script is the standard way of generating bridge support files. Once the files are generated, you can load them using Kernel#load_bridge_support_file and passing the path to the file as the parameter.

You may write a Objective-C header as following.

// OSInfo.h
#define VERSION_LION 		 0x1070
#define VERSION_SNOW_LEOPARD 0x1060

To generate Bridge Support file, runs below command in Terminal.app.

$ gen_bridge_metadata -c '-I .' OSInfo.h > OSInfo.bridgesupport

OSInfo.bridgesupport contains as following.

<?xml version='1.0'?>
<!DOCTYPE signatures SYSTEM "file://localhost/System/Library/DTDs/BridgeSupport.dtd">
<signatures version='1.0'>
<enum name='VERSION_LION' value='4208'/>
<enum name='VERSION_SNOW_LEOPARD' value='4192'/>
</signatures>

Let's use a Bridge Support file.

$ macirb 
irb(main):001:0> load_bridge_support_file 'OSInfo.bridgesupport'
=> main
irb(main):002:0> VERSION_LION
=> 4208
irb(main):003:0> VERSION_SNOW_LEOPARD
=> 4192
irb(main):004:0>