Posts Tagged cocoa

MacRuby tips: browse for folder or file dialog

This is yet another pretty simple tip.
Use case: let say you want your applications users to choose one or multiple files or folder on their file system. A good example would be that you want the user to choose a file to process or a folder where to save some data.

In the example above, I added a browse button and a text field.

I would like my users to click on the browse button, locate a folder and display it in the text field.

In your MacRuby controller, use a simple action method as well as an accessor to the text field:

attr_accessor :destination_path
def browse(sender)

Now, in Interface builder bind the destination_path outlet to the text field you want to use to display the path and bind the button to the browse action.

Let’s go back to our action method and let’s create a dialog panel, set some options and handle the user selection:

def browse(sender)
  # Create the File Open Dialog class.
  dialog = NSOpenPanel.openPanel
  # Disable the selection of files in the dialog.
  dialog.canChooseFiles = false
  # Enable the selection of directories in the dialog.
  dialog.canChooseDirectories = true
  # Disable the selection of multiple items in the dialog.
  dialog.allowsMultipleSelection = false
  # Display the dialog and process the selected folder
  if dialog.runModalForDirectory(nil, file:nil) == NSOKButton
  # if we had a allowed for the selection of multiple items
  # we would have want to loop through the selection
    destination_path.stringValue = dialog.filenames.first

That’s it, your user can now browse for a folder and the selection will be displayed in the text field. Look at the NSOpenPanel documentation for more details on the Cocoa API.

, ,

1 Comment

MacRuby tips: how to play an audio file

Let’s say you would like to play an audio file in your MacRuby app/script, how would you do?
It’s actually pretty simple, you just need to use NSSound. If we wanted to use a system sound we could do:


But let’s look at a more advanced example with some Cocoa patterns. We will loop through all the audio files in a folder and we will play them one after the other.

#!/usr/bin/env macruby
framework 'Cocoa'
# Cocoa documentation reference:
def play_sound
  if @sounds.empty?
    sound_file = @sounds.shift
    s = NSSound.alloc.initWithContentsOfFile(sound_file, byReference: false)
    puts "previewing #{sound_file}"
    s.delegate = self
# This is a delegate method called by the sound object
def sound(sound, didFinishPlaying: state)
  play_sound if state
# Delegate method called when the app finished loading
def applicationDidFinishLaunching(notification)
  @sounds = Dir.glob("/System/Library/Sounds/*.aiff")
# We are delegating the application to self so the script will know when
# it finished loading
NSApplication.sharedApplication.delegate = self

On my machine, I get the following output:

$ macruby macrubysound.rb
previewing /System/Library/Sounds/Basso.aiff
previewing /System/Library/Sounds/Blow.aiff
previewing /System/Library/Sounds/Bottle.aiff
previewing /System/Library/Sounds/Frog.aiff
previewing /System/Library/Sounds/Funk.aiff
previewing /System/Library/Sounds/Glass.aiff
previewing /System/Library/Sounds/Hero.aiff
previewing /System/Library/Sounds/Morse.aiff
previewing /System/Library/Sounds/Ping.aiff
previewing /System/Library/Sounds/Pop.aiff
previewing /System/Library/Sounds/Purr.aiff
previewing /System/Library/Sounds/Sosumi.aiff
previewing /System/Library/Sounds/Submarine.aiff
previewing /System/Library/Sounds/Tink.aiff

Cocoa delegation might seem a bit strange at first when you come from Ruby. In Ruby we rarely do any type of async delegation so let’s quickly look at what’s going on in this script.

The first thing we do is loading the cocoa framework which makes total sense, then we define a bunch of methods and finally we get to:

NSApplication.sharedApplication.delegate = self

We are setting the run loop to delegate to set, which means that when an event is triggered, the delegation method will be called on self (our script).
Once that done, we are starting out run loop.

Once the application is loaded the applicationDidFinishLaunching delegate is being triggered (line 24). At this point, we are looking for all the sound files in the sound system folder and storing them in an instance variable (line 25). Finally, we are calling the play_sound method (line 26).

The play_sound method checks that we have some audio files left to play (line 7), otherwise it quits the app. (line 8 ) If we still have some files in the queue, we get the first one (line 10) and use it to create an instance of NSSound (line 11).

Before playing the NSSound instance, we are setting its delegate to our script (self) (line 13). If you read NSSound Cocoa documentation for #play, you will notice that #play “initiates playback asynchronously and returns control to your application. Therefore, your application can continue doing work while the audio is playing.” In our case, we don’t want to do that, otherwise all the sounds will play at the same time.

The documentation also mentions a delegation you can use to avoid that. This is exactly what we did when we implemented:  def sound(sound, didFinishPlaying: state) (line 19)

Rubyists might be surprised by the method signature. MacRuby extended Ruby to support Objective-C selectors.

When the sound is playing, this delegate gets called, we check on the state of the sound, if it finished playing then we call play_sound again.

That’s it!  It’s a very elegant implementation giving us the benefits of both Ruby and Cocoa.

, , ,

No Comments

MacRuby tips: embed a custom font

Let say you want to release your MacRuby app and use a custom embedded font?
You probably don’t want to force your users to install the font.
Well, don’t worry, just put the font file in your resources folder and use the following code:

font_location = NSBundle.mainBundle.pathForResource('MyCustomFont', ofType: 'ttf')
font_url = NSURL.fileURLWithPath(font_location)
# in MacRuby, always make sure that cocoa constants start by an uppercase
CTFontManagerRegisterFontsForURL(font_url, KCTFontManagerScopeProcess, nil)

That’s it, now your custom font is available and you can change your textfield instance’s font like that:

text_field.font = NSFont.fontWithName('MyCustomFont', size:24)

The only tricky things here were to know the Cocoa API call and to know that even if the Cocoa API references the constant to use as kCTFontManagerScopeProcess, because in Ruby, constants start by an uppercase, you need to convert it to: KCTFontManagerScopeProcess.

, , , , , ,


MacRuby, changing the Ruby ecosystem

What’s MacRuby?

MacRuby is an Apple-sponsored, open source, full Ruby implementation on top of Objective-C runtime. In other words, whatever code runs on Ruby 1.9, should/will run on MacRuby. Yes, you read correctly, MacRuby can/will be able to run all your Ruby code. That means that eventually you will even be able to run your Rails/Sinatra/new-sexy-ruby-framework app on MacRuby.

Unlike RubyCocoa, MacRuby is not a bridge, it is a full implementation of the Ruby language on top of Apple’s Objective-C runtime. Taking a huge shortcut, MacRuby implements the Ruby syntax by aliasing it to the Obj-C language counterpart. A Ruby string instance is really in fact, an instance of NSMutableString. This is obviously transparent for you as a developer since you have the same Ruby API, but it also means that MacRuby can make use of the various Objective-C’s goodies such as native threads,  garbage collector in the background as well as the runtime performance.

On top of that, you have full access to the Obj-C API from your Ruby code. (tip: in macirb, try “my string”.methods(true, true).sort to see the available Ruby + Objective-C methods on your String instance)  The reason why having access to Objective-C is important is because it gives you the possibility to write native Cocoaapps using Ruby. For those who don’t know Cocoa, is a set of APIs for MacOSX development.

However, note that even though, the Cocoa support is almost complete and stable, MacRuby is still in development, especially on the Ruby side of things.

What is it not?

  • MacRuby is not a fork of Ruby. Full rubyspec compliance is expected! It’s true that MacRuby supports smalltalk/Obj-C method selectors so it might be considered a language superset.
  • MacRuby is not limited to the OSX platform. All its dependencies are open source and could possibly be compiled for other POSIX-based systems such as Linux.. (not done yet)
  • Even if MacRuby’s primary goal is to allow you to write efficient Cocoa apps, it does not mean that MacRuby is limited to that.
  • MacRuby doesn’t require you to learn Objective-C in order to develop Cocoa apps. (you just need to understand Obj-C selectors)

What’s coming up?

The current version of MacRuby (today being the 27th of May 2009) is version 0.4. You might have heard of things about MacRuby crazy performance, LLVMAOT compilationAOT compilation etc… This is all happening in the ‘experimental’ branch.

What’s going on is that up to MacRuby 0.5, MR was  using YARV (Ruby 1.9 interpreter) on top of Obj-C and which obviously limited MacRuby’s  to YARV’s performance. After RubyConf 2008, Laurent Sansonetti, MacRuby’s lead developer, decided to try removing YARV to only keep its AST and experiment with using LLVM instead of the 1.9 interpreter.

This switch turned out to be very promising and was noticed right away by influential people such as IBM’s Antonio Cangiano. Since then, performance and compatibility have increased. Laurent even started working on an Ahead Of Time (AOT) compiler: What’s really impressive is that in this specific example (Fibonacci sequence), MacRuby’s compiled code is faster than Objective-C! But let’s not jump the gun. First this is a very very early prototype and most of your apps won’t be using Fib. sequences ;) In this case, MacRuby’s recursive method dispatch is faster but again, this is just a proof of concept and even though MacRuby is getting close to Obj-C speed, it’s still far from matching Obj-C’s impressive performance.

What this basically means is that you will be able to compile your Ruby code down to binary code. Imagine, taking your Rails app and compiling it down to a binary file that you can just push to your server :) But really, what’s almost more important is that Ruby will get closer to Objective-C’s speed.

Why will MacRuby change the Ruby ecosystem?

As a web developer, getting better performance is great. But Ruby is already fast enough. Rails is faster than any PHP framework out there and when doing Merb’s benchmarks we proved that Ruby for the web can definitely be fast.

Now if MacRuby ends up running 3-7X faster than Ruby 1.9 (no one can tell for sure), existing Ruby developers will certainly be really pleased but it will probably affect more people outside of our community than within. Let’s face it, our community isn’t that big but it’s growing fast and people are mainly coming to Ruby for its web frameworks. But Ruby has much more than that to offer. Desktop applications, video games, scientific computation and even embedded apps. Apple is betting on Ruby probably because of the fact that they see the potential in the language itself.

Would people still use Java if Ruby is as fast/faster than Java? Probably! Would they think about using Ruby for their next project? I would certainly hope so!

Ruby is viral, it’s such a great language, people who have started using it are having a hard time going back to what they used before. But to spread the ‘love’, we need to give people the opportunity to discover why Ruby is so great and to do that, we need to make sure Ruby is relevant to them. By making Ruby a realistic option to write desktop/mobile applications, we are targeting a new audience. An experienced audience which will be able to bring a new perspective to our ecosystem and help it grow.

Of course, MacRuby isn’t the only implementation out there trying to do that. JRuby and IronRuby are also very interesting projects. My take on it, is that MacRuby will be able to change things because of its new approach and potential community. It will more than likely be the first Ruby implementation compiling down to binary code, it will more than likely be the fastest implementation and it will more than likely draw a different type of developer.

Does it mean that JRubyIronRubyBlueRuby will be useless? Absolutely not! Matz is the first one to encourage diversity and I agree that this is a great way to move forward. These projects solve different problems and all have pros and cons, but they also all share a similar goal: making Ruby a better and more popular language outside of its original community. JRuby, IronRuby and MacRuby bring Ruby to the respective Java, .net and Cocoa communities, and indirectly bring fresh developers to Ruby. These implementations are critical in the way they actually bridge existing communities and in the end, all Rubyists will benefit from it. Also, even though MacRuby and IronRuby are in active development, JRuby is the only mature alternative to MRI/YARV at this point and it proved that Ruby can coexist in a different community as well as contribute a lot of interesting things back to the Ruby community.

To summarize, I see tremendous potential in MacRuby. There is the obvious technical aspect of the implementation, but also the indirect affect MacRuby could have on our community. I believe that MacRuby is an agent of change and will help bringing more diversity to our community. It will change mentalities and push Ruby to places where it’s being struggling to make a mark. I can see the so-called “Enterprise” people looking at Ruby differently. I also think that MacRuby has the potential to be at the origin of a new type of hybrid application, mixing desktop/mobile/distributed applications with centralized web applications. Why not dream of p2p applications/games using a subset of Rails to communicate between each other and with a central server?


If you are interested in learning more about MacRuby, check the list of resources available on the MacRuby site.

Rich Kilmer and I are also working on a documentation application for Cocoa and HotCocoa as well as a MVC framework for writing Cocoa apps using HotCocoa (HotCocoa is a thin, idiomatic Ruby layer that sits above Cocoa and other frameworks).

Make sure to keep an eye on @macruby and the MacRuby website if you want to keep track of the latest news.

, , , , , , ,