Ruby FFI bindings for the OSX Cocoa API を使うと楽にMacアプリが書ける


Ruby FFI bindings for the OSX Cocoa API というものがあったので使ってみました。

インストール

activesupport requires Ruby version >= 2.2.2.とあるので、まず独自にRubyをインストール。Homebrewを使ってみました。

brew install ruby

最新バージョンのRubyが使えるのを確認後、cocoa gem をインストール。

gem install cocoa

準備は以上です。

サンプルコード

require 'cocoa'

Cocoa::NSAutoreleasePool.new

app = Cocoa::NSApplication.sharedApplication
app.setActivationPolicy Cocoa::NSApplicationActivationPolicyRegular
app.activateIgnoringOtherApps true

alert = Cocoa::NSAlert.alloc.init.autorelease
alert.setMessageText "Hello world!"
alert.runModal

メモリ管理は NSAutoreleasePool を使用した半自動管理なコードになるようです。

もう少しMacアプリっぽくウィンドウアプリを書いてみました。

hello.cocoa.rb
#!/usr/local/bin/ruby

require "cocoa"
Cocoa::NSAutoreleasePool.new

class UserInterface < Cocoa::NSObject
  attr_accessor :window, :btnHello, :btnGoodbye
  def init
    @window = NSWindow.alloc.initWithContentRect(NSRect.new(x: 0, y: 0, width: 250, height: 100), 
      styleMask: NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask, 
      backing: NSBackingStoreBuffered, 
      defer: false).autorelease
    @window.setTitle "Welcome to Ruby"
    @window.setReleasedWhenClosed true
    @window.center

    @btnHello = NSButton.alloc.initWithFrame(NSRect.new x: 10, y: 10, width: 110, height: 80).autorelease
    @btnHello.setTitle "Hello!"
    @btnHello.setBezelStyle NSRegularSquareBezelStyle
    @btnHello.setSound NSSound.alloc.initWithContentsOfFile("/System/Library/Sounds/Tink.aiff", byReference:true).autorelease
    @window.contentView.addSubview btnHello

    @btnGoodbye = NSButton.alloc.initWithFrame(NSRect.new x: 130, y: 10, width: 110, height: 80).autorelease
    @btnGoodbye.setTitle "Goodbye!"
    @btnGoodbye.setBezelStyle NSRegularSquareBezelStyle
    @btnGoodbye.setSound NSSound.alloc.initWithContentsOfFile("/System/Library/Sounds/Basso.aiff", byReference:true).autorelease
    @window.contentView.addSubview btnGoodbye

    return self
  end
end

class AppDelegate < Cocoa::NSObject
  def init
    @ui = UserInterface.alloc.init.autorelease
    @ui.btnHello.setAction :hello
    @ui.btnGoodbye.setAction :goodbye
    return self
  end

  def hello sender
    puts "hello"
  end

  def goodbye sender
    puts "goodbye"
    $application.terminate nil
  end

  def applicationDidFinishLaunching notification
    @ui.window.makeKeyAndOrderFront self
  end

  def applicationShouldTerminateAfterLastWindowClosed sender
    return true
  end

end

$application = Cocoa::NSApplication.sharedApplication
$application.setActivationPolicy Cocoa::NSApplicationActivationPolicyRegular
$application.setDelegate AppDelegate.alloc.init.autorelease
$application.activateIgnoringOtherApps true
$application.run

コツはサブクラス定義でCocoa::NSObject由来のクラスを継承する、サブクラスのイニシャライザーでselfを返す、autoreleaseを忘れずに、と言ったところでしょうか。

RubyでMacアプリを書けるのは楽しいです。