AlloyでのiPhoneアプリ開発にCoffeeScriptを使う

犬が早朝から吠えたので4時に叩き起こされたが、タイムラインを追いかけていたらAlloy with CoffeeScript のお誘いというこれは!というエントリがあがっていたので、早速誘われてみた。尚、jsとcoffeeは一緒のディレクトリに置いておくのが好みなので@k0sukeyのオリジナルなjmkに変更をくわえてあります。

ストップウォッチをつくる

Titanium Mobile iPhone/Androidアプリ開発入門のストップウォッチを参考にしました。

ProductName Titanium Mobile iPhone/Androidアプリ開発入門―JavaScriptだけで作る
小澤 栄一
秀和システム / 2520円 ( 2012-02 )


本書ではui.jsという1つのファイルで完結していますがAlloyのようなMVCフレームワークを使ったほうがコードの見通しは良くなると思います。

雛形をつくる

$ titanium create stopwatch # 対話でターゲットデバイスやapp-id設定
$ cd stopwatch
$ alloy new # alloyのひな形作成
$ alloy generate jmk

coffeeがコンパイルされるようにする

stopwatch/app/alloy.jmkを以下のように書きなおす。これでapp/controller のcoffeeファイルはalloy compileでコンパイルされるようになります。

task("pre:compile", function(event,logger) {
    var wrench = require("wrench"),
    fs = require("fs"),
    path = require("path"),
    controller_root = event.dir.controllers,
    coffee = require("coffee-script");

    wrench.readdirSyncRecursive(controller_root).forEach(function(controller){
    if (controller.match("coffee$")) {
        fs.writeFileSync(
        path.join(controller_root,controller.replace("coffee", "js")),
        coffee.compile(
            fs.readFileSync(path.join(controller_root, controller)).toString(),
            { bare: true }));
    }
    });
});

task("post:compile",function(event,logger){
});

alloyの開発

あとは普通に。

app/views/index.xml

<Alloy>
    <TabGroup>
        <Tab id="tab1" icon="dark_clock.png" title="ストップウォッチ">
            <Window id="window1" title="ストップウォッチ">
                <Label id="label">
          00:00:00.000
                </Label>
        <Button id="button">
          Start
        </Button>
            </Window>
        </Tab>
        <Tab id="tab2" icon="dark_info.png" title="使い方">
            <Window id="window2" title="使い方">
          <WebView url="howto.html">
          </WebView>
            </Window>
        </Tab>
    </TabGroup>
</Alloy>

/app/styles/index.tss

{"Window": {
  "barColor": "#000",
  "backgroundColor": "#000"
},
"Tab": {
  "backgroundColor": "#000"
},
"#label": {
  "color": "#fff",
  "width": "auto",
  "height": "auto",
  "font": {
    "fontSize": 40,
    "fontWeight": "bold"
  }
},
"#button": {
  "color": "#000",
  "width": 150,
  "height": 40,
  "bottom": 30
}}

webview用のhtmlは /app/assets/iphone/howto.htmlに

<html>
    <head>
        <title>使い方</title>
    </head>
<body>
    <h1>使い方</h1>このサンプル
は<a href="http://www.amazon.co.jp/dp/479803231X">Titanium Mobile
iPhone/Androidアプリ開発入門</a>のストップウォッチのサンプルをAlloyで
書きなおしてみたものです。
<img src="http://ec2.images-amazon.com/images/I/41jDZ5IY01L._BO2,204,203,200_PIsitb-sticker-arrow-click,TopRight,35,-76_AA300_SH20_OU09_.jpg"/>
</body>

興味を持ったら購入して読んでみるとよいと思いますよ。

</html>

最後にcoffeeをapp/controllers/index.coffeeに

started =false
intervalID = null

startStopwatch = ->
  $.label.text = "00:00:00.000"
  startTime = new Date()

  _updateTimer = ->
    u_sec = 1000
    u_min = 60 * u_sec
    u_hour = 60 * u_min
    now = new Date()
    diff = now - startTime

    hour = Math.floor(diff/u_hour)
    min = Math.floor((diff - hour*u_hour)/u_min)
    sec = Math.floor((diff - hour*u_hour - min*u_min)/u_sec)
    msec = diff / u_sec
    $.label.text = ("0" + hour).slice(-2) + ":" +
                   ("0" + min).slice(-2) + ":" +
                   ("0" + sec).slice(-2) + "." +
                   ("00" + msec).slice(-3)

  intervalID = setInterval(_updateTimer, 3)
  return

$.button.addEventListener 'click', -> 
  if started
    clearInterval(intervalID)
    $.button.title = "Start"
    started = false
  else
    startStopwatch()
    $.button.title = "Stop"
    started = true
  return

$.index.open()

実行

$ alloy run

alloy_stopwatch

Let's Enjoy!

Emacs+titanium cli+alloyでiPhoneアプリを開発する

静岡javaScript勉強会 #2に参加されるみなさんこんにちは、参加されないみなさんもこんにちは。僕は11月のすべての土曜日を留守にして家族の顰蹙を買うという重圧に耐え切れずに早々に離脱してしまいましたので朝からお家でjsってます。

ところで、@tomofの「Backbone.js入門」面白かったですか?よっしゃーいっちょおとうさんもbackbone.jsでウェブアプリケーションつくっちゃうぞーっていう気になりましたか?はいそうですか、そうですよね。ついでに、その勢いでBackbone.jsっぽい開発のノリでiPhoneアプリを開発できたら面白いと思いません?思いますよね。

やりましょう、Alloyで!(孫社長風味でよろしく)

というような内容のエントリです。

1351985625

Titanium CLIとAlloyのインストール

Titanium CLIはTitaniumで開発するためのコマンドラインのツールで、AlloyはTitaniumのためのMVCフレームワークですが、両方共Node.jsのv0.8以上がインストールされていればnpmでインストールできるはずです。

npm install -g titanium
npm install -g alloy

うまくいかない場合は下のサイトを参考にしてください。

エディタ

EmacsでもvimでもST2でも好きなモノを使えば良いとおもいますが、僕はEmacs(24)をつかっています。でnxmlモードにzencoding-modeをあててあります。入ってない場合はM-x list-packaegesでzencoding-modeを選んでインストールしたらinit.elに下の内容を追記しておけばいいです。

(add-hook 'sgml-mode-hook 'zencoding-mode) 
(add-hook 'nxml-mode-hook 'zencoding-mode)

他にはjavascriptの開発用にjs2-modeを入れているのとcoffeeモードにflymakeが効くようにしてありますが今回の内容では必要ありません。

追記121118: CoffeeScriptを使って開発する方法

開発の流れ

最初にtitaniumコマンドでプロジェクトの雛形をつくります

$ titanium create
Titanium Command-Line Interface, version 3.0.13
Copyright (c) 2012, Appcelerator, Inc.  All Rights Reserved.

Please report bugs to http://jira.appcelerator.org/

Target platforms: (android,ios,ipad,iphone,mobileweb) iphone
App ID: com.example.tabsample
Project name: tabsample

[INFO] Creating Titanium Mobile application project
[INFO] Project 'tabsample' created successfully in 276ms

プロジェクトのディレクトリに移動してalloyの雛形をつくります。

$ cd tabsample/
$ alloy new
       .__  .__                
_____  |  | |  |   ____ ___.__.
\__  \ |  | |  |  /  _ <   |  |
 / __ \|  |_|  |_(  <_> )___  |
(____  /____/____/\____// ____|
     \/                 \/
Alloy by Appcelerator. The MVC app framework for Titanium.

[INFO] Installed "ti.physicalSizeCategory" module to tiapp.xml
[INFO] Installed "ti.alloy" plugin to tiapp.xml
[INFO] Deployed ti.alloy compiler plugin to plugins/ti.alloy/plugin.py
[INFO] Deployed ti.alloy hooks to plugins/ti.alloy/hooks
[INFO] Generated new project at: app

ここからEmacsを使っていきます。まずはapp/views/index.xmlを修正します。中身を全部消してzencodingでさくっと用意します。zencodingはC-returnで起動させて

Alloy>TabGroup>(Tab#tab icon title>Window#window title backgroundColor>Label#lable)*2

みたいにワンライナーで書けるので便利すぎです。

emacs-zen

zencodingで大まかにスキャフォールドを用意したら、アトリビュートなんかを設定します。ちなみに画像はここから取ってきてapp/assets/iphone/に起きました。

<Alloy>
    <TabGroup>
        <Tab id="tab1" icon="KS_nav_views.png" title="Tab 1">
            <Window id="window1" title="Tab 1" backgroundColor="#fff">
                <Label id="label1">
          I am Window1
                </Label>
            </Window>
        </Tab>
        <Tab id="tab2" icon="KS_nav_ui.png" title="Tab 2">
            <Window id="window2" title="Tab 2" backgroundColor="#fff">
                <Label id="label2">
          I am Window2
                </Label>
            </Window>
        </Tab>
    </TabGroup>
</Alloy>

app/styles/index.tssはLabelの設定だけ。

    "Label": {
    font: {fontSize:20, fontFamily:"Helvetica Neue"},
    color: "#999"
}

tssにはどのモードをアサインするのがいいのでしょうか?誰か知っていたら教えて下さい。(追記121117 json-modeあててみた)

iOSシミュレータを起動

プロジェクトのトップディレクトリで

$ alloy run

するとシミュレータが立ち上がります。素敵ですね。これで、雛形作ってシミュレータを動かすところまでできました。あとはapp以下を良い感じにいじってイケてるアプリを作るだけですね。

ti-alloy

シミュレータが立ち上がるまでに結構待たされるのでストレスを感じますが、それは僕のmacが遅いからですね。はやく買い換えたい。

おまけ(Alloyの開発にJadeを使う)

@k0sukeyAlloyJadeという素敵なものがあることを教えてもらいました。Jade派の僕としては非常に惹かれるので後で試そうかなと思っています。

Alloyのモデル

ドキュメントによると、

Alloy uses Backbone.js to provide support for its models and collections. Alloy also borrows the concepts of migrations and adapters from Rails for storage integration.

ということでmodelの仕組みはbackbone.jsっぽくてマイグレーションはRoRっぽいが、設定周りがちょっと違う。

  • 'sql' for the SQLite database on the Android and iOS platform.
  • 'localStorage' for HTML5 localStorage on the Mobile Web platform.
  • 'properties' for storing data locally in the Titanium SDK context.

とどのストレージを使うか選ぶ。backbone.jsとかspine.jsだとlocalstrageかrestなところが選択肢が増えたことで型とか設定するようになっている。

モデルの変更とかで送られてくるイベントの種類はドキュメントに書いてないのでソースコード読まないといけないっぽいんだけど、backbone.jsと同じかなと思っている。

alloy / test / apps / models / propertiesを眺めてみたんだけどcollectionの位置づけがわからなかった(backbone.jsのコレクションとモデルって分かれてるよね)。

なんか書いてみないとダメだな。

AlloyのTSSで演算させる

alloy / test / apps / advanced / themesはconfig.jsonでプラットフォームごとにテーマを切り替えるというサンプル。

alloy themes

TSSっていうのはAlloyのスタイルシート(CSS)みたいなものだが、exprを使うと演算できる。昨日@k0sukeyが言ってたのはこれのことかと今更理解したわけだが。

スライダーが縦になっているが、これはTSSをみると

"#slider": {
    leftTrackImage: '/title_green.png',
    top: 228, 
    left: -145,
    width: 350,
    transform: expr(Titanium.UI.create2DMatrix().rotate(-90))
},

こんな感じで縦方向に-90度回転されている。

Alloyでの継承(というより別のところが理解できてない)

alloy / test / apps / advanced / inheritanceを読んでみた。継承は分かりやすいが、windowまわりがいまいちわからん。

controller/index.js

$.index.open();

function openDialog(e) {  
    Alloy.createController(e.source.title, {
        message: 'Opened ' + e.source.title
    }).openDialog($.index);
}

createControllerメソッドで文字列を渡すとコントローラーが作られる。

views/index.xmlはこんな感じになっていて

<Alloy>
    <Window>
        <View layout="vertical">
            <Button onClick="openDialog">baseDialog</Button>
            <Button onClick="openDialog">animatedDialog</Button>
        </View>
    </Window>
</Alloy>

Buttonのtextじゃなくてtitleで渡される(e.source.title)みたいだ。コード追っかけててちょっと引っかかった。

openDialogメソッドはそれぞれのコントローラーで実装されている。

controllers/baseDialog.js

var args = arguments[0] || {};
$.message.text = args.message || 'dialog';

exports.openDialog = function(win) {
    $.refWin = win; 
    $.refWin.add($.cover);
    $.refWin.add($.dialog);
}

exports.closeDialog = function() {
    $.refWin.remove($.cover);
    $.refWin.remove($.dialog);
    $.refWin = $.cover = $.dialog = null;   
}

refWinってなんだよ?と思ったが新しい属性作って放り込んでいるだけなのかな?あとWindowにWindowを追加してんの?($.refWin.add)。それとも$.cover,$dialogはviewなんだろうか?コンパイル後のコードを読むにはどうしたらいいんだろうか?

継承はexports.baseControllerにスーパークラスを指定する。あとは普通にメソッドを上書きすればいい。

// Establish inheritance from baseDialog
exports.baseController = 'baseDialog';

// Override baseDialog's openDialog() function. We will still
// be able to use baseDialog's closeDialog() function.
exports.openDialog = function(win) {
    $.refWin = win; 
 ...

}

静岡 Titanium Mobile もくもく会に参加した

参加されたみなさんお疲れ様でした。

titanium cliでのアプリ開発の流れを学んだのが大きな収穫だった。Titanium Studioを使わずにEmacsで開発できるようになるのは個人的には非常に大きい。

ステッカー頂いた。

1351985625

インストールとか使い方は先取り “新” Titanium CLIの通りなんだが、titanium createコマンドがなかったのでちょっと躓いた。結局Developer Preview: The New Titanium Command Line Interfaceにあるように

titanium sdk install --branch 3_0_X --default

と打って、3.0系を入れることでtitanium createが使えるようになった。

もくもく会ではtodoMVCをalloyでやるつもりだったんだけど、Alloyのドキュメント読んで、Test/Basicsを触ったら終わってしまった。

todoサンプルを見つけたのでこっちも読んでみる。

その他

  • iPad miniは実物みると欲しくなる
  • mac book pro retina素敵。コンパイル速くて羨ましい(僕のはmacbook 2008 early)

11/3は静岡 Titanium Mobile もくもく会ですよ

javascriptでiPhoneアプリをつくりつつ、お昼にみんなでつけナポリタンを食べにいきましょう。

車でくる場合、ほんいちパーキングとかいうところに停めると2h無料になるんだけど、入り口が裏手にあってわかりにくいので、確認してから来たほうがよいかも。

僕はalloyTodoMVCでもやってみようかなぁと。

Node.js入門を読んだ

500ページ超の大作だが読みやすかったのでさくっと読みおわった。

ProductName サーバサイドJavaScript Node.js入門
清水俊博
アスキー・メディアワークス / 3990円 ( 2012-10-26 )


16章のExpressの章が丁寧、しかも分かりやすく書かれて理解が深まった。ここだけでも購入した意味があったかも。

他には7,9,10のイベント、ネット関係と22章のnpmパッケージを作る章がよかった。

Node.js入門を読み始めた

8章まで読んだ。

ProductName サーバサイドJavaScript Node.js入門
清水俊博
アスキー・メディアワークス / 3990円 ( 2012-10-26 )


イベントループの章(7章)がわかりやすいが、読書会向けの本ではなさそうな感触。

追記 121029

第二部まで読んだ。

9章のnetと10章のHTTP/HTTPSが詳しくて良かった。

11月の静岡はクライアントサイドの開発の勉強会が盛り沢山だ

来月の勉強会はやたらと盛りだくさんだ。

Titanium MobileはjavascriptでiPhone,Androidアプリを開発したい人にとってはよいソリューションだと思います。で、独りで開発しようと思ったらデザインとか配色とか習っておきたいのでTalkNoteは役に立ちそう。

もう少しゆるい感じのjQueryとかそっち方面に興味があれば静岡javaScript勉強会に参加すればいいいと思います。

そして、あなたが本物のプログラマならば「三島Haskell無名関数の会」が第一選択肢になるはず(ですよね?)。

(ATNDできた。)

YesodというHaskellのウェブアプリケーションフレームワークが熱く語られるという噂です。僕は「Stateモナドで理解するモナドというパターン」という話でもしようかなと思っているが、みんなモナド理解しているかもしれないから、「Haskellでつくる人工無脳」にするかもしれない。

ProductName すごいHaskellたのしく学ぼう!
Miran Lipovača
オーム社 / 2940円 ( 2012-05-23 )


次の週にはJAWS静岡 #2があるのか、、、