RubyMotionの最近のブログ記事

MotionCVというOpenCVRubyMotionから使えるようにするラッパーライブラリを細々と開発してます。

現状

ある程度、枠組みはできてあとはOpenCVのAPI郡をどんどんObjective-Cでラップしていけば良い状況なのですが、APIが山ほどあってこりゃ大変だぞと(笑)

そこで、サンプルで実際に動かしながら必要なAPIから少しずつメソッドを追加していってます。上から黙々と単純作業で増やしていくだけだと心が折れる(汗) いつかはやらなきゃいけないのですが...

マンガ風フィルタサンプル

OpenCVで画像変換と言えばマンガ風カメラですからw実装しながら必要なAPIを拡充していきました。

ネットを探せばOpenCVでマンガ風フィルタを実装している例がいくつか見つかりますが、ほとんどがC APIを使っていてMotionCVでラップするのが些か面倒です。

C APIも@watson1978さんが実装方法をコミットしてくれたのでできそうですが...

ビルドには困らなくなったので暫く放置していた「OS XとiOSのためのOpenCV環境構築ガイド」をパラパラめくっていたら最後にアプリを作ってみようという章でマンガ風フィルタがC++ APIで実装されていたのですよ!

OS XとiOSのためのOpenCV環境構築ガイド
酒井 雅裕 加藤 寛人
カットシステム
売り上げランキング: 329,757


ここに載っている例で必要となるAPIをMotionCVに実装して、ロジックをRubyMotionに書き換えたのがこちらです。

cartoonize.rb

実際にiPhone Simulatorで動かしてみると、こんなふうに変換されます。

お台場のガンダム(Ruby会議帰りw)

あさぎり

初号機

実機でも問題なく動きました。

ビルド方法

最後に、この興味深いwプログラムをビルド&実行する手順を説明しておきます。

まず、サンプルプログラムをcloneしてきます。

$ cd /path/to/work
$ git clone https://github.com/iwazer/motioncv-test.git
$ cd motioncv-test

vendorの下に私がForkしてあるOpenCVをcloneして、opencv2.frameworkをビルドします。

$ pushd vendor
$ git clone https://github.com/iwazer/opencv.git
$ cd opencv
$ rake build target=all
$ popd

buildはi386(Simulator)、arm7、arm7sの3プラットフォーム分行われますのでかなり時間がかかります。

@watson1978さんがビルド用のRake Taskを書いてくれたので随分楽になりました!(・∀・)

あとはRubyMotionでほのぼのrake!

$ rake

RubyMotionのREPLはシミュレータでアプリを実行しながら内部の状態を確かめられるので便利ですが、こういう風に書けばいいのかな?といったトライアンドエラーで実験するときに、Frameworkや外部ライブラリ中のクラスや定数がプログラム中で使っていないとREPLでは未定義となっていて残念です。

こういう実験の例

使っているライブラリの定義を全て列挙したソースを生成してやれば参照できるようになるに違いないと思ってRakeタスク作りました(・ω・)

https://gist.github.com/iwazer/6292291

RubyMotionのRakefileのおしりにコピペしてpreload=trueオプションを付ければ使えます。

$ rake preload=true

だがしかし、自動生成したソースのコンパイルに5分とかかかります!m9(^Д^)

@watson1978 さんによると、そのうち本体で対応してくれるようになるかも、という事ですので、それまで用です(笑)

前回からだいぶ時間が経ってしまいましたが、実際にRubyMotionからOpenCVを使えるようにしていきます(`・ω・´)

前回紹介したとおりOpenCVはiOS用のビルドもできるのでXcode/Objective-Cで開発する場合はCocoapodsを利用すれば非常に簡単です。

RubyMotionから通常のstaticライブラリ(Framework)を利用する際には、Framework内のC/Objective-Cのヘッダファイルから定数や構造体、Objective-Cのクラス情報などをビルド時にBridge Supportというファイルに出力して、RubyMotionのはそこに定義があれば呼び出せるようになります。

RubyMotionのビルドではC++は解釈できないのでBridge Supportを作るときに読み込むヘッダファイルはCまたはObjective-Cのヘッダファイルである必要があります。RubyMotionに気づかれないようにC++部分を覆い隠すラッパーを作成していきます。

まずは最小限のラッパーを実装して動かしてみるところまでを目標にします。

サンプルは画像をグレイスケールに変換するフィルタとします。実はOpenCV for iOSの使い方 -- Qiitaのパク...いやインスパイアされてます。

Matの変換関数はMotionCVというObjective-Cのクラスのクラスメソッドとして実装することにしました。

UIImage <=> cv::Matの変換も先ほどのサンプルをそのまま利用させていただいています。

cv::Matを直接見せないようにMotionMatというObjective-Cクラスでラップすることにします。

これをOpenCVのビルド環境に組み込むのですが、リポジトリを観察した結果modulesの中に置くのが良さそうです。modules/motionというディレクトリを作成して、他のmoduleに習って下記のようにソースを配備します。

modules/motion/
├── include
│   └── opencv2
│       └── motion
│           ├── MotionCV.h
│           └── MotionMat.h
└── src
    ├── MotionCV.mm
    └── MotionMat.mm

ヘッダファイルはビルドの設定が書かれているcmake/OpenCVModule.cmakeの中でパスがinclude/opencv2/${name}/*.hと指定されているため、ディレクトリ構成を変更するとframework/Headersに入って来ません。

続いてこれらを利用するサンプルプログラムを作っていきます。

表示する部分はRubyMotionで次のようになります。

ほぼ、先ほどのページのサンプルをMotionCV/MotionMatを使用してRubyMotionに書き換えただけです。

次にRakefileに最低限必要な設定です。

追加したiOSのFrameworkはOpenCVが内部で使用しているため必要です。また-lc++もOpenCVが内部で標準C++ライブラリを使用しているため必要となります。

vendor_projectの設定に見慣れないforce_loadというオプションを指定しています。これを指定しないとduplicate symbol ...というエラーがたくさん出てコンパイルできません。

Duplicate symbol error when linking AdMob SDK in RubyMotion -- StackOverflowにこの回避方法が載っていたのでやってみたら先に進めるようになりました(・ω・)ノ

さて、これでやっとうまくいくかと思いきや、まだクラスやメソッドが見つからないというエラーで終了します。確かにBridge Supportファイルを検索しても入っていません。

Bridge Supportファイルの生成はデフォルトではvendorプロジェクト(OpenCVの場合vendor/opencv2.framework)内の"*/.{c,m,cpp,cxx,mm,h}"にマッチする全てのファイルが対象になりますので、何もしないとOpenCVのC APIのヘッダファイル全てが対象になると思われます。

ところがOpenCVのC APIにはC++のテンプレート構造体やINLINEマクロが大量に含まれていて、ほとんど処理されず空振りしているようです。

エラーになるのならばわかり易かったのですが、どうやらBridge Support出力は無視して進むように見えます。おそらく使えるものだけでも簡単に使えるようにしようという対応かと想像します

少々面倒ですが、サンプルプログラムのRakefile内に対象とすべきヘッダファイルをリストすることにします。

さて、これでもまだうまく行きません(笑)ビルドは成功するも実行時にMotionCVやMotionMatのメソッドが見つからないといって落ちます。

ビルドされたスタティックライブラリをstringsコマンドでのぞいてみると確かに存在しないようです。

OpenCVのcmake/OpenCVModule.cmakeをもう一度よくみてみると、

  :
  file(GLOB lib_srcs     "src/*.cpp")
  :

こういう設定がありました。本来ならモジュール内のCMake設定ファイルを用意して変更すべきなのでしょうが、暫定的にここにsrc/*.mmを追加してビルドしました。

iPhoneSimulatorで動いた様子

長いこと試行錯誤した結果RubyMotionTokyo meetup#4で動かすことができるようになったので写真はその時のものです。

実はラッパープログラムの方にも自分では結構長い時間分からなかったミスがあったのを@watson1978さんが見つけてくれたのも大きかったですヽ(´ー`)ノ

OpenCVのRubyMotion向けラッパーはMotionCVという名前でhttps://github.com/iwazer/opencvmotioncvブランチで開発していますので、興味のある方はチェックしてみてください。

利用するサンプルプログラムもGitHubに上げてあります。

まだ、これに毛が生えただけの状態ですが...

この記事は RubyMotion Advent Calendar 2012 の 23 日目の記事です。

#いつものように自分のブログに書こうとしたのですがコードハイライターをGistに依存してて今回はちまちま貼るのが面倒になったのでGistをオリジナルにしちゃいます(汗)

RubyMotionでTDDしつつどこまでMetaProgrammingできるかな?の実験

さてRubyMotion Advent Calendarも最後の方まで来ましたがテストに関するエントリーが依然としてない。私も実際のところRubyMotionでテスト書いたことない(ていうかまだまともなアプリ書いてないw)ですがチャレンジしてみようと思います。そんな目的なのでUIのテストは扱いません(汗)

またRubyMotionは少し制限はありますがRubyですのでメタプログラミングも魅力の一つのはずです。その辺も絡めながら書けたらいいなと思っておりますが、今日のネタは行き当たりばったりなのでどこに向かうかは分かりません(笑)というかRubyMotion Advent Calendar用のエントリーですがRuby寄りのネタになってしまうと思われます(・ω・)

メタプログラミングと言えばActiveRecordじゃん?という事で、書籍を表現するシンプルなモデルクラスBookを考えてみましょう。ただし私はしばらくActiveRecordなモデルじゃなくてMongoDB用のMongoMapperやMongoidを使っていたためそっちっぽくなります。

TDDでやってみる

TDDのRed-Green-Refactorを実践するためにテストから書きます。

#続きはこちらへ...

この記事は RubyMotion Advent Calendar 2012 の 16 日目の記事です。

いきなりRubyMotionで書き始めるのはなかなか辛いので、最初は語彙を増やすたObjective-CのサンプルプログラムをRubyMotionで実装するというのを結構やりました。

Objective-Cのコードをコピペして文法をRubyに書き換えていくのは慣れればそれほど難しくないのですが、最初は簡単なことで次々と引っかかって手が止まることが多かったので覚えている限りですがリストアップしていこうと思います。

形式プロトコルの指定は悩まないで省略してよし

モジュールのincludeとかそういうのかなー?といろいろやったりしましたが、RubyはObjective-Cよりもっと動的な言語なのでメソッドは実行時に存在すればOKという事(たぶん)で次のような形式プロトコルの採用の指定 は無視しても良さそうです。

また非形式プロトコルは既存クラスを開いてメソッドを追加する(オープンクラス)だけです。

オーバーライドして親の実装を呼ぶのはsuperだけでよい

パラメータが必要な場合でもsuperを呼ぶだけで、パラメータもそのまま親クラスのメソッドに渡されます。

型の宣言はすべてばっさり落とす

先ほどの例のメソッドパラメータのBOOLの宣言を無視したようにRubyは型の宣言は一切ないので落としてしまいます。

ターゲットにメッセージを送るの書き換え

Rubyの文法が拡張されていてパラメータ名の指定に対応されています。 (Ruby1.9の簡易Hash指定とどう区別してるのだろう...)

括弧は省略してもかまいません。

target.message3 arg1, with:arg2

クラスメソッドも同様ですので、よく見るオブジェクトの生成は

@propertyはインスタンス変数

@property指定はそのクラスのメンバーなのでRubyではインスタンス変数として扱うのが適切だと思います。

@synthesizeはattr_accessor

@synthesizeはsetter/getter生成の指定なのでRubyではattr_accessorにするのが適切だと思います。オプションによってはattr_reader/attr_writerで指定する方が適切かもしれません。

ちなみに、RubyMotionでattr_accessorを使用すると通常のrubyのアクセサ(property,property=)の他にsetPropertyというsetterも生成されます。

このsetProperty、property=と同じなのかと思いきや

cオブジェクト自身を返してます。メソッドチェーンできると言うことですね!

定数の最初の文字に気をつけよう

iOS SDKの定数は小文字から始まりますがRubyMotionではRubyの規約にあわせて大文字から始まるように変換されています。Objective-Cのソースコードをコピペして修正すると、定数の先頭を大文字にするのを忘れがちです。

エラーメッセージを見れば分かるのですが、慣れてくると見つけたらすぐに大文字にするのが習慣になります。

if文の条件に気をつけよう

Rubyの偽はnilとfalseですがObjective-CはCなので偽は0のみです。
#nilやNULLやNO(BOOL)も値としては0です。

なので、これをそのまま置き換えたらダメ。0以外が真と明示的に書かないといけない。

また、真偽値(BOOL)はObjective-CではYES(1)/NO(0)ですが、すべてtrue/falseに置き換える必要があります。

ファイルの依存関係

RubyMotionではappフォルダの中に入っているRubyソースコードをディレクトリが階層になっていてもすべてコンパイルしてくれますが、コンパイルの順番が単純な辞書順なためファイルを分けると未定義でコンパイルエラーになることがあります。

RubyMotionがリリースされたばかりでまだ情報がほとんどなかった頃は困って全部1ファイルにしたりしていましたが、依存関係をRakefileに書けば大丈夫なのでした。

ところが最近リリースされた1.28から自動で解決するようになったようです。
#が、まだうまく動かないことが多いらしいです(-ω-)

ブロック呼び出し方

Objective-Cにはクロージャを実現するブロック構文があって、アニメーションのコールバックなどでよく使います。lambdaを使います。

#この例は公式ガイドそのものですが...

lambdaじゃなくてprocでも動きますが、procはパラメータ数に無頓着だったりreturnで外側のメソッドを抜けてしまったりObjective-Cのブロックとは違う動きをするのでlambdaの方が安心です。

ポインタ

描画系のAPIを使う場合にCのポインタをライブラリに渡さなければいけないシチュエーションが多々あります。

RubyMotionのPointerを使えばC言語のポインタ操作ができますが、Objective-Cから移植していると、表記方法を混乱しがちなので落ち着いて対応していきましょう。

こういうのでもGLubyte(OpenGLESの型)をXcodeで調べればサイズはunsigned charと分かるので、

とかけます。アクセス方法は配列と同じですが相手はCの配列=メモリエリアなので何を格納するのがプログラムの意図として正しいのかよく考えて移植しましょう。

写経に使えるサンプルコードの紹介

元になるコードはGithubなどでObjective-CやiOSで検索してもたくさん出てくるでしょうが、まずは小さなプログラムから始めたいところです。

私が参考にしたサンプルコードを紹介します。

iOS Developer Library Sample Code

Appleのサンプルコード集です。

cocoa controls for iOS

iOS用のUIパターン実装例が大量にあります。移植したものが動くとかなり嬉しいです。

iOS 6 新機能のサンプルコード/ライブラリのまとめ22個 - Over&Out その後

iOS 6の新機能を紹介したブログエントリーですが、新機能をRubyMotionで実装してみる実験に良さそうです。

まとめ

Objective-Cで実装できていることは、なんでもできる...と言い切れるかはまだ分かりませんが、私がいろいろやってみたサンプルコードではひとつ(OpenELGSを使っているもの)を除いてすべて動きました。

もっといろいろ突っかかった事があった気がするのですが、思い出せたのはこのくらいでした。簡単なことでも気づいたことをブログに書くなどして残しておいた方がいいですね。

この記事は RubyMotion Advent Calendar 2012の13日目の記事です。

BubbleWrapRubyMotion Tutorialでお馴染みのClay Allsoppさんによって書かれたRubyMotion本がリリースされました。

章立て

  • Foreword
  • Ruby on iOS
  • Creating a New App
  • Filling the Screen with Views
  • Organizing Apps with Controllers
  • Representing Data with Models
  • Showing Data with Table Views
  • Testing Your Apps
  • Example: Writing an API-Driven App
  • Uploading to the App Store

序文とRubyMotionの概要

序文はRubyMotionの開発者Laurent Sansonettiさんによって書かれています。

続いてRubyMotionの成り立ちや、この本についての説明など概要部分が「Ruby on iOS」の章となっています。

3章の「Creating a New App」からは実践的になります。

ここは公式のスタートアップガイド(日本語)を詳しくしたような内容です。

RubyMotionを初めてさわる方でも迷うことなくビルドしてシミュレータが動作するところまで行けそうです。Rakefileの内容にも少し触れられていて公式のガイドより理解を深めながら実験できる作りになっています。

ViewとController

4章の「Filling the Screen with Views」はWindowとViewの階層構造やハンドリング方法の基本です。 最初は戸惑う、Viewをアニメーションさせる方法についてもわかりやすく解説されています。

5章の「Organizing Apps with Controllers」はiOS SDKプログラミングのキモであるUIViewControllerについての解説です。

rootViewControllerとして単純なUIViewControllerインスタンスを使う方法から始まって、UINavigationControllerを使ってのスタック型の画面遷移、UITabBarControllerの使い方、モーダルビューの使い方が説明されています。

このViewとControllerの解説パートはiOSの知識があるならば飛ばしても大丈夫かもしれません。

データの扱い方

6章の「Representing Data with Models」ではRubyMotionでMVCプログラムを書くための基本が説明されています。 モデルクラスを自分で定義してUIViewとUIViewControllerをどう連携させるか、データの初期化をどこでやるかなど初心者にとって迷うポイントだと思われるのでいいですね。

パラメトリックなモデルの初期化など簡単な動的モデルの構築についても書かれていて、Rubyもこれからという人にとって良いサンプルです。(ほんの触りだけではありますが)

データの保存はNSUserDefaultsを使ってでした。CoreDataを使ってRubyっぽくやる方法のアイディアがあったら良かったな〜。そこはiOSプログラミングやサードパーティライブラリのスコープで専門的すぎるのかしら。

7章の「Showing Data with Table Views」ではUITableViewにデータを表示する方法の解説です。

この章もiOSのUITableViewの知識があるならば飛ばしても大丈夫かもしれません。

テスト

8章の「Testing Your Apps」ではRubyMotionもくもく会でも毎回話題になる、みんなどういうのがベストプラクティスなのか分からなくて困っているテストについて。

しかし結論から言うと内容としては公式ドキュメントのWriting Tests for RubyMotion Apps(日本語)の方がずっと詳しい(爆)

この章の内容は、TDDでspecを書くための最初のとっかかりとして使い、UIのテストについては必要なオブジェクトをテスト時にどう引っ張ってくるかを理解するために4章の内容をじっくりやって、公式のドキュメントを参照しながら、必要なテストの語彙を自分で増やしていくのが良さそうです。

最近RailsプロジェクトでやっとTDD厨になったので、RubyMotionを使ったアプリを作るときはTDDで開発して、またの機会にそれを報告できたらいいなと思います。

実践的な開発例

9章の「Example: Writing an API-Driven App」ではこれまでの内容をもとにAPIをたたいて(Mashup!)表示をする簡単なアプリを作成する例となっています。

App Storeへの申請

10章の「Uploading to the App Store」はApp Storeへ申請するためのプロジェクトの設定方法と作業内容についての解説となっています。

iOSアプリの設定はハマりどころ満載で最初は本当に大変なので、本書の最も使える内容なのではないかと思われます。

まとめ

RubyMotionをこれからはじめる人にはとても良い内容になっていると思います。

今まではネットにある優れたチュートリアルをやるのが最初のステップとなっていましたが、さすがに書籍としてまとまっているだけあって、本書を写経して実際に動かしながら確認すればひと通りのことはできるようになるでしょう。

駆け足で紹介してきましたが、私もまだRubyMotionでアプリを作り上げたことがないレベルなので、時間を作ってひと通りやってみようと思っています。

この記事は RubyMotion Advent Calendar 2012の二日目の記事です。

第2回RubyMotionもくもく会でYahoo! iOSマップSDKのサンプルをRubyMotionに翻訳していて
http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/iphonesdk/tutorial2.html

YMKCoordinateRegionMakeのところで

Undefined symbols for architecture i386:
  "___concrete__YMKCoordinateRegionMake", referenced from:
      _MREP_F77F7B4AA08045E9933EBA24E7FDF1CA in app_delegate.rb.o
  "___concrete__YMKCoordinateSpanMake", referenced from:
      _MREP_F77F7B4AA08045E9933EBA24E7FDF1CA in app_delegate.rb.o
ld: symbol(s) not found for architecture i386

こんなエラーで落ちます。何を言ってるのか分からなかったのですがWatsonさんのおかげでサードパーティーのライブラリのマクロは自動で定義されないので存在しないことが分かりました。

大文字で始まっているのですぐ気づいても良さそうなものですが、このときは全然気づかなかった(汗)

本題はそこではなくて、frameworkのヘッダファイルからYMKCoordinateRegionMakeを探して同じ事をするメソッドを定義する際にC構造体の扱いがすぐに分からなかったこと。

該当ヘッダファイルの内容はこうでした。

マクロじゃなくてstatic inlineな関数でしたが同じ事なのでしょう。

このYMKCoordinateSpan構造体のオート変数としてspanを定義するところです。

構造体変数はframework内で使われているので勝手に他のもの代替するわけにはいきません。YMKCoordinateSpan構造体としてのアクセス領域が必要です。

これはPointerを使えば回避できます。
http://www.rubymotion.com/developer-center/guides/runtime/#_pointers

元のプログラムはオート変数を生成して値を設定しそのまま返して、値をコピーして利用している。同じ事をするためにYMKCoordinateSpan型のポインタを作ったら#valueで値を取得して使用します。([0]で値にアクセスしてもいいですが)

これでバッチリ動作するようになりました。

余談ですが、再定義しても上記のようにメソッド名をYMK...からymk...と小文字に変えないと動きませんでした。static inlineの方の定義が使えないのに名前としては参照できるからという事でしょうか...(・ω・)

ちょっと遅くなりましたがRubyMotion BlogRubyMotion API Reference, Smart File Dependenciesというエントリーが上がってます。

RubyMotion API Reference

iOS SDKのObjective-Cのリファレンスを参照して開発する必要がありましたが、Rubyの名前で定義されたリファレンスを参照できるようになりました。

情報は大して変わらないはずなのにとても見やすく感じます。

また、Dashのdocset形式も用意されているので、デスクトップアプリでサクサク索くことができるようになりました。

スクリーンショット 0024-11-19 15.21.51.png

使用するには、http://rubymotion.com/files/RubyMotion.docset.zipをダウンロードして解凍し、Dashの[環境設定>Docsets]で左下の[+]をクリックして、解答したRubyMotion.docsetを選択します。

Smart File Dependencies

Rubyプログラムファイルの依存関係を明示的に指定しなければいけなかったのが自動で解決されるようになりました。

プログラムが大きくなってきてファイルが増えてくると依存関係を自分で指示するのは非常に面倒なので、とても便利になりましたね!

こういうのが欲しかった!

RubyMotionはiOSアプリをRubyで作れる気持ちのいい開発環境ですが、RubyGemsなどを一切使えないため、全てIOS SDKを使って書かないといけません。

Objective-Cで最も面倒なコーディングのひとつであるNSDictionaryやNSArrayはRubyのHash/Arrayと相互に交換可能になっているため、かなり楽にはなってますが、通信にnet系やhttpclientを使えないのはかなり面倒でした。

ここでBubbleWrapの登場です。Rakefileにrequire 'bubble-wrap'すれば、下記のリストについて簡易なインターフェースが追加されます。

  • Core
    • App
    • Device
    • Camera
    • JSON
    • NSIndexPath
    • NSNotificationCenter
    • NSUserDefaults
    • Persistence
    • Observers
    • String
    • Time
  • Location
  • UI
    • Gestures
    • UIViewController
    • UIControl / UIButton
  • HTTP
  • RSS Parser
  • Reactor (EventMachine)

iOS SDKを直接使うより相当楽になるはずですのでバリバリ活用していきたいです!

BubbleWrap

まだ新しいプロダクトなので仕方ないのかもしれませんが具体的なエラーメッセージなどなくsimulator session ended with errorと吐いてストンと落ちることがあります。

何が原因なのか分からず戸惑うのですが、そういう時はprintデバッグで落ちてるところを見つけて例外を明示的に捕まえてあげれば分かることがあります。

続き...(Qiitaに書きました)

このアーカイブについて

このページには、過去に書かれたブログ記事のうちRubyMotionカテゴリに属しているものが含まれています。

前のカテゴリはRuby on Railsです。

次のカテゴリはScalaです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。