ページ

2011/09/19

勝手/全/超訳 Titanium Mobile Module Developer Guide for iOS


以下はTitanium Mobile Module Developer Guide for iOSの全訳です。
 事前の許可なく勝手に日本語に翻訳しました。画像はAppceleratorのサイトへのリンクとなっています。
 オリジナルのドキュメントがいささか古く、現行のTitanium StudioではなくTitanium Developerを使って説明されているため、最近Titaniumを始められた方は面食らうかもしれません。適宜補って読む必要がありますが、モジュール開発は、基本的にコマンドラインによる開発になるので、十分訳しておく意味があると思われます。
 また、元々のドキュメントの品質が、エディトリアルを経ていない開発者が書いたストレートなものになっているので、若干ブロークンだったり、文意を読み取れない箇所が存在します。訳注等で補いましたが、あまりにもうるさくなりすぎる箇所は、断らずにもとの文を変更して訳している箇所もあります。
 また、Xcodeのメニューなど原文同様英語版のままにしてあります。適宜置き換えてください。
 モジュール開発しようという人たちの助けになれば幸いです。
 また、私の間違い、勘違いなど不備があると思います。お気づきの点がございましたら、ご遠慮なく、コメントしていただけると助かります。
---------------------------

概要

Titanium は、Titanium+Plusモジュールと呼ばれる一連の追加的な拡張を通じて、組み込み済みの機能を拡張する能力を提供します。このガイドではそれらを簡単に「モジュール」と呼びます。
このドキュメントはTitaniumモジュール開発者ガイドシリーズの一部であり、iOSモジュール開発のステップ0からステップ3までをカバーしています。
  • ステップ0は、実際の開発環境をカバーし、iOSベースのモジュール開発の環境の設定について説明します。
  • ステップ1は、Objective-Cを使って最初のモジュールを作成します。
  • ステップ2では、モジールの基盤となるアーキテクチャについて学びます。
  • ステップ3では、作成したモジュールの利用方法と第三者に作配布する方法を学びます。

iOS モジュールの事前条件

iOSベースのモジュールを開発するためには、次の事前条件を満たす必要があります:
  1. Intel-based Macintosh running OSX 10.5 以上
  2. XCode 3.2 以上
  3. iOS 4.0 SDK 以上
  4. Titanium 1.4 Mobile SDK 以上ではiPhone と iPad 用の機能拡張をサポートします. 以下の(ステップ0の)環境の事前条件に加えて、Objective-C 開発についての全般的な知識を持っている必要があります。

ステップ0:モジュール環境の設定

OSX上のモジュール環境を設定するために、パスに関して補助的なスクリプトを設置したり、ファイルへの環境エイリアスの設定が必要となるでしょう。このドキュメントでは、以下のエイリアスを作ることを前提にします。
Titanium 1.4.0 SDK (以上の現行バージョン)をどこにインストールしたか確認する必要があります。それは、/Library/Application\ Support/Titanium にインストールされているかも知れないし、~/Library/Application\ Support/Titanium にインストールされているかもしれません。以下の説明では、 /Library 以下にインストールされたものと仮定します。もし~/Library 以下にインストールされているなら、下の3の該当の箇所を補正する必要があります。
  1. Consoleアプリケーションを開きます。 Console アプリケーションは /Applications/Utilities/Console 以下にあります。
  2. $HOME ディレクトリ以下にある.bash_profile という名前のファイルを編集します。
  3. このファイルのなかに、ファイルの末尾に以下の行を追加します:
    alias titanium='/Library/Application\ Support/Titanium/mobilesdk/osx/1.4.0/titanium.py'
    このスクリプトは titanium.py 補助スクリプトへのエイリアスを作成します。 使用するTitanium mobile SDKのバージョンによって、上にあげたバージョン(1.4.0)を置き換える必要があります。
  4. ファイルを保存します。
  5. コンソールで以下のコマンドをタイプします:
    source \~/.bash_profile
  6. コンソールで以下のコマンドをタイプします:
    titanium
    以下のような出力を得るはずです(バージョンは置き換えてください)
    Appcelerator Titanium
    Copyright (c) 2010 by Appcelerator, Inc.
    commands:create  - create a project
    run              - run an existing project
    help             - get help
  7. 次のステップのためにコンソールは開いたままにしておきます。

ステップ1: 最初のモジュール作成

最初のモジュールをビルドする前に、環境設定が正確であるか、またテストアプリケーションでモジュールが使用できるか確認するために、テストモジュールを作成した方がいいでしょう。
モジュールを作成するためには、コンソールで以下のコマンドを実行する必要です:
> titanium create --platform=iphone --type=module --dir=~/tmp/ --name=test --id=com.test
このコマンドを実行後、次の結果を得るでしょう:
Appcelerator Titanium XCode templates installed
Created iphone module project
これはディレクトリ~/tmp/testに新しいモジュールを作成するでしょう。新しく作成されたモジュールの内容を確認しましょう。
> cd ~/tmp/test
> ls -la
次の内容(とおおむね同じようなもの)が得られているはずです:
total 80
drwxr-xr-x  16 jhaynie  staff 544 Jul 30 15:08 .
drwxr-xr-x@ 254 jhaynie  staff 8636 Jul 30 15:08 ..
-rw-r--r--  1 jhaynie  staff 20 Jul 30 15:08 .gitignore
drwxr-xr-x  7 jhaynie  staff 238 Jul 30 15:08 Classes
-rw-r--r--  1 jhaynie  staff 62 Jul 30 15:08 ComTest_Prefix.pch
-rw-r--r--  1 jhaynie  staff 78 Jul 30 15:08 LICENSE
-rw-r--r--  1 jhaynie  staff 4877 Jul 30 15:08 README
drwxr-xr-x  3 jhaynie  staff 102 Jul 30 15:08 assets
-rwxr-xr-x  1 jhaynie  staff 5490 Jul 30 15:08 build.py
drwxr-xr-x  3 jhaynie  staff 102 Jul 30 15:08 documentation
drwxr-xr-x  3 jhaynie  staff 102 Jul 30 15:08 example
drwxr-xr-x  7 jhaynie  staff 238 Jul 30 15:08 hooks
-rw-r--r--  1 jhaynie  staff 373 Jul 30 15:08 manifest
-rw-r--r--  1 jhaynie  staff 777 Jul 30 15:08 module.xcconfig
drwxr-xr-x  3 jhaynie  staff 102 Jul 30 15:08 test.xcodeproj
-rw-r--r--  1 jhaynie  staff 478 Jul 30 15:08 titanium.xcconfig
ファイルとディレクトリが何のためのものか説明します:
ファイル/ディレクトリ 説明/目的
.gitignore 特別な .gitignore ファイルはリビジョン管理システムgitで使用され、システムに特定の一時ファイルや一時ディレクトリを度外視するように指示する。
このファイルはモジュールとともに配布されない。
 gitを使用しないなら、無視してかまいません。
Classes Objective-C ヘッダや実装クラスがおかれるディレクトリ
これらのファイルはモジュールとともに配布されない。XCodeコンパイラによって使用される。
ComTest_Prefix.pch プリコンパイルヘッダ。これらのファイルはモジュールとともに配布されない。XCodeコンパイラによって使用される。

LICENSE モジュールのライセンステキストの内容として配布するためのファイル。
このファイルはエンドユーザにモジュールとともに配布される。
README モジュールプロジェクトの簡単な説明をするファイル。
このファイルはモジュールとともに配布されない。
assets 画像ファイルなどのモジュールアセットをおくディレクトリ。
build.py このファイルは、利用や配布のためにモジュールをコンパイルしたりパッケージしたりするスクリプトです
documentation このディレクトリはエンドユーザ向けのモジュールのドキュメントを格納する場所です。index.mdファイルは、モジュールドキュメンテーションを書くときのMarkdownフォーッマットされたテンプレートファイルです。これはTitanium+Plus Market-placeで配布するモジュールでのみ必須となります。モジュールを配布ない場合には、無視しても大丈夫です。
example このディレクトリは、モジュールのサンプルを入れておく場所です。生成されたapp.jsファイルは、テストウインドウでモジュールをロードするサンプルを含んでいます。このファイルは、モジュールのクイックテストに使われるばかりでなく、どのようにモジュールを使用するかエンドユーザに伝えます。このディレクトリはモジュールとともに配布されます。
hooks このディレクトリは現在は無視されます。将来の利用のために予約されています。
manifest この特別なファイルはモジュールのメタデータを記述して、Titaniumコンパイラが利用します。
このファイルは必須で、モジュールとともに配布されます。
module.xcconfig この特別なファイルは、モジュールを参照するエンドユーザのTitaniumアプリケーション内にモジュールをコンパイルするときXcodeによって使用されます。
このファイルはXcode設定ファイルで、モジュールに固有のコンパイラやリンカの特別な記述子をセットアップします。このファイルはモジュールとともに配布されます。


test.xcodeproj このディレクトリはXcodeプロジェクトを含みます。このディレクトリはモジュールとともに配布されません。

titanium.xcconfig この特別なファイルはモジュールをビルドする際にコンパイル時にXcodeによって使用される。このファイルはモジュールに固有のコンパイラやリンカの特別な記述子をセットアップするXcode設定ファイルです。
このファイルは配布されません。モジュール開発時に使用されるのみです。


XcodeでTestModuleを開く

このステップでは、Xcodeで新規に作成したTestModuleを開くことにします。
Xcodeで test.xcodeproj を開きます。プロジェクトを開くには、コンソールから次のようにタイプします:
> open test.xcodeproj
これで、(既に起動されていなければ)Xcodeが起動して、テストプロジェクトが開きます。次のようなウインドウが現れます(Xcode 3の場合。Xcode 4では新しいUIのウインドウの外観になります)

メインモジュールファイルは ComTestModule.h (ヘッダ) と ComTestModule.m (実装)で定義されています。
モジュールをパッケージするとき、システムで生成された ComTestModuleAssets.h (ヘッダ) と ComTestModuleAssets.m (実装) を無視すべきです。これらのファイルを変更しても、モジュールをビルドするたびにシステムによって上書きされます。
この時点で、ビルドメニューのビルドコマンドでXcode内でモジュールをビルドできるはずです。
ビルドが成功すると、モジュールをパケージしたり、実際のTitaniumアプリケーションでテストしたりする準備ができています。

モジュールのパッケージング

モジュールをパッケージするためには、コンソールから build.py スクリプトを実行します。

> ./build.py
このスクリプトはコンソールに対して一連のコンパイラ命令を標準出力に送り、数秒でビルドとパケージを行います。一旦ビルドが成功すれば、出力の最後に次の行が現れる:

> ** BUILD SUCCEEDED **
4ページの注意と同様、Titanium SDKが/Libraryではなく~/Library にインストールされているかもしれません。 また、titanium.xcconfig ファイルは正しいディレクトリを指していなければなりません。さもないとビルドは失敗するでしょう
(訳注:元々この文書がPDFファイルで配布されていた当時の名残で「4ページ」といっている)
MacにプレインストールされているPythonインタープリタ用のデフォルトのPythonPath上にはmarkdown2モジュールが存在しません。インストールされていないとモジュールドキュメントの生成時にbuild.pyが失敗します。まだインストールしていなければ、以下の手順でmarkdown2をインストールしてください:
  1. 最新版をhttps://github.com/trentm/python-markdown2からダウンロード
  2. ダウンロードしたものをUnzip
  3. ダウンロードしたzipファイルを解凍したディレクトリにあるsetup.py スクリプトを使って`sudo python setup.py install`でインストールします
  4. build.pyを再実行
カレントディレクトリ(~/tmp/test)にcom.test-iphone-0.1.zipという名前のファイルができます。このファイルが見つからなければ、モジュールはビルドもパッケージングも正しくできていません。

モジュールのテスト

モジュールをテストするには、単にモジュールテストハーネスを実行するだけです。モジュールテストハーネスは一時的プロジェクトを生成して、exampleディレクトリの(既に生成されている) app.jsファイルをコピーして、モジュールをロードします。カレントディレクトリ(~/tmp/test)からコンソールで以下を実行することで、テストを実施できます:
> titanium run
これは、コンソールに対して標準出力に一連のログを送信させて、数十秒でビルド、パッケージしてiPhoneシミュレータを起動します。
成功すれば、iPhoneシミュレータと白いスクリーンが現れます。(訳注:現行のバージョンでは、確認用のテキストが表示されるようになっています)

コンソール出力の最後の方に以下のものが現れます
[INFO] [object ComTestModule] loaded
[DEBUG] loading: /var/folders/4n/4nhl7xlPFsaSrlAGHhPwg++++TI/-Tmp-/mNdElWkti/test/Resources/com.test.js, resource: var/folders/4n/4nhl7xlPFsaSrlAGHhPwg++++TI/-Tmp-/mNdElWkti/test/Resources/com_test_js
[INFO] module is => [object ComTestModule]
[DEBUG] application booted in 82.601011 ms
確認すべき2つの重要なステートメントがあります:
[INFO] [object ComTestModule] loaded
[INFO] module is => [object ComTestModule]
1番目のステートメントは、ファイルComTestModule.mの中の最初で名付けたメソッドをみれば、見つけられます
-(void)startup
{
  // this method is called when the module is first loaded
  // you *must* call the superclass
  [super startup];
  NSLog(@"[INFO] %@ loaded",self);
}
このメソッドについて後のセクションでより詳しく説明します。2番目のステートメントはexampleディレクトリのファイルapp.jsを見れば、見つかります。
var test = require('com.test');
Ti.API.info("module is => "+test);
JavaScriptからモジュールが読み込まれる時、上記のように require('module_id')コマンドを使ってモジュールを要求してインポートする必要があります。

テストアプリケーションでモジュールをテストする

さて、テストアプリケーションでモジュールをテストしなければなりません。
実際のアプリケーションでモジュールをテストする前に、モジュールをインストールしなければなりません。前のステップで既にモジュールをパッケージしていますから、モジュールのビルドステップは省略できます。しかし、モジュールのコードを変更したときには、ビルドとインストールのステップを繰り返さなければならないことを覚えておいてください。
実際のアプリケーションでモジュールを使用するにはモジュールをインストールしなければなりません。コンソールで以下のコマンドを実行します:
> cp com.test-iphone-0.1.zip /Library/Application\ Support/Titanium/
このコマンドはモジュール配布ファイルをTitaniumディレクトリにコピーします。Titaniumコンパイラは賢いので、アプリケーションが参照するとき自動的にモジュールを正しいディレクトリに展開します。(訳注:実際にはアプリケーション用のbuild.pyが実行されたときに展開されます)
モジュールは以下のディレクトリに展開されます:
/Library/Application Support/Titanium/modules/iphone/com.test/0.1
このディレクトリには、同名のモジュールの複数バージョンが同時に共存することが可能です。
4ページの注意と同様、Titanium SDKが/Libraryの代わりに~/Libraryにインストールされていれば、上記のパスを修正することが必要です。
次にTitanium Developerでテストアプリケーションを作成します。(訳注:現在はTitanium StudioだけがAppcelaratorから供給されています)

アプリケーションのプロジェクトが作成されたあと、テストアプリケーションのフォルダの中を探して、テキストエディタでtiapp.xmlという名前のファイルを編集してください。(訳注:現在ではTitanium Studio内で編集できます)
このファイルの最後の方に以下の行を追加する必要があります:
<modules>
  <module version="0.1">com.test</module>
</modules>
これはアプリケーションが一つ以上のモジュールを必要としていることやどのバージョンを必要としているかをTitaniumコンパイラに教える上で重要なステップです。このテストの場合、com.testモジュールでデフォルトのバーション0.1を参照する必要があります。(訳注:「デフォルトのバージョン」という言い回しには疑問があります。指定している限り「デフォルト」とはいえないからです。モジュールを新規作成する場合のバージョン番号が0.1になるという意味でなら「デフォルト」といえなくもありませんが、この箇所でそれを持ち出すと混乱を生じます。またplatform属性に関しての記述が欠けています。)
最終的な XML file は以下のようになります:
xml version="1.0" encoding="UTF-8"?>
<ti:app xmlns:ti="http://ti.appcelerator.org">
  <id>com.test</id>
  <name>testapp</name>
  <version>1.0</version>
  <publisher>not specified</publisher>
  <url>not specified</url>
  <description>not specified</description>
  <copyright>not specified</copyright>
  <icon>appicon.png</icon>
  <persistent-wifi>false</persistent-wifi>
  <prerendered-icon>false</prerendered-icon>
  <statusbar-style>default</statusbar-style>
  <statusbar-hidden>false</statusbar-hidden>
  <fullscreen>false</fullscreen>
  <navbar-hidden>false</navbar-hidden>
  <analytics>true</analytics>
  <guid>c347a9a1-44bd-48c8-a6bf-7762f2582f50</guid>
  <modules>
    <module version="0.1">com.test</module>
  </modules>
</ti:app>
guidの値は、グローバルに一意なIDで、その都度生成されるので、上の例とは異なっているでしょう。
さて、モジュールをロードするためにapp.jsを編集する必要があります。テストアプリケーションのResourcesディレクトリ内のapp.jsを編集しましょう。ファイルの最後に次の行を追加してください。:
var test = require('com.test');
Ti.API.info("module is => "+test);
シミュレータ内でアプリケーションを起動すると、正常なテストアプリケーションが現れるでしょう:(訳注:いきなりiPhoneシミュレータから起動するかのように記述されていますが、もちろんTitanium Developerなり、Titanium Studioからシミュレータ実行するという意味です)

Titanium Developerコンソールはモジュールがロードされたことを確認できる重要なログステートメントを出力するはずです:
Detected third-party module: com.test/0.1
このステートメントは、tiapp.xmlファイルで参照されているモジュールごとに出力されるはずです。
また、app.jsに追加したコードに基づいて以下のものが現れるはずです:
[INFO] [object ComTestModule] loaded
[INFO] module is => [object ComTestModule]
このステートメントが現れていて、エラーがログコンソールにないなら、モジュールはロードされ作動していることになります。アプリケーションは準備完了という訳です。

ステップ2:モジュールアーキテクチャの基礎

Titaniumプラットフォームはモジュールアーキテクチャに基礎をおいています。それに基づいて、このドキュメントで述べているサードパーティのモジュールを作ることが可能な一方で、Titanium自身も、組み込みAPIのために内部的に同一のモジュールSDKを使用しています。
モジュールアーキテクチャは以下の主要なインターフェースコンポーネントを含みます:
Proxy JacaScriptコードとネイティブコードとの間のつながりを表現する基底クラス.
ViewProxy Viewをレンダーする方法を知っている特殊なProxy.
View TitaniumがレンダーするUIコンポーネントのビジュアル表現.
Module 特殊なAPIあるいは名前空間を記述する特殊なタイプのProxy.
モジュールを作成する際、Moduleクラスをただ1つだけ持つことができますが、Proxy、View、ViewProxyは 0個以上持つことができます。
おのおののViewに対して1つのViewProxyが必要です。
ViewProxy は、(Proxy自身の中でViewが解放される必要がある場合でも保持される)モデルデータを表し、ViewがサポートするAPIやイベントを公開する責任があります。
JavaScriptとネイティブコードとの間で非ビジュアルなデータをやり取りする必要があるときにProxyが必要です。Proxyはメソッドやプロパティのディスパッチやイベントの起動をどう行うかを知るものです。

Proxy

Proxyを作成するには, インターフェース宣言でTiProxyを継承しなければなりません。importステートメントの中に"TiProxy.h"をインポートしてください。例えば、インターフェースは次のようになるでしょう:
#import "TiProxy.h"
@interface FooProxy : TiProxy
{
}
@end
実装部は次のようになるでしょう:
#import "FooProxy.h"
@implementation FooProxy
@end
規約としてTitaniumはProxyクラスを示すためにProxy接尾辞を必須とします。

Proxy メソッド

Proxiy は、標準的なObjective-C構文を使ってメソッドとプロパティを公開します。メソッドを公開するには、Proxyは以下のような正当なシグニチャのうち1つを持っていなければなりません:
-(id)methodName:(id)args
{
}
このシグニチャは呼び出し元へ値を返すときに使用されます。戻り値は正当なNSObjectやnil、Proxy(あるいはそのサブクラス)を使うことができます。
-(void)methodName:(id)args
{
}
このシグニチャは値を返さない場合に使用すべきです。
どちらのシグニチャの場合も、1つの引数を持ち、その型はNSArrayです。しかし、一般にidという宣言を使うことを推奨します。以下で述べるように入ってくる値を別の値にタイプキャストするのを容易にするでしょう。(訳注:「推奨する」といっていますが、実際にはほかの選択肢はありません。ほかのシグニチャを使った場合Titanumはメソッドと認識しません。)
もし関数が引数を持たない場合には、入ってくるargsの値を単に無視してかまいません。(訳注:「関数」といっていますが、メソッドのことです。モジュールの作成過程で、既存の関数からメソッドを作成することを想定してこういう表現になっていると思われます)
Titaniumは、NSArrayから入ってくる値から特定の型へタイプキャストしたり、値を抽出する便利なマクロを提供します。(訳注:原文はarrayとなっていますが、文意から明らかにNSArrayのことです)
ENSURE_SINGLE_ITEM(args,type)
  • 第一引数は(メソッドの)引数です (訳注:parameterとargumentの使い分けが混乱/間違っているような気がします。どちらも「引数」と訳しておきます。適当に括弧書きで「メソッドの」などを追加して区別します。)
  • 第2引数は値がとるべき型の名前です
このマクロは2つの作用があります。:
  1. 最初の(メソッドの)引数を抽出する (つまり[args objectAtIndex:0])
  2. 第2引数で渡された型へ1.の戻り値をキャストする
このマクロは単一引数のメソッドにだけ使うことができます。複数の引数を持つなら、単純に普通の配列アクセスメソッドを使うべきです。
ENSURE_UI_THREAD_0
このマクロは、現在のメソッドが(メインスレッドである)UIスレッドでのみ実行されるようにするために使用されます。メソッドがメインスレッド以外で呼び出された場合、単にUIスレッド上のメソッドに再度キューされます。このメソッド(訳注:おそらくマクロの間違い。以下断りなく文意により変更して訳す)は[NSThread performSelectorOnMainThread]と同等です。
このマクロは戻り値を持たない場合にだけ使用できます。
ENSURE_UI_THREAD_1(arg)
このマクロは現在のメソッドが引数を持つ場合、UIスレッドでのみ実行されるようにするために使用されます。引数を渡されるようにするということ以外前項のマクロと同じです。
Titaiumは特定の値を変換したり検証したりするための便利なライブラリを提供します。このライブラリはTiUtilsの中にあり、次のようにしてインポートすることができます:
#import "TiUtils.h"
いくつかの一般的な変換ユーティリティの例を挙げます:
CGFloat f = [TiUtils floatValue:arg];
NSInteger f = [TiUtils intValue:arg];
NSString *value = [TiUtils stringValue:arg];
NSString *value = [TiUtils stringValue:@"key" properties:dict def:@"default"];
TiColor *bgcolor = [TiUtils colorValue:arg];

Proxy プロパティ

ProxyでJavaScriptプロパティを公開するには、単にObjective-Cのプロパティを使用します。
@property(nonatomic,readwrite,assign) NSString* propertyName;
オプションとしてgetter,setterプロパティを定義できます。
-(void)setPropertyName:(id)value
{
}
-(id)propertyName
{
  return @"foo";
}
setterメソッドでは、Titaniumは(メソッドの場合のようにNSArrayではなくて)単一の値を第一引数として変換された値で渡します。
getterメソッドでは、プロパティメソッドはNSObject, nilあるいはTiProxy(あるいはサブクラス)のいずれかとして値を返さなければなりません。

戻り値になるオブジェクト

以下のObjective-Cの型が変換なしに戻り値として使用できます:
NSString NSDictionary NSArray
NSNumber NSDate NSNull

戻り値になるプリミティブな値

メソッドやプロパティからプリミチィブな値を返すためには、プリミティブな値を適切にラップされたプリミティブな値をもつNSNumberを返すべきです。Titaniumはこれを簡単にするためのマクロ群を提供します:
Macro Description
NUMINT Equivalent to [NSNumber numberWithInt:value]
NUMBOOL Equivalent to [NSNumber numberWithInt:value]
NUMLONG Equivalent to [NSNumber numberWithLong:value]
NUMLONGLONG Equivalent to [NSNumber numberWithLongLong:value]
NUMDOUBLE Equivalent to [NSNumber numberWithDouble:value]
NUMFLOAT Equivalent to [NSNumber numberWithFloat:value]

戻り値としての複合的な値

複合的な値を返すには2つのアプローチがあります:
  • 第一のアプローチは、NSDictionaryに値をセットしてNSDictionaryを返すことです。NSDictionaryが返されるとTitaniumはこれをJavaScriptのオブジェクトに変換します。マップされたkey/valueは、JavaScriptのproperty/valueオブジェクトになります。
  • 第二のアプローチは特別なProxyを作成することです。Proxyは関数とプロパティを持ったJavaScriptオブジェクトとして公開されるものとして返されます。返されたProxyに対する呼び出しは返されたProxyインスタンスに対する呼び出しになります。Proxyインスタンスを返す際、メソッドの中で作成したならautoreleaseにしておかなくてはなりません。

戻り値としてのファイル

ファイルシステムのファイルへの参照を返すには、TiFile プロキシのインスタンスを返すべきです。これはネイティブファイルとそのメッソッドとプロパティの公開を自動的に受け持ちます。ファイルを返すには、次の例のようにすることができます:
return [[[TiFile alloc] initWithPath:path] autorelease];

戻り値としてのBlob

(NSDataのような)blobデータへの参照を返すには、TiBlobのインスタンスを返すべきです。これは、いくつかのネイティブなblob操作の公開を自動的に受け持ちます。blobを返すには、次の例のようにできます:
return [[[TiBlob alloc] initWithData:nsdata mimetype:@"application/octet-stream"] autorelease];
return [[[TiBlob alloc] initWithImage:image] autorelease];
return [[[TiBlob alloc] initWithFile:file] autorelease];
上の最初の例は、第2引数はローデータコンテントのMIMEタイプへのマップしています。もしデータがバイナリデータなら、"application/octet-stream"という値を使用できます。

戻り値としてのCGRect

CGRectを返すために, Titanium はTiRectという名前のプロキシを提供します。次の例のようにできます:
TiRect *result = [[[TiRect alloc] init] autorelease];
[result setRect:rect];
return result;

戻り値としてのCGPoint

CGPointを返すために、Titanium はTiRectという名前のプロキシを提供します。次の例のようにできます:
return [[[TiPoint alloc] initWithPoint:point] autorelease];

戻り値としてのNSRange

NSRangeを返すために、Titanium はTiRangeという名前のプロキシを提供します。次の例のようにできます:
return [[[TiRange alloc] initWithRange:range] autorelease];

戻り値としてのUIColor

UIColorを返すために、Titanium はTiColorという名前のプロキシを提供します。次の例のようにできます:
return [[[TiColor alloc] initWithColor:color name:@"#fff"] autorelease];
第2引数(name)は(つけるのであれば)UIColorのテキスト名でなければなりません。

Proxyの値の設定

JavaScript側でプロキシを作成する場合、 通常は、オプションでkey/value ペアのディクショナリを渡します。このパターンを使って、Titaniumは、プロキシのプログラミングを容易にする組み込みの機能を提供します。
簡単な例を使ってみましょう:
var my_module = require('com.test');
var foo = my_module.createFoo({
  "bar":"123"
});
Titanium は、上記のようなプロキシファクトリつまりcreateメソッドを、自動的に正しく定義しディスパッチします。必要なのは単にモジュールと同じ名前のProxyを定義し、メソッド名にProxyの後置辞を加えることです。(訳注:つまりJavaScript上での名前は メソッド名create + モジュールのProxy名Foo で,Proxyファクトリ createFoo を表現すします)
ComTestFooProxy.h
ComTestFooProxy.m
モジュールではcreateFoo メソッドを定義する必要はありません。この規約に従うことで、Titaniumは既にモジュールがこれをどう行うべきか知ることになります。
プロキシにおいて、barプロパティを扱うためにsetterメソッドを定義したいくなるかもしれません。(initでの)構築において、Titaniumは自動的にコンストラクタに渡されるすべてのプロパティに対するsetterを呼び出す。
#import "ComTestFooProxy.h"#import "TiUtils.h"
@implementation ComTestFooProxy
-(void)setBar:(id)value
{
   NSString *str = [TiUtils stringValue:value];
}
@end
上の例では、setterを単純に定義して、TiUtilesを使って値をstringValudeに変換しています。このユーテリティを使うことで、文字列表示できるどんな型の値でも引数へ渡せるようになります。それゆえ、ユーザが数値123を渡したとすると、value @"123"を伴ったNSStringを返すでしょう。
また、プロキシは、そのプロパティを扱ったり保持したりする仕方に関して特殊になります。プロキシは常に、'dynprops'と呼ばれる特別な内部のNSDictionaryに渡された値を保存します。これは、プロパティごとにgetter/setterを定義することなしにプロキシの値を扱うために次の方法を使うことができることを意味します:
id value = [self valueForUndefinedKey:@"bar"];
valueForUndefinedKeyを使うなら、元々のプロパティの値を扱うことになるだろう。しかし、(JavaScript内での元々のプロパティの値を返す、あるいは、返さない)記述可能なgetterを呼びたいなら、以下のようにすべきです:
id value = [self valueForKey:@"bar"];
上記のコード例では、次のようにメソッソが定義してあると、内部の元々のプロパティを扱わずに、このメソッドが呼ばれます。
-(id)bar
{
  return @"456";
}
プロパティは内部で設定されるためにそれらのコンストラクタに渡されずに、自前のsetterが呼び出されます。
foo.bar = 789;
プロキシのプロパティが呼び出される際に、以下のことが起こります:
  • 定義済みのsetterがあれば、それが呼ばれる。
  • setterの定義がなければ、プロパティと値はdynpropsに内部的に保存されるだろう。
setterを実装するのであれば、自分のプロパティを独自にdynpropsに保存されなければならない。以下のメソッドを呼ぶことで可能です:
-(void)setBar:(id)value
{
  [self replaceValue:value forKey:@"bar" notification:NO];
}
3番目の引数(notification)は、Titaniumに、setterがこのプロパティの変更から呼び出されるかどうか知らせています。 setterの内部に既にいるので、無限の再帰を避けるためにNOを渡します。

イベント処理

プロキシは自動的にイベント発行とイベントリスナの管理を処理します。内部的にJavaScriptからプロキシのインスタンスに対してaddEventListener や removeEventListenerが呼ばれると、プロキシは自動的にイベントリスナを管理するコードを処理します。
追加や削除時に通知が必要な場合には、メソッドをオーバーライドできる:
-(void)_listenerAdded:(NSString*)type count:(int)count
{
}
-(void)_listenerRemoved:(NSString*)type count:(int)count
{
}
_listenerAdded メソッドはイベント名(type)と(新規のイベントリスナも含めた)同じタイプの存在するリスナの総数を引数として呼び出されるでしょう。例えば、イベントを受信する少なくとも一つのリスナに一度だけ何らかのアクションをさせたい場合に便利である。これはシステムリソースの節約の役に立ちます。
_listenerRemoved メソッドはイベント名(type)と(削除されたリスナを除いた)同じタイプの残ったリスナの総数を引数として呼び出されるだろう。これは、実際にイベントを受信しているリスナがない場合にシステムリソースをクリーンナップするのに有用でしょう。イベントリスナにイベントを送るために、便利なメソッドを使うことができます:
-(void)fireEvent:(NSString*)type withObject:(id)obj;
fireEventメソッドはイベントを発行するデフォルトのもっとも普通の方法です(訳注:原文の主語が間違っていると思われるので、修正)。第一引数(type)はイベント名です。第2引数 (obj) はイベントプロパティの NSDictionary です。第2引数は、追加のイベントプロパティが不要なら、nilにすることもできます。イベントプロパティはすべてのイベント関数の第1引数となるイベント引数の要素となります。
例:
-(void)mymethod{NSDictionary *event = [NSDictionary dictionaryWithObjectAndKeys:@"foo",@"name",nil];
[self fireEvent:@"foo" withObject:event];}
この例では、値'foo'を伴った'name'という名前の追加のイベントプロパティを一つ追加しています。JavaScript ではこれは、次のように扱われます:
foo.addEventListener('foo',function(e){
  alert("name is "+e.name);
});
追加のイベント引数を渡すのに加えて、Titaniumは自動的にイベント発行時に次の組み込みプロパティを提供します:
  • source — イベントを発行する送信元オブジェクト(プロキシ)
  • type — イベントタイプ
また、イベントを発行する前にリスナの存在をチェックすることができます(推奨されます)。これは_hasListenersメソッドで行うことができる。
(void)fireMyEvent
{if ([self _hasListeners:@"foo"])
{
// fire event
}
}
リスナに処理能力を節約させるたいなら、イベントオブジェクト構築しイベントを発行することは、一般に推奨されます。(訳注:文意不明。無駄なイベントオブジェクトを生成したり、イベント発行したりすると、処理能力が低下するといいたいのだろうか?)

メモリ管理

Proxyは、Objective-Cクラスと同じように働き、すべてのメモリ管理規則が考慮されなければなりません。メソッドから新規のプロキシのインスタンスを返すとき、インスタンスをautoreleaseにしなければなりません。Titaniumは、結果となるJavaScriptの変数が参照への参照へマップするプロキシへの参照をretainします。しかしJavaScript変数はもはや参照可能でない場合、それは解放され、プロキシはdeallocメッセージを受け取ります。
Titaniumでは、オブジェクトのretain/releaseには特に注意を払わなければならないのは、Objective-Cベースのプログラムと同じです。不適切なretain/releaseはクラッシュを引き起こしたり、望まない結果を生じさせます。

View Proxy

ViewProxyはProxyの特殊化でありViewのために使用されます。Viewとはスクリーン上に描画するUIとインタラクトするオブジェクトです。
View Proxy はデータ (model)を保持し、viewに対してプロパティ変更やメソッドをディスパッチするcontrollerのように働きます。Viewは、UIKitとインタラクトしたりUIKitイベントを扱ったりするための内部的なロジックを扱います。ViewはView Proxyへのモデル委譲です。そして、参照されている限りは、プロパティ変更を受信します。
View Proxyは、常にViewへの正当な参照を保有しているとは限りません。Viewは、必要なときに要求に応じてだけ生成されます。Viewが生成されView Proxyによって保有された場合、View Proxyへのすべてのプロパティ変更イベントは、Viewへと先渡しされます。
Viewのプロパティメソッドは特殊な必須の規約を使って名付けられます。
-(void)setFoo_:(id)value
{
}
プロパティ名の後置辞はアンダースコアで終わらなければなりません。Viewプロパティメソッドの場合、Viewへの呼び出しは常にUIスレッド上にあるでしょう。
実際のViewで処理されるべきView Proxyにプロパティを持つ場合、Viewでそれらを定義する必要はありません。そうせずに、Viewの中で上記の構文を使ってそれらを定義することもできます。そうすると自動的にディスパッチされます。プロパティがセットされた後でViewにアタッチされたイベントは、View Proxy が自動的にすべてのプロパティ変更イベント(これはおのおののsetterメソッド呼び出しの結果です)を構築時のViewへ先送りします。
ところで、Viewは、View自身がViewに処理させたいメソッドを持たねばなりませんし、ディスパッチしなければなりません(訳注:itがなんなの不明。Viewとして訳す。この辺りの記述は意味不明。全体の話のはがれでは、プロパティ以外のメソッドに話が移ります)。次の例のコードでこのことが正常に処理できます:
(void)show:(id)args
{
  [[self view] performSelectorOnMainThread:@selector(show:)
    withObject:args waitUntilDone:NO];
}
同等なことをする便利なマクロを使うことができます:
USE_VIEW_FOR_UI_METHOD(methodName)
次のコード例は上のshowメソッドと同じものです:
USE_VIEW_FOR_UI_METHOD(show)
View Proxyのviewへの生成された参照を得るには、メソッド'view'を呼んで、自分のViewクラスにviewの結果をキャストすることができます。(訳注:説明が不足していると思われる。)

View

View の実装はTiUIView クラスを拡張しなければなりません。TiUIView クラスはUIViewを拡張して、Titanium固有の機能を提供します。
新しいViewを定義します。例えば:
#import "TiUIView.h"@interface ComTestFooView : TiUIView
{
}
@end
View の実装を定義します。例えば:
#import "ComTestFooView.h"
@implementation ComTestFooView
@end
View はView Proxyによって必要とされるUIView階層へアタッチされます。しかし、もしサブビューがあるなら、必要に応じてサブビューを自分自身(TiUIView)にアタッチする必要があります。これは通常は、viewをインスタンス変数に代入して、それへの参照を保持することで行われますが、参照がnilの場合にだけは生成してアタッチしなければなりません。(訳注:説明が不足していると思われる。)

ViewとView Proxyの作成

Viewを作成するには、Xcodeでメニュー File->New File... を選択して、現れた次のファイル選択ダイアログを選びます:

左側のウインドウペインにあるiPhone OS 配下にAppcelerator itemが現れるはずです。右側のウインドウペインのおのおののアイコンが、Appcelerator固有のテンプレートから素早く生成できる様々なファイルタイプを提供しています。"TiUIView" を選んで "Next" ボタンをクリックします。
次のダイアログで、ファイル名 "ComTestView.m" を入力し、プロジェクトディレクトリと "Classes" サブディレクトリを選択します。"Choose..." ボタンを使ってファイル選択できます。

この時点で、Xcodeプロジェクトに2つのファイルができているはずです。

おそらく、独自の事情で、デフォルトの著作権常用文を変更したいでしょう(訳注:生成されたソースコードの先頭のAppceleratorのコピーライト表示を変更してもかまわないといいたいと思われます) 。この時点でコードに有用なこをとさせる準備は整っています。
この例ではcolorプロパティがセットされた子サブビューとして矩形がアタッチされただけのViewを作成します。ComTestView.hで次のコードを使います:
#import "TiUIView.h"
@interface ComTestView : TiUIView
{
  UIView *square;
}
@end
ComTestView.m に次の変更を加えます:
#import "ComTestView.h"
#import "TiUtils.h"
@implementation ComTestView
-(void)dealloc
{
  RELEASE_TO_NIL(square);
  [super dealloc];
}
-(UIView*)square
{
  if (square==nil)
  {
    square = [[UIView alloc] initWithFrame:[self frame]];
    [self addSubview:square];
  }
  return square;
}
-(void)frameSizeChanged:(CGRect)frame bounds:(CGRect)bounds
{
  if (square!=nil)
  {
    [TiUtils setView:square positionRect:bounds];
  }
}
-(void)setColor_:(id)color
{
  UIColor *c = [[TiUtils colorValue:color] _color];
  UIView *s = [self square];
  s.backgroundColor = c;
}
@end
例のビジュアルな部分を表現するViewクラスを作成しました。次はView Proxyを作成します。それはJavaScriptをネイティブコードと繋ぐ方法を知っています。メニュー "File->New File..."を再び選択して "TiViewProxy"を作成します。
これは簡単な例なので、今はテンプレートのコードをあるがままにしておきます。
新しいViewをテストするためには、JavaScript でアプリケーションロジックを書く必要があります。簡単なViewをテストするので、titaniumコマンドラインツールを使って、素早くテストすることができます。以下のJavaScriptコードをプロジェクトのexampleディレクトリにあるapp.jsファイルに追加します:
var my_module = require('com.test');
var foo = my_module.createView({
  "color":"red",
  "width":20,
  "height":20
});
window.add(foo);
このコードはモジュールをロードしてView Proxy(とView)を作成し、Viewを20x20の赤い矩形としてウインドウにアタッチします。
これを実行するためには、コンソールでモジュールプロジェクトディレクトリから次のコマンドをタイプするだけです:
> titanium run
プロジェクトがコンパイルされ、パッケージされ、テストアプリケーションがビルドされ、実行される間多くのコンソールログが表示されるでしょう。だいたい30秒後に次のように表示されるはずです:

テストコートについての注:
  • TitaniumのViewにおいて、通常推奨されるのは、(この例での"square"にあたる)クラスメンバ変数として、メインのUIViewへの参照を保持しておき、Viewをロードし、必要に応じて自身(TiUIView)にViewがアタッチすることです。この例ではプロパティがセットされたときにだけViewにアタッチされています。
  • "frameSizeChanged:bounds:"という特別のメソッドを実装しなければなりません。このメソッドはTitaniumの中でframe/bounds/centerが変更されるたびに呼ばれます。Titaniumは特別なレイアウトエンジンを持っており、このメソッドを使ってViewのframeやboundsに変更を通知すべきです。"setBounds:", "setFrame:" や "setCenter:"をオーバーライドしてはいけません。これらのメソッドをオーバーライド場合には、TiUtilesヘルパークラスに対して"setView:positionRect:"という特別なメソッドを呼ばなければなりません。このメソッドはViewの新しいboundsの正しい境界内に子ビューを正しくレイアウトするでしょう。
  • 生成のためのViewの名前パターン(モジュールに対するメソッド"createView")を使っているので、モジュール内でこのメソッドを定義する必要はありません。Titaniumは自動的に名前規約を使ってこれを実行します。しかし、そうせずに、もし"createFooView"という名前のメソッドを作成したいなら、モジュール内でこのメソッドを定義して、Proxyを直接生成する必要があります。

特殊なコンパイラ記述子の追加

(訳注:以下の記述で、モジュールのプロジェクトとテストアプリケーションのプロジェクトが出てきますが、記述がいささか混線しています。注意深く読む必要があります
モジュールが特別なFrameworkやその他のシステムライブラリや特殊なコンパイラ記述子を必要としているなら、モジュールのxcconfig ファイルを使ってそれらを定義できます。Titaniumは自動的にmodule.xcconfig というファイルとtitanium.xcconfigとを生成します。titanium.xcconfig はパッケージするためにコンパイル時に使用されます。一方、module.xcconfig は、アプリケーションビルド時に、モジュールが参照されていれば、アプリケーションのコンパイラによって使用されます。これは、この過程(訳注:テストアプリケーションの作成過程)でもコンパイラ記述子をコントロールできるようにします。
このことを示すために、簡単な例を見てましょう。まず、Frameworkを追加してみましょう。
まず、XcodeでFrameworkを追加する必要があるでしょう。この例では、GameKitを追加します。プロジェクトフォルダ内でFrameworksを選択し、右クリックして、"Add -> Existing Frameworks" を選びます。

これで次のダイアログが現れるはずです:

"GameKit.framework"を選択して"Add" ボタンをクリックします。Frameworkフォルダ内に新しいFrameworkの参照が現れているはずです:

今の状態では、プロジェクトをコンパイルすると、エラーとなります。
プロジェクトにFrameworkを追加したので、コンパイル時にアプリケーションコンパイラがこのFrameworkをインポートできるようにセットアップする必要があります。これをするには、module.xcconfig を編集して、最後の方に以下の行を追加します:
OTHER_LDFLAGS=$(inherited) -framework GameKit
これが正しく動くことを示すために、GameKitを実際に使った2つのメソッドを実際に定義しましょう。この例ではGameKitセッションを生成して、UIピアチューザーを表示しようと思います。
アプリケーションコードから始めましょう。Titaniumではダイアログからの値の選択をサポートするAPIが設計されているので、通常推奨されるのは、"success", "error" と "cancel"というキーに値としてコールバック関数をオブジェクトに渡すことです。
コードでは以下のようになります:
my_module.start('Jeff');
  my_module.showChooser({
      success:function(e)
    {
      alert("peer choosen: "+e.peer);
    },
      cancel:function(e){
      alert("cancelled");
    }
  });
1番目の関数 (start) は単にGameKit セッションを、私の表示名("Jeff")で開始します。2番目の関数(showChooser)はGameKit ピアチューザーUIダイアログを表示します。"success" と "cancel"という2つのコールバック関数を渡します。ピアが選択されたときに "success"がよばれ、選択ダイアログでキャンセルされたときに "cancel"が呼ばれることを意図しています。さらに初期化エラー等々が起こった場合のために"error" コールバックを定義したいと思います。
今度は、ネイティブコードへの追加を見てみましょう。 ComTestModule.m と ComTestModule.h ファイルで上記のことが行われます。既に定義されたメッソッドの後で"@end"ステートメントの前に 以下のコードをペースとしてください:
#pragma mark Internal
-(void)dealloc
{
  if (session!=nil)
  {
    [session disconnectFromAllPeers];
    RELEASE_TO_NIL(session);
  }
  RELEASE_TO_NIL(peer);
  if (controller!=nil)
  {
    [controller performSelectorOnMainThread:@selector(dismiss) withObject:nil waitUntilDone:NO];
    RELEASE_TO_NIL(controller);
  }
  [super dealloc];
}
#pragma Public APIs
-(void)start:(id)name
{
  ENSURE_SINGLE_ARG(name,NSString);
  NSString *sessionid = [TiUtils createUUID];
  [[GKSession alloc] initWithSessionID:sessionid
  displayName:name
  sessionMode:GKSessionModePeer];
}
-(void)stop:(id)args
{
  if (session!=nil)
  {
    [session disconnectFromAllPeers];
    RELEASE_TO_NIL(session);
  }
}
-(void)showChooser:(id)args
{
  ENSURE_UI_THREAD_1_ARG(args);
  ENSURE_SINGLE_ARG(args,NSDictionary);
  id success = [args objectForKey:@"success"];
  id cancel = [args objectForKey:@"cancel"];
  RELEASE_TO_NIL(successCallback);
  RELEASE_TO_NIL(cancelCallback);
  successCallback = [success retain];
  cancelCallback = [cancel retain];
  controller = [[GKPeerPickerController alloc] init];
  controller.delegate = self;
  [controller show];
}
#pragma mark Delegates
/* Notifies delegate that a connection type was chosen by the user.
*/
- (void)peerPickerController:(GKPeerPickerController *)picker didSelectConnection-Type:(GKPeerPickerConnectionType)type
{
}
/* Notifies delegate that the connection type is requesting a GKSession
*/
- (GKSession *)peerPickerController:(GKPeerPickerController *)picker sessionForConnection-Type:(GKPeerPickerConnectionType)type
{
  return session;
}
/* Notifies delegate that the peer was connected to a GKSession.
*/
- (void)peerPickerController:(GKPeerPickerController *)picker didConnectPeer:(NSString*)peerID toSession:(GKSession *)session_
{
  RELEASE_TO_NIL(peer);
  peer = [session_ retain];
  if (successCallback)
  {
  NSDictionary *event = [NSDictionary dictionaryWithObjectsAnd-
  Keys:peerID,@"peer",nil];
  [self _fireEventToListener:@"success" withObject:event listener:successCallback
  thisObject:nil];
  }
}
/* Notifies delegate that the user cancelled the picker.
*/
- (void)peerPickerControllerDidCancel:(GKPeerPickerController *)picker
{
  if (cancelCallback!=nil)
    {
      [self _fireEventToListener:@"cancel" withObject:nil listener:cancelCallback this-Object:nil];
    }
  }
}
このコードを説明しましょう。第一にdealloc は簡単にモジュールがアンロードされたときにクリンナップします。単純に自分で確保したメモリを解放しています。 Titanium は、マクロRELEASE_TO_NIL を定義しています。これは、NSObjectに対してreleaseを呼び、参照にnilをセットすることを簡単にします。再度nilの値に対して呼び出しがあっても安全です。
次のメソッドstartはGKSessionを開始してメンバ変数sessionに参照を保持します。startメソッドはマクロ ENSURE_SINGLE_ARG を使って、引数を変換し、それをほどいて、第一引数として変数 "name"に入れます。
次のメソッド stop は多くの接続したピアからセッションを切断して、参照を解放します。
次のメソッド showChooser はUIダイアログへの責務を負っています。モジュールメソッドは非UIスレッドで実行されていますが、すべてのUIKitメソッドはメインUIスレッド上で実行されなければなりません。この場合、マクロ ENSURE_UI_THREAD_1_ARG を使って、UIスレッドでメソッドが実行されるようにします。第2のマクロ ENSURE_SINGLE_ARG は、第一引数としてNSDictionary に変換して渡します。
コールバックを抜き出し、既に潜在的に保持しているメモリを解放し、クラスのメンバ変数として引数を保持します。それらを、後でネイティブコードから関数が呼び出したいときに使います。GameKitの残りのコードは、コントローラを生成したり、デレゲートとして登録したり、チューザーを表示したりしています。
残りのメソッドは、自前で実装した追加的なGameKitチューザーデレゲートメソッドです。"peerPicker-Controller"メソッドはピアが選択されたとき呼ばれます。この場合"success"コールバックにいくつかのデータとともにイベントを発行します。JavaScriptイベントに渡されたデータは、 key/valueペアのNSDictionary として渡されます。キーのおのおのはコールバック関数に渡されるイベントオブジェクトのプロパティになります。Proxy メソッド_fireEventToListener:withObject:listener:thisObject: を呼んで、コールバックメソッドを呼び出し、それにデータを渡すことができます。追加的なイベントデータが何もないなら、"withObject" に対してnilを渡すこともできます。
さて、練習を完成させるために、ComTestModule.h のインターフェースコードを追加しましょう:
#import "TiModule.h"#import <GameKit/GameKit.h>
@interface ComTestModule : TiModule <GKPeerPickerControllerDelegate>
{
  GKSession *session;
  GKSession *peer;
  GKPeerPickerController *controller;
  KrollCallback *successCallback;
  KrollCallback *cancelCallback;
}
@end
さて、インターフェースと実装を追加したので、Xcodeでコンパイルしコンパイルエラーにならないことを確認しましょう。エラーがないことが確認できたら、コンソールから”titanium run”を実行すれば、次の結果を得るでしょう:

さて、"Cancel" ボタンを押して、 cancel コールバックを呼び出してみましょう。次の結果になるはずです:

cancelが現れたら、万事良好です。動いています!

モジュールアセットの構築

モジュールとともにモジュールアセットを配布するには、プロジェクトの "assets"ディレクトリにそれらを配置しなければなりません。このフォルダ内にあるどのアセットも(後で見るようにJavaScriptファイル以外は)配布され、アプリケーションバンドルの"module/<moduleid>"というパターンのフォルダにコピーされます。Objective-Cコードからは、この相対パスを使ってアセットをロードできます。例えばfoo.png"という名前のモジュール画像がある場合、以下の例のようにロードすることができるでしょう:
NSString *path = [NSString stringWithFormat:@"modules/%@/foo.png",[self moduleId]];NSURL *url = [TiUtils toURL:path proxy:self];UIImage *image = [TiUtils image:url proxy:self];

JavaScript ネイティブモジュールの構築

(訳注:"JavaScript native module"という用語は、誤解を与える表現なので、単に"JavaScript module"と呼ぶべきではないか?)

Titaniumモジュールのすばらしい点の一つが完全にネイティブでも完全にJavaScript でもありうることです。今まで、もっぱら完全にネティブなモジュールについて議論してきました。

しばしばネイティブモジュールを作成したいが、JavaScriptで書いて実装しコンパイルしたモジュールとして配布したいことがあります。Titaniumでは、モジュールのコードを"assets"ディレクトリ内の"<module_id>.js"と名付けたファイル内でモジュールコードを書くことができます。 サンプルプロジェクトの名前に従えば、そのファイル名は"com.test.js"となるでしょう。
モジュールファイルは、モジュールの宣言とイクスポートにCommonJS フォーマットを使用しなければなりません。一つのプロパティと一つの関数を定義したとても簡単な例から始めましょう:
exports.prop1 = 123;
exports.func = function(v)
{
  return "hello : " + v;
};
このコードを"assets"フォルダ配下の"com.test.js"となづけた新規ファイル にコピーしましょう。
さて、モジュールを利用するために、テストアプリケーションのコードを修正します。既にあるこのファイルにある(訳注:どれだかわからない)コード変更を取り去って、次のものと置き換えましょう:
var my_module = require('com.test');
var result = my_module.func( "prop1 is " + my_module.prop1 );
alert(result);
さて、コンソールで"titanium run"をタイプして、プロジェクトを実行します。次のようになるはずです:

CommonJS は、(モジュールが読み込まれる以前に事前定義された)特別な"exports"オブジェクトに対して定義することで、1つ以上のプロパティ/関数をクリーンにイクスポートできるようにします。"exports"オブジェクトの外側で定義されたメソッドやプロパティはどんものでも、モジュールのスコープの中では考慮されますが、モジュールの外側からは見えなくなるでしょう。

ステップ3:モジュール配布のためのパケージング

Titanium モジュールは、開発者自身の配布メカニズムの内部でも、Titanium+Plus marketplaceのような外部でも 、配布のためのビルドが容易になるように生成されます。

モジュールの記述

Titanium モジュールメタデータは "manifest"と名付けられた特別のファイルに記述されます。このファイルは単純なkey/value プロパティフォーマットになっています。テストモジュールの例をあげます:
#
# this is your module manifest and used by Titanium
# during compilation, packaging, distribution, etc.
#
version: 0.1
description: My module
author: Your Name
license: Specify your license
copyright: Copyright (c) 2010 by Your Company
# these should not be edited
name: test
moduleid: com.test
guid: 5bfdfd9b-12fe-42a4-bf2f-6ce7841fb4ae
platform: iphone
minsdk: 1.4.0
モジュールを配布する前に、この manifest ファイルを編集して、いくつかの値を変更しなければなりません。いくつかの値は事前に生成されていて、編集すべきではありませんーーこれらは前にコメントをつけて注意を喚起してあります。manifest ファイルではハッシュ文字 (#)で始まる行が無視されます。次のものがmanifestのおのおののエントリの説明です:
エントリ  説明/目的
version モジュールのバージョン。メジャーな変更がある場合にその都度この値を変更して配布すべきです。versionはドット記法で書かれます (X.Y.Z)。空白を含んではいけません。数字以外の文字は使用できません。
description モジュールの人間に読める記述。モジュール名ととも表示するための、短く表示可能なものであるべきです。
author モジュールをともに表示するための人間に読める作者名。単に "Jeff Haynie"のような個人名にしてもよいし、"Appcelerator"のような組織の名称にしてもよい。
license 人間に読めるライセンスの名称。"Apache Public License" や "Commercial"のように短く記述すべきです。
copyright 人間に読めるモジュールの著作権文字列。例えば、"Copyright (c) 2010 by Appcelerator, Inc."
name プロジェクトを作成したときに生成された変更不可のモジュールの名前。この値を変更すべきでありません。
moduleid プロジェクトを作成したときに生成された変更不可のモジュールのID。この値を変更すべきでありません。注意:一意なIDを生成すべきです。推奨されるのは、一意性を担保するためのパターンとして、反転DNS企業名にモジュール名を加えたものを使うことです。Titanium Marketplaceは、配布の際に、一意なモジュールIDだけを許可します。この値を編集しなければならないなら、モジュールの実装ファイルの中の値も編集しなければなりません。
guid プロジェクトを作成したときに生成された変更不可の一意なモジュールID。この値を変更すべきでありません。
platform プロジェクトを作成したときに生成された変更不可のモジュールのプラットフォームターゲット。この値を変更すべきでありません。
minsdk モジュール作成時に使用された最小のTitanium SDKのバージョンを表す生成された値。現状では使用されないが将来のために予約。

モジュールの手動配布

手動でモジュールを配布するには、 "build.py" を使ってモジュールを単純にビルドしてzipファイルを直接配布すればいいでしょう。モジュールは /Library/Application\ Support/Titanium ディレクトリにコピーされ、(プロジェクト内で参照され、プロジェクトがビルドされると)インストールされます。
4ページの注意と同様、Titanium SDKが<t1>/Library</t1>の代わりに<t0>~/Library</t0>にインストールされていれば、/Library/Application\ Support/Titanium ではなくて ~/Library/Application\ Support/Titanium にモジュールをコピーする必要があります。

Titanium+Plus Marketplace 経由の配布

(訳注:この項は、「構想」が書かれている。現在(2011/9)準備中のAppcelerator Marketplaceが公開される予定である。しかし配布の様式など、変更されていると思われる)

Titanium+Plus Marketplaceを通じてモジュールを配布するには、まず普通にパッケージしておく必要があります。ローカル環境でのモジュールのテストがすんでいて、配布の準備ができていれば、配布のためにマーケットプレースに登録できます。配布可能になるためにはいくつかの事前の必須項目があります:
  • 正当な Titanium デベロッパアカウントが必要です
  • manifest の値を完全に埋めておかなければなりません
  • プロジェクトのLICENSE ファイルに正当なライセンス文書をおかなければなりません
  • プロジェクトのドキュメンテーションディレクトリのindex.md ファイルに正当なドキュメンテーションをおかなければなりません
  • アップロードの際に(フリーの可能ですが)価格のような追加のメタデータを指定しなければなりません
  • モジュールに課金する場合には、Appceleratorが支払い可能な支払い方法の設定を確立しておく必要があります
  • Titanium+Plus Marketplace のサービス規約に同意しなければなりません
一旦モジュールがアップロードされ必要な申し込み手続きが完了したら、モジュールは登録され、マーケットプレースディレクトリで利用可能になります.
初めてモジュールを登録する場合は、上記の基本的な必須項目についてレビューが行われます。しかし、一旦承認されれば、更なる承認なしに直ちに登録されるでしょう。


文書の終わり

1 件のコメント:

  1. AppceleratorのサイトのURLがかわってしまったので、画像のリンクが切れています。そのうち直します。

    返信削除