To import a framework in RubyCocoa, you use a combination of Ruby’s require method and RubyCocoa’s require_framework addition:
# RubyCocoa code require 'osx/cocoa' OSX.require_framework('CoreData')
MacRuby unifies these methods with the framework method added to the Kernel module:
# MacRuby code framework 'Cocoa' framework 'CoreData'
In RubyCocoa, all imported Objective-C classes are located inside the OSX module. In MacRuby, Objective-C classes are imported into the main namespace.
Because RubyCocoa is not implemented on top of Objective-C, it requires objects passed to Objective-C APIs to inherit from NSObject:
# RubyCocoa code class MyClass < NSObject end
In MacRuby, the Objective-C NSObject and Ruby Object classes are identical; therefore, because Ruby classes implicitly inherit from Object, all objects in MacRuby are valid NSObjects. Explicit inheritance is therefore no longer required:
# MacRuby code class MyClass end
Many of MacRuby’s built-in data structures are toll-free bridged to corresponding Cocoa or CoreFoundation equivalents:
String class is bridged to NSString, NSMutableString, CFStringRef, and CFMutableStringRef.Array class is bridged to NSArray, NSMutableArray, CFArrayRef, and CFMutableArrayRef.Hash class is bridged to NSDictionary, NSMutableDictionary, CFDictionaryRef, and CFMutableDictionaryRefInteger and Float classes are bridged to NSNumber and CFNumberRef.Because these classes are toll-free bridged, the #to_ns and #to_ruby converters present in RubyCocoa code are no longer necessary and have been removed.
Certain Cocoa data types, such as NSRect, NSSize, and NSPoint are C-style structs, not Objective-C objects. Functions that return structures of these types will return them wrapped in corresponding NSRect, NSSize, and NSPoint classes:
$ macirb --simple-prompt >> framework 'Foundation' => true >> rng = NSMakeRange(0, 10) => #<NSRange location=0 length=10> >> rng.class => NSRange
Because Cocoa code often requires working with C struct types, MacRuby allows you to pass in an Array containing the required data where a C struct would normally be expected. Some examples follow:
# MacRuby code window.frame = NSMakeRect(0, 0, 100, 200) # Using the NSMakeRect function window.frame = NSRect.new(NSPoint.new(0, 0), # Using the MacRuby NSRect class NSSize.new(100, 200)) window.frame = [0, 0, 100, 200] # Using a array in flat style window.frame = [[0, 0], [100, 200]] # Using an array [point, size] style
Please note that MacRuby’s Range, Set, and Enumerator classes are not bridged to their Cocoa or CoreFoundation equivalents.
Objective-C’s interleaved-keyword syntax is not compatible with traditional Ruby syntax. To be able to call Cocoa API’s from RubyCocoa, you use the “underscore hack”, which translates Ruby-style function calls into ObjC calls by joining the selector’s components together with underscores:
# RubyCocoa code NSFileManager.defaultManager.createFileAtPath_contents_attributes(p, c, a)
In order to make the use of Objective-C methods from MacRuby as natural as possible, the MacRuby team extended standard Ruby syntax to support Objective-C style interleaved arguments:
# MacRuby code NSFileManager.defaultManager.createFileAtPath(p, contents:c, attributes:a)
This is the best way to call Objective-C methods; code written using the underscore hack or RubyCocoa’s objc_send function will fail to run under MacRuby.
MacRuby allows you to use this syntax in your own method declarations, too:
# MacRuby code def saveWorld(planet, fromVillain: badguy, withHero:superhero) superhero.fight(badguy) planet.thank(superhero) end
However, it’s important to note that this syntax is unique to MacRuby; code written in that uses this syntax will fail to run on any other implementation of Ruby.
MacRuby’s integration with Interface Builder is significantly cleaner than RubyCocoa’s. Previously, declaring an instance variable that also functioned as an Interface Builder outlet required two declarations, one to declare Ruby-style accessors and another to declare a key-value compliant accessor:
# RubyCocoa code class MyView < NSView attr_accessor :pdfView ib_outlet :pdfView end
MacRuby extends the semantics of attr_accessor and attr_writer to provide a key-value compliant setter method that Interface Builder will recognize. As such, all references to the ib_outlet method can be removed:
# MacRuby code class MyView < NSView attr_accessor :pdfView end
Another significant improvement comes in the form of transparent support for Interface Builder actions. RubyCocoa required that IB actions be declared with the ib_action helper method:
# RubyCocoa code class MyController < NSObject ib_action(:firstPage) do |sender| currentPDFView.setPageNumber(1) end end
Because of MacRuby’s tight integration with the Objective-C runtime, there is no special syntax required to declare an IB action: any Ruby method can serve as an IB action, though it must have a sender parameter like in Objective-C:
# MacRuby code class MyController def firstPage(sender) currentPDFView.pageNumber = 1 end end
In RubyCocoa, the super() function did not work correctly if the superclass method being called was implemented in Objective-C. A further extension to the underscore hack became necessary:
# RubyCocoa code class MyClass < NSObject def init super_init() # Calls -[NSObject init] p 'Initializing!' self # Always return self from init! end end
MacRuby rectifies this inconsistency, allowing the super() function to call both Ruby and Objective-C methods:
# MacRuby code class MyClass def init super # Also calls -[NSObject init] p 'Initializing!' self # Always return self from init! end end
Though Ruby’s Exception class and Objective-C’s NSException class are not technically bridged classes, begin...rescue blocks in MacRuby can catch both Ruby Exceptions and Cocoa NSExceptions. Conversely, you can catch MacRuby Exceptions with Objective-C’s @try/@catch.
The MacRuby team has ported the syntactic extensions present in RubyCocoa for cases when Objective-C idioms differ with Ruby idioms, such as in the case of mutator methods and boolean accessor methods:
layer.setPosition([100, 100]) # Objective-C style layer.position = [100, 100] # Ruby style; calls the same method the Objective-C one does layer.isHidden # Objective-C style layer.hidden? # Ruby allows the '?' character in method names
MacRuby provides additional syntax for the commonly-used objectForKey: and setObject:forKey: methods:
obj.objectForKey('data') # Objective-C style obj['data'] # Ruby style obj.setObject('mystery', forKey: 'data') # Objective-C style obj['data'] = 'mystery' # Ruby style
RubyCocoa added extensions for easy manipulation of property lists:
# RubyCocoa code array_data = ['a', 'b', 'c'].to_plist OSX.load_plist(array_data)
MacRuby ports these extensions, adding load_plist to the Kernel module and to_plist to Object:
# MacRuby code array_data = ['a', 'b', 'c'].to_plist load_plist(array_data)