RubyMotion:RubyのネイティブiOSアプリケーション(翻訳)



2007年、Apple開発者のLaurent Sansonettiは、オープンソースプロジェクトであるMacRubyを設立しました。 彼の目標は、RubyとCocoa OS Xエコシステムの間の透過的な相互作用を提供するObjective-Cランタイム上にRubyインタープリターを作成することでした。そして彼は成功しました。 現在、SansonettiはiOSでも同様のことをしたいと考えています。





最近、SansonettiはAppleを辞め、過去7年間、HipByteというスタートアップを設立するために働きました。 彼は本日、最初の製品を発表しました[ 5月3日-約。 perev。 ] RubyMotionと呼ばれる開発ツールは、Rubyプログラミング言語でネイティブiOSアプリケーションを作成する可能性を開きます。



サンソネッティが初期のクローズドベータ製品へのアクセスを許可した3月からRubyMotionをテストしました。 この記事では、RubyMotionの独占的な直接レビューを提示し、それを使用してiOSソフトウェアを作成する方法を説明します。 この記事には、redditのトップ投稿のリストを表示する、私が作成したシンプルなiOSデモのすべてのソースコードが含まれています。



RubyMotion


RubyMotionはMacRubyの背後にあるまったく同じObjective-C Ruby実装の上に構築されていますが、新しいLLVMコンパイラを使用してRubyコードを最適化されたネイティブコードに変換します。 RubyMotionコンパイラーは、通常のRubyコードに固有のパフォーマンス制限のない効果的なネイティブアプリケーションを生成します。







RubyMotionで作成されたモバイルアプリケーションは、Objective-Cと同等の速度で実行され、相応の量のハードウェアリソースを使用します。 さらに、RubyMotionアプリケーションはAppleのApp Storeの要件を完全に満たしています。 Sansonettiによると、検証のすべての段階を通過したいくつかのRubyMotionアプリケーションがApp Storeですでに受け入れられています。



すべての標準iOS APIはRubyMotionで利用可能です。つまり、Ruby開発者はObjective-C開発者のすべての機能を利用できます。 さらに、RubyMotionアプリケーションは、プラットフォームの他の部分の外観と一致させることができます。 UIKitのウィジェットの標準セットを使用します。



RubyMotionを使用したソフトウェア開発


サンソネッティは、RubyMotionをAppleのIDEに組み込むのではなく、Rubyの従来の方法であるコマンドラインツールを使用することにしました。 モーションコンソールチームは、コード、グラフィックス、およびその他のリソース用のフォルダーを含むアプリケーションブランクを含むテンプレートを使用して、新しいプロジェクトを生成します。 appディレクトリにある.rbファイルは、最終アセンブリに自動的にコンパイルされます。 RubyMotionでは、requireキーワードを使用する必要はまったくありません。



RubyMotionのビルドプロセスは、ほとんどのRubyプログラマーに馴染みのあるRakeツールに依存しています。 Rakeビルドオプションは、依存関係およびその他のアプリケーション設定を指定するために使用されます。 コマンドラインからrakeが起動すると、アプリケーションがコンパイルされ、iOSシミュレーターで起動されます。



シミュレーターでアプリケーションを起動することに加えて、ターミナルでREPLを使用できます。これにより、Ruby式を使用して実行中のアプリケーションと対話的に対話できます。 ウィジェットのプロパティやアプリケーションの内部データ構造をオンザフライで変更する機能は、エラーをテストおよび検出するときに非常に役立ちます。







Rakeのターゲットを使用して、デバイスでアプリケーションを起動したり、.ipaパッケージを作成してApp Storeに配布したりすることもできます。 デジタルコード署名の制限により、デバイスでアプリケーションをテストするにはApple Developer Programサブスクリプションが必要です。



シミュレータでRubyMotion redditデモをコンパイルして実行すると、MacBook Air 2011で約5秒かかりました。コンパイル時間はそれほど長くはありませんが、変更後すぐに結果を見ることに慣れているRuby開発者にとっては珍しいことです。 コンパイラに加えて、RubyMotionには独自のruby



コマンドが付属しており、RubyMotion環境でRubyスクリプトを実行できます。



RubyMotionの開発プロセスは非常に快適です。 Vimでコードを記述し、ターミナルを開いたままにして、Rakeを起動してプログラムをテストします。 Xcodeと比較した1つの重要な欠点は、視覚的なインターフェース設計ツールを使用できないことです。 UIKit APIを詳しく調べる必要があるため、これは初心者の開発者に問題を引き起こす可能性があります。



サンソネッティと話す


Xcodeビジュアルツールとの非互換性について質問したところ、Xcodeとの統合は実装されるが、近い将来には実装されないと答えました。



代わりに、プログラムでiOSインターフェイスを作成するためのRubyライブラリのセットを作成します。 これらのライブラリは、UIKit APIの高レベルラッパーになり、使いやすくなります。 このアプローチはMacRubyのHotCocoaライブラリと同じですが、抽象度がより高くなっています。



「私たちは、RubyMotion用の一連の高レベルRuby gemの作成に注力しています。 それらの1つはUIKitの軽量ラッパーです」と、Sansonetti氏は語りました。 「私たちは、CSSに少し似たサブジェクト指向言語を使用して、インターフェイスマークアップシステムを実装することにしました。そのため、Ruby開発者は、くつろいでいます。 私たちのアイデアは、 Cocoaの自動レイアウトに似ています [ Cocoaの自動レイアウト-約 perev。 ]、ASCIIに似た言語で構築されていますが、Rubyでさらに改善できます。 これは、Interface Builderよりも信頼性の高いUIを作成する方法であると考えています。 このすべてをコードで視覚化できます。」



これらのライブラリをRubyMotionに埋め込む代わりに、サンソネッティはそれらを許容ライセンスの下でGitHubに配置します。 彼は、RubyMotionサポーターが異なるスタイルの独自のマークアップライブラリを作成すると信じています。 また、アプリケーション開発者には幅広い選択肢があります-ネイティブラッパーまたはサードパーティラッパーを使用します。



Sansonettiに、Appleのオープンソースプロジェクトとして設立したMacRubyプロジェクトとのやり取り、およびRubyMotionの開発に注力した後のプロジェクトの進化について尋ねました。 彼は、MacRubyでの作業を続けたいという願望が、Appleからの離脱と会社の設立の主な理由であると説明しました。



「アップルのコアOSチームのメイン開発者として、私はMacRubyを作成および開発しましたが、MacRubyは私のすべての責任の一部にすぎませんでした」と彼は言いました。 「MacRubyがかなり安定した後、その開発を放棄しなければならないことに気付きました。 私はプロジェクトとその素晴らしいコミュニティを去りたくなかったので、しばらくの間考えて、RubyMotionスタートアップを作成することが私にできる最善のものであることに気づきました-私はMacRubyで働き続け、同時に生計を立てます。」



彼はAppleを離れてから6か月の移行期間中にMacRubyの開発への参加を一時的に停止せざるを得ませんでしたが、現在は復帰しており、仕事をやめません。 現在、彼は彼の新しい会社の唯一の従業員ですが、「年末近くにMacRubyコミュニティから数人のメンバーを雇って」チームを編成する予定です。



RubyMotionを単独で作成することは、難しい技術的なタスクでした。 開発中に克服しなければならなかったポイントのいくつかを説明するように依頼しました。



「最も難しいのは、RubyMotion用のまったく新しい静的コンパイラとメモリモデルの実装でした」と彼は言いました。 「ARM ABIと低レベルプロトコルの実装も複雑でした。 これには、デバイスの実行速度を微調整するのにかなりの時間がかかりました。」



また、Sansonettiは、表現力と柔軟性の高いRubyが開発速度を向上させる方法についてのビジョンを共有しました。



「最も重要なフィッチの1つは、他の多くのプラットフォームにはない、ルービストが当たり前のことと考えているインタラクティブな環境です。 リアルタイムで変更を監視できることは、アプリケーションのデバッグとテストを行う際に非常に役立ちます」と彼は言いました。 「Rubyは簡潔で表現力豊かな言語です。 Ruby iOSアプリケーションには、同様のObjective-Cアプリケーションよりも大幅に少ないコード行が含まれます。 コードが少なくなると、開発サイクルが短くなり、バグが少なくなり、メンテナンスが容易になり、Skyrimをプレイする時間が長くなります。



橋の建設


動作するアプリケーションを作成する前に、RubyMotionがRubyとObjective-Cランタイムをどのように橋渡しするのかを見てみましょう。



Objective-Cは、オプションの動的型付けやオブジェクト指向プログラミングなどの機能を追加するC拡張機能です。 NeXTは、このプログラミング言語をStepstoneからライセンス供与しました。Stepstoneは、言語のソースであり、それを使用してNeXTstep OSのフレームワークを作成し、後にMac OS Xになりました。それ以来、AppleはObjective-Cの改良と改善を続けています。



Objective-Cには、言語間の相互作用を容易にするRubyのいくつかの共通プロパティがあります。 RubyとObjective-Cには、Smalltalkから取得した同様のメソッド呼び出しメカニズム(メッセージの送信)があります。 それらのオブジェクトモデルは、単一の継承を観察し、優れたイントロスペクション機能を備えているという点で似ています。 Objective-Cは、Rubyブロックに似た軽量の匿名関数を作成するための新しい構文メカニズムを最近受け取りました。



これらの機能を使用すると、RubyMotionの開発時に慣れ親しむことができますが、軟膏にハエがあります。 最大の障害は、Objective-Cでのメソッドシグネチャの提示とその呼び出しです。これは、開発者が他の一般的なプログラミング言語で見るものとは根本的に異なります。



Objective-Cのメソッドは、パラメーターが名前の一部になるように設計されています。 一見、これは命名規則のように思えるかもしれませんが、実際には異なります(Objective-Cメソッドの構文の説明はこの記事の範囲外ですが、詳細な説明のあるリソースがいくつかあります)。



RubyMotionユーザーにとっての主な結論は、Rubyで使用する場合、Objective-C APIは少し変わった形式で表示されるということです。 この問題を説明するために、この記事の後の方でデモredditアプリケーションで使用されるUIKitの例を紹介します。 このアプリケーションでは、テーブルの行を選択解除する必要がありました。 Objective-Cでは、次のようになります。



 [tableView deselectRowAtIndexPath:indexPath animated:YES]
      
      







実際のメソッド名はdeselectRowAtIndexPath:animated



です。 NSIndexPath *およびBOOL型のパラメーターの値は、呼び出されたときに関数の名前に続く場所で埋め込まれます。 Rubyにはこの構文はありません。 名前付きパラメーターの規則に従いますが、括弧を使用したより伝統的なスタイルです:



 tableView.deselectRowAtIndexPath(indexPath, animated:true)
      
      







MacRubyとRubyMotionには、Objective-Cと同等のハイブリッドシステムがあります。 このハイブリッドシステムでは、すべての標準Rubyタイプが標準Objective-Cタイプの上に実装されます。 たとえば、すべてのRubyオブジェクトはNSObjectを継承し、Rubyの文字列はすべてNSMutableStringです。 MacRubyアプリケーションコンソールでString.ancestors



コマンドを呼び出すと、これが何を意味するのかを知ることができます。



 => [String, NSMutableString, NSString, Comparable, NSObject, Kernel]
      
      







このタイプのタイプ等価は、ランタイムのフレームワーク内で非常に効果的です。 RubyとObjective-C APIの間で複雑なデータ型を変換するために重いコンピューティングを行う必要はありません。



別の利点は、これらの基本クラスに付属する一般的に使用される便利なメソッドがすべて一般的に利用可能になったことです。 これは、コンテナで作業する場合、たとえば、チェーンメソッドNSMutableArray map



group_by



およびsort_by



をブロックで使用する場合に大きな利点をもたらします。



デモアプリケーション


過去1か月にわたって、RubyMotionでいくつかのアプリケーションを作成し、それを使用して単純なMacRubyアプリケーションをiOSに移植しました。 この記事では、いくつかのUIKit APIを扱っている間に書いた簡単なデモアプリケーションを紹介したいと思います。



このアプリケーションは、Reddit Webサイトのシンプルなクライアントです。 Reddit JSON APIを介して現在の上位の投稿のリストをダウンロードし、データを解析して画面に表形式で表示します。 ユーザーがリスト内のアイテムをタップすると、対応するリンクがモバイルSafariで開きます。



 class RedditPost attr_accessor :title, :url, :author attr_accessor :comments, :score attr_accessor :subreddit def initialize(data) @url = data["url"] @title = data["title"] @author = data["author"] @comments = data["num_comments"] @link = data["permalink"] @score = data["score"] @subreddit = data["subreddit"] end end class RedditController < UITableViewController def viewDidLoad @posts = [] view.dataSource = view.delegate = self refresh "top.json" end def tableView(tv, numberOfRowsInSection:section) @posts.size end def tableView(tv, cellForRowAtIndexPath:indexPath) cid = "PostCell" cell = tv.dequeueReusableCellWithIdentifier(cid) || UITableViewCell.alloc.initWithStyle( UITableViewCellStyleSubtitle, reuseIdentifier:cid) p = @posts[indexPath.row] cell.textLabel.text = p.title cell.detailTextLabel.text = "Posted by #{p.author} in #{p.subreddit}" cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator cell end def tableView(tv, didSelectRowAtIndexPath:indexPath) url = NSURL.URLWithString @posts[indexPath.row].url UIApplication.sharedApplication.openURL url tv.deselectRowAtIndexPath(indexPath, animated:true) end def get(address) err = Pointer.new_with_type "@" url = NSURL.URLWithString address raise "Loading Error: #{err[0].description}" unless data = NSData.alloc.initWithContentsOfURL( url, options:0, error:err) raise "Parsing Error: #{err[0].description}" unless json = NSJSONSerialization.JSONObjectWithData( data, options:0, error:err) json end def refresh(endpoint) Dispatch::Queue.concurrent.async do begin response = get "http://reddit.com/#{endpoint}" data = response["data"]["children"].map {|i| RedditPost.new i["data"] } Dispatch::Queue.main.sync { @posts = data; view.reloadData } rescue Exception => msg puts "Loading Failed: #{msg}" end end end end class AppDelegate def application(app, didFinishLaunchingWithOptions:launchOptions) @win = UIWindow.alloc.initWithFrame( UIScreen.mainScreen.applicationFrame) @win.rootViewController = RedditController.alloc.initWithStyle( UITableViewStylePlain) @win.rootViewController.wantsFullScreenLayout = true @win.makeKeyAndVisible return true end end
      
      







アプリケーション全体は、100行未満のコードを持つ単一の.rbファイルとして設計されています。 AppDelegate



クラスのapplication



メソッドは、アプリケーションの起動時に呼び出されます。 このメソッドを使用して、起動時にユーザーに表示するビューのインスタンスを作成して表示します。



ほとんどすべてのアプリケーションロジックは、 UITableViewController



サブクラスであるRedditController



クラスで実装されUITableViewController



。 クラスがユーザーインターフェイスに読み込まれるときに呼び出されるviewDidLoad



メソッドは、投稿を含むインスタンス変数を設定してから、データを読み込むrefresh



メソッドを呼び出します。



refresh



メソッドはget



呼び出しget



。これはNSData



クラスを使用してJSON形式でデータをダウンロードし、 NSData



を使用して簡単に使用できるデータ構造に変換します。 refresh



方法では、Grand Central Dispatch(GCD)を使用して、バックグラウンドストリームのデータをロードおよび変換することに気づいたかもしれません。



RedditController



クラスのいくつかのtableView



メソッドは、ビューの動作のさまざまな側面を処理します。 tableView:cellForRowAtIndexPath



行オブジェクトを作成してデータを取り込むために使用されます。 ユーザーが表の要素をタップすると、 tableView:didSelectRowAtIndexPath



が呼び出されます。 このメソッドのコードは、選択されたオブジェクトを判別し、ブラウザでこのオブジェクトのURLを開き、オブジェクトから選択を削除します。



おわりに


RubyMotionは、iOSアプリケーション開発者に妥協のないRubyの力と表現を提供します。 iOS用のネイティブアプリケーションを開発するためのより魅力的な方法を想像することは困難です。 RubyMotionの実装は本当に印象的で、基礎となるテクノロジーは成熟しています。



RubyMotionを使用したアプリケーションの開発にしばらく時間を費やした後、唯一の欠点は、ユーザーインターフェイスを作成するためのXcodeとの統合の欠如でした。 UIKitの経験のない開発者にとって、コードを手動で記述することは時間がかかり、複雑になる可能性があります。



この欠陥はRubyで作業する生産性を否定しませんが、新しい開発者はRubyMotionから完全に利益を得る前にトレーニングの初期段階を経なければなりません。 幸いなことに、すぐに来る高レベルのライブラリはこれを修正するはずです。



年間更新サポートを含むRubyMotionライセンスの価格は199.99ドルです。 今では、149.99ドルで割引価格で購入できます。 詳細については、製品の公式Webサイトをご覧ください。 そこで、Pragmaticスタジオのオープニングスクリーンキャストを見ることができます。



この記事は翻訳です。 オリジナルはこちらから入手できます



All Articles