scalin titanium mobile
TRANSCRIPT
ScalingTitaniumMobile
at Android Bazaar and Conference 2012 Spring
http://bit.ly/scale-ti
Saturday, March 24, 12
おぐらじゅんや
@junya
Saturday, March 24, 12
合同会社キュニップabout.qnyp.com
Saturday, March 24, 12
Scaling
Saturday, March 24, 12
Scalingチーム開発
機能の追加・削除
対応デバイスの追加
Saturday, March 24, 12
Scalingチーム開発
機能の追加・削除
対応デバイスの追加 }Saturday, March 24, 12
Scalingを容易にする
コードの書き方
チーム開発
機能の追加・削除
対応デバイスの追加 }Saturday, March 24, 12
What’s Titanium Mobile
最近のTitanium Mobile
コードの分割
underscore.js
CoffeeScript
プラットフォーム依存コードの隠蔽
カスタムイベント
Agenda
Saturday, March 24, 12
What’s
Titanium Mobile?
Saturday, March 24, 12
Titanium Mobileタイタニウム・モバイル
Saturday, March 24, 12
by Appcelerator, Inc.
Saturday, March 24, 12
from JavaScript
Saturday, March 24, 12
from JavaScript
to iOS
Saturday, March 24, 12
from JavaScript
to iOS
to Android
Saturday, March 24, 12
from JavaScript
to iOS
to Android
It’s OpenSource!
Saturday, March 24, 12
from JavaScript
to iOS
to Android
It’s OpenSource!
Community License => Free
Saturday, March 24, 12
from JavaScript
to iOS
to Android
It’s OpenSource!
Community License => Free
Indie License => $49/month
Saturday, March 24, 12
開発環境
Mac OS XWindowsUbuntu Linux
Saturday, March 24, 12
iOS 4.0.X -Android 2.2 (API 7) -
Titanium Compatibility Matrix - Documentation & Guides - Appcelerator Wikihttps://wiki.appcelerator.org/display/guides/Titanium+Compatibility+Matrix
最新版のターゲット環境
Saturday, March 24, 12
Only Mac OS X
iOS
Saturday, March 24, 12
Code Example
Saturday, March 24, 12
Saturday, March 24, 12
var win = Ti.UI.createWindow({ title: 'github/appcelerator' });
var tableView = Ti.UI.createTableView();win.add(tableView);
var tabGroup = Ti.UI.createTabGroup();var tab = Ti.UI.createTab({ icon: 'KS_nav_ui.png', title: 'Repos', window: win});tabGroup.addTab(tab);tabGroup.setActiveTab(tab);tabGroup.open();
var xhr = Ti.Network.createHTTPClient();var url = 'https://api.github.com/orgs/appcelerator/repos?type=public';xhr.open('GET', url);xhr.onload = function() { var res = JSON.parse(this.responseText); tableView.setData(res.map(function(repo) { return Ti.UI.createTableViewRow({ title: repo.name }); }));;};xhr.send();
Saturday, March 24, 12
var win = Ti.UI.createWindow({ title: 'github/appcelerator' });
var tableView = Ti.UI.createTableView();win.add(tableView);
var tabGroup = Ti.UI.createTabGroup();var tab = Ti.UI.createTab({ icon: 'KS_nav_ui.png', title: 'Repos', window: win});tabGroup.addTab(tab);tabGroup.setActiveTab(tab);tabGroup.open();
var xhr = Ti.Network.createHTTPClient();var url = 'https://api.github.com/orgs/appcelerator/repos?type=public';xhr.open('GET', url);xhr.onload = function() { var res = JSON.parse(this.responseText); tableView.setData(res.map(function(repo) { return Ti.UI.createTableViewRow({ title: repo.name }); }));;};xhr.send();
25行
Saturday, March 24, 12
var win = Ti.UI.createWindow({ title: 'github/appcelerator' });
var tableView = Ti.UI.createTableView();win.add(tableView);
var tabGroup = Ti.UI.createTabGroup();var tab = Ti.UI.createTab({ icon: 'KS_nav_ui.png', title: 'Repos', window: win});tabGroup.addTab(tab);tabGroup.setActiveTab(tab);tabGroup.open();
var xhr = Ti.Network.createHTTPClient();var url = 'https://api.github.com/orgs/appcelerator/repos?type=public';xhr.open('GET', url);xhr.onload = function() { var res = JSON.parse(this.responseText); tableView.setData(res.map(function(repo) { return Ti.UI.createTableViewRow({ title: repo.name }); }));;};xhr.send();
25行
Saturday, March 24, 12
var win = Ti.UI.createWindow({ title: 'github/appcelerator' });
var tableView = Ti.UI.createTableView();win.add(tableView);
var tabGroup = Ti.UI.createTabGroup();var tab = Ti.UI.createTab({ icon: 'KS_nav_ui.png', title: 'Repos', window: win});tabGroup.addTab(tab);tabGroup.setActiveTab(tab);tabGroup.open();
var xhr = Ti.Network.createHTTPClient();var url = 'https://api.github.com/orgs/appcelerator/repos?type=public';xhr.open('GET', url);xhr.onload = function() { var res = JSON.parse(this.responseText); tableView.setData(res.map(function(repo) { return Ti.UI.createTableViewRow({ title: repo.name }); }));;};xhr.send();
25行
Saturday, March 24, 12
Titanium Mobile製アプリ
Saturday, March 24, 12
MogSnapZaim
GetGlue Welcome to NIFTY-Serve
Built with Appcelerator Titanium / http://www.builtwithtitanium.com/
Saturday, March 24, 12
Titanium Mobile
最近の
Saturday, March 24, 12
Titanium Mobile SDK 1.8.2Feb 29, 2012
Saturday, March 24, 12
1.8系のサービスパック
メモリリーク修正
クラッシュバグ修正
など
http://developer.appcelerator.com/apidoc/mobile/1.8.2/changelog.html
Saturday, March 24, 12
4月に2.0がリリース予定
Cocoafishサービスとの統合
ジオロケーション機能の強化
TableViewのパフォーマンス改善
モジュールAPIの改良
Saturday, March 24, 12
Scalingを容易にする
コードの書き方
チーム開発
機能の追加・削除
対応デバイスの追加 }Saturday, March 24, 12
Code Structure
コードの分割
Saturday, March 24, 12
// app.jsvar win = Ti.UI.createWindow({ backgroundColor: '#fff'});
var button = Ti.UI.createButton();button.addEventListener(‘click’, function(e) { alert(‘Button Clicked’);});win.add(button);
win.open();
簡単なアプリ。
Saturday, March 24, 12
// app.jsvar win = Ti.UI.createWindow({ backgroundColor: '#fff'});
var button = Ti.UI.createButton();button.addEventListener(‘click’, function(e) { alert(‘Button Clicked’);});win.add(button);
win.open();
ウィンドウの生成。
Saturday, March 24, 12
// app.jsvar win = Ti.UI.createWindow({ backgroundColor: '#fff'});
var button = Ti.UI.createButton();button.addEventListener(‘click’, function(e) { alert(‘Button Clicked’);});win.add(button);
win.open();
ボタンの生成とクリック時の処理設定。
Saturday, March 24, 12
// app.jsvar win = Ti.UI.createWindow({ backgroundColor: '#fff'});
var button = Ti.UI.createButton();button.addEventListener(‘click’, function(e) { alert(‘Button Clicked’);});win.add(button);
win.open();
ウィンドウのオープン。
Saturday, March 24, 12
1つのapp.jsでは、
すぐに見通しが悪くなる。
Saturday, March 24, 12
チーム開発では分業も困難。
Saturday, March 24, 12
コードを分割する手法。
Saturday, March 24, 12
window.url
Ti.include()
CommonJS
Saturday, March 24, 12
手法によって、
Android対応
テストのしやすさ
保守性
などに大きく影響する。
Saturday, March 24, 12
window.url
Saturday, March 24, 12
ウィンドウ生成時の
urlパラメータを利用。
Saturday, March 24, 12
// app.jsvar win = Ti.UI.createWindow({ backgroundColor: '#fff', url: 'window.js'});win.open();
// window.jsvar win = Ti.UI.currentWindow;var button = Ti.UI.createButton();button.addEventListener(‘click’, function(e) { alert(‘Button Clicked’);});win.add(button);
Saturday, March 24, 12
window.urlスタイルは、
古いサンプルコード、ブログ、記事
などで使われている。
Saturday, March 24, 12
パフォーマンスの問題もあり、
現在は推奨されていないので
使わないこと。
Saturday, March 24, 12
window.url
Ti.include()
CommonJS
Saturday, March 24, 12
Ti.include
Saturday, March 24, 12
Ti.include関数を利用。
Saturday, March 24, 12
// app.jsvar name = 'Foo';var win = Ti.UI.createWindow({ backgroundColor: '#fff', name: name});Ti.include('window.js');win.open();
// window.jsvar label = Ti.UI.createLabel({text: win.name});label.addEventListener('click', function(e) { alert(name + ' Clicked');});win.add(label);
Saturday, March 24, 12
var name = 'Foo';var win = Ti.UI.createWindow({ backgroundColor: '#fff', name: name});var label = Ti.UI.createLabel({text: win.name});label.addEventListener('click', function(e) { alert(name + ' Clicked');});win.add(label);win.open();
実際には1つのファイルに結合されて実行される。
Saturday, March 24, 12
名前の衝突が起こりやすい。
Saturday, March 24, 12
現在はTi.includeも非推奨なので
使わないこと。
Saturday, March 24, 12
window.url
Ti.include()
CommonJS
Saturday, March 24, 12
CommonJS
Saturday, March 24, 12
サーバーサイドJavaScriptにおける
共通仕様 CommonJSの、
モジュール仕様を採用。
Saturday, March 24, 12
コードの分割・再利用の手法として
現在、Appceleratorが推奨。
Saturday, March 24, 12
// app.jsvar name = 'Foo';var window = require('window');var win = window.myWindow(name);win.open();
alert('label = ' + label); // labelは見えない
// window.jsexports.myWindow = function(name) { var win = Ti.UI.createWindow({ backgroundColor: '#fff' }); var label = Ti.UI.createLabel({text: name}); label.addEventListener('click', function(e) { alert(name + ' Clicked'); }); win.add(label); return win;};
Saturday, March 24, 12
Titanium Studioで生成される
テンプレートもCommonJS形式。
Saturday, March 24, 12
Saturday, March 24, 12
Saturday, March 24, 12
app.jsui/ApplicationWindow.jsui/FirstView.js
Saturday, March 24, 12
function FirstView() { var self = Ti.UI.createView(); var label = Ti.UI.createLabel({ color:'#000000', text:String.format(L('welcome'),'Titanium'), height:'auto', width:'auto' }); self.add(label);
label.addEventListener('click', function(e) { alert(e.source.text); });
return self;}
module.exports = FirstView;
ui/FirstView.js
Saturday, March 24, 12
function ApplicationWindow() { var FirstView = require('ui/FirstView');
var self = Ti.UI.createWindow({ backgroundColor:'#ffffff', navBarHidden:true, exitOnClose:true });
var firstView = new FirstView(); self.add(firstView);
return self;}
module.exports = ApplicationWindow;
ui/ApplicationWindow.js
Saturday, March 24, 12
var ApplicationWindow = require('ui/ApplicationWindow');new ApplicationWindow().open();
app.js
Saturday, March 24, 12
注意
相対パスを指定した際の挙動が
CommonJSと異なり、Resources/
からの相対指定として認識される。
Saturday, March 24, 12
パフォーマンスの問題がない。
メンテナンス性が高い。
再利用しやすい。
単体テストが行いやすい。
Saturday, March 24, 12
var Window;if (globals.osname === 'ipad') { Window = require('ui/ipad/iPadWindow');} else if (globals.osname === 'iphone') { Window = require('ui/iphone/iPhoneWindow');} else { Window = require('ui/android/AndroidWindow');}new Window().open();
デバイスに応じたUIの読み込み。
Saturday, March 24, 12
window.url
Ti.include()
CommonJS
Saturday, March 24, 12
underscore.js
Saturday, March 24, 12
配列や関数に対する操作を
容易にするライブラリ。
Saturday, March 24, 12
JavaScriptの
ビルトインオブジェクトを
拡張しない。
Saturday, March 24, 12
関数プログラミング由来の
ものを中心とした60ほどの関数。
each, map, reduce,filter, reject, any,max, min, shuffle,union, uniq, ...
Saturday, March 24, 12
It’s CommonJS
var _ = require('underscore')._;
Saturday, March 24, 12
JSONをUI用のデータに整形・変換。
var items = JSON.parse(responseText);
var itemNames = _(items).chain() .uniq(false, function(item) { return item.id; }) .select(function(item) { return item.available; }) .sortBy(function(item) { return item.name; }) .name();
Saturday, March 24, 12
JSONをUI用のデータに整形・変換。
var items = JSON.parse(responseText);
var itemNames = _(items).chain() .uniq(false, function(item) { return item.id; }) .select(function(item) { return item.available; }) .sortBy(function(item) { return item.name; }) .name();
Saturday, March 24, 12
JSONをUI用のデータに整形・変換。
var items = JSON.parse(responseText);
var itemNames = _(items).chain() .uniq(false, function(item) { return item.id; }) .select(function(item) { return item.available; }) .sortBy(function(item) { return item.name; }) .name();
Saturday, March 24, 12
JSONをUI用のデータに整形・変換。
var items = JSON.parse(responseText);
var itemNames = _(items).chain() .uniq(false, function(item) { return item.id; }) .select(function(item) { return item.available; }) .sortBy(function(item) { return item.name; }) .name();
Saturday, March 24, 12
JSONをUI用のデータに整形・変換。
var items = JSON.parse(responseText);
var itemNames = _(items).chain() .uniq(false, function(item) { return item.id; }) .select(function(item) { return item.available; }) .sortBy(function(item) { return item.name; }) .name();
Saturday, March 24, 12
JSONをUI用のデータに整形・変換。
var items = JSON.parse(responseText);
var itemNames = _(items).chain() .uniq(false, function(item) { return item.id; }) .select(function(item) { return item.available; }) .sortBy(function(item) { return item.name; }) .name();
Saturday, March 24, 12
CoffeeScript
Saturday, March 24, 12
JavaScriptに比べて
コードの記述量を削減できる。
Saturday, March 24, 12
バグ、ケアレスミスの削減。
Saturday, March 24, 12
パラメータ記述が簡潔に。// JavaScriptvar win = Ti.UI.createWindow({ title: 'github' });var tab = Ti.UI.createTab({ icon: 'KS_nav_ui.png', title: 'Repos', window: win});
# CoffeeScriptwin = Ti.UI.createWindow(title: 'github')tab = Ti.UI.createTab icon: 'KS_nav_ui.png' title: 'Repos' window: win
Saturday, March 24, 12
コールバック記述が簡潔に。
// JavaScriptbutton.addEventListener('click', function(e) { Ti.API.debug('button clicked');});
# CoffeeScriptbutton.addEventListener 'click', (e) -> Ti.API.debug('button clicked')
Saturday, March 24, 12
ループがシンプルに。// JavaScriptvar rows = [];for (var i = 0; i < items.length; i++) { var item = items[i]; if (item.isPublic()) rows.push(createTableRow(items[i]));}var tableView = Ti.UI.createTableView({ data: rows });
# CoffeeScriptrows = []for item in items when item.public() rows.push(createTableRow(item))tableView = Ti.UI.createTableView(data: rows)
Saturday, March 24, 12
後置ifでプラットフォーム毎の
場合分けをシンプルに。
# CoffeeScriptTi.UI.iPhone.appBadge = 10 if Ti.Platform.osname is “iphone”
# JavaScriptif (Ti.Platform.osname === “iphone”) { Ti.UI.iPhone.appBadge = 10;}
Saturday, March 24, 12
クラスベースに設計しやすい。
# CustomView.coffeeclass CustomView view: null constructor: -> @initView() @initEvents() initView: -> @view = Ti.UI.createView() initEvents: -> @view.addEventListener 'click', (e) -> alert('clicked')
module.exports = CustomView
# app.coffeeCustomView = require('CustomView')view = new CustomView()
Saturday, March 24, 12
文字列内で変数展開ができる。
name = "Titanium"alert("Hello, #{name}")
Saturday, March 24, 12
Auto Compile with Guard
$ brew install nodejs$ curl http://npmjs.org/install.sh | sh$ npm install -g coffee-script
$ gem install guard-coffeescript$ gem install rb-fsevent
# Guardfileguard 'coffeescript', :input => 'coffee', :output => 'Resources', :bare => true
$ guard
Saturday, March 24, 12
Facade
プラットフォーム
依存コードの隠蔽
Saturday, March 24, 12
iOSとAndroidで場合分けするコード。if (Ti.Platform.osname === "android") { var picker1 = Ti.UI.createPicker({type:Ti.UI.PICKER_TYPE_DATE}); var picker2 = Ti.UI.createPicker({type:Ti.UI.PICKER_TYPE_TIME}); picker1.addEventListener('change', callback); picker2.addEventListener('change', callback); win.add(picker); win.add(picker2);} else { var picker = Ti.UI.createPicker({ type:Ti.UI.PICKER_TYPE_DATE_AND_TIME }); picker.addEventListener('change', callback); win.add(picker);}
Saturday, March 24, 12
分岐処理をラップして隠蔽。var calendarWindow = function(callback) { var win = Ti.UI.createWindow(); if (Ti.Platform.osname === "android") { var picker1 = Ti.UI.createPicker({type:Ti.UI.PICKER_TYPE_DATE}); var picker2 = Ti.UI.createPicker({type:Ti.UI.PICKER_TYPE_TIME}); picker1.addEventListener('change', callback); picker2.addEventListener('change', callback); win.add(picker1); win.add(picker2); } else { var picker = Ti.UI.createPicker({ type:Ti.UI.PICKER_TYPE_DATE_AND_TIME }); picker.addEventListener('change', callback); win.add(picker); } return win;};
Saturday, March 24, 12
分岐処理をラップして隠蔽。
var win = calendarWindow(function(e) { ... });
Saturday, March 24, 12
モジュール化して再利用。
// utils.jsexports.createCalendarWindow = function(callback) { ...};
// app.jsvar utils = require('/utils');var win = utils.createCalendarWindow(function(e) { ...});
Saturday, March 24, 12
Custom Event
カスタムイベント
Saturday, March 24, 12
イベント
Saturday, March 24, 12
button.addEventListener(‘click’, function (e) {...});window.addEventListener(‘focus’, function (e) {...});
UIウィジェットの操作に伴って発生するもの、
Saturday, March 24, 12
とは限らない。
Saturday, March 24, 12
アプリ内コンポーネントの
連携にもイベントを活用する。
Saturday, March 24, 12
ApplicationLevelEvents
Saturday, March 24, 12
アプリケーションレベルイベント
アプリケーション内でグローバルに有効。
利用にはTi.Appモジュールを使う。
すべてのコンテキスト、関数スコープ、
CommonJSモジュールから参照できる。
Saturday, March 24, 12
Ti.App.addEventListener('settingsUpdated', function(params) { alert('New value: ' + params.newValue);});
Ti.App.fireEvent('settingsUpdated', { newValue: '...'});
ハンドラの設定とイベントの発生
Saturday, March 24, 12
コンポーネント同士がメソッドを呼び合うと、
エラー処理が分散する。
// メモ作成addButton.addEventListener('click', function(e) { if (dataManager.insertMemo(newMemo)) { memoList.append(newMemo); } else { alert('error!'); }});
Saturday, March 24, 12
イベントのやり取りで疎結合に。
メモ作成
Ti.AppdataManager
memoList
Saturday, March 24, 12
イベントのやり取りで疎結合に。
メモ作成 fire memoWillCreate
Ti.AppdataManager
memoList
Saturday, March 24, 12
イベントのやり取りで疎結合に。
メモ作成 fire memoWillCreate
Ti.AppdataManager
memoList
memoWillCreate
Saturday, March 24, 12
イベントのやり取りで疎結合に。
メモ作成 fire memoWillCreate
Ti.AppdataManager
memoList
memoWillCreate
fire memoDidCreated
Saturday, March 24, 12
イベントのやり取りで疎結合に。
メモ作成 fire memoWillCreate
Ti.AppdataManager
memoList
memoWillCreate
fire memoDidCreated
memoDidCreated
Saturday, March 24, 12
エラー時はイベントを発生させない。
メモ作成 fire memoWillCreate
Ti.AppdataManager
memoList
memoWillCreate
fire memoDidCreated×
Saturday, March 24, 12
// メモ作成addButton.addEventListener('click', function(e) { Ti.App.fireEvent('memoWillCreate', { id: memo.id, title: memo.id, body: memo.body });});
メモ作成の際には、データ保存やメモ一覧の更新には関与しない。
Saturday, March 24, 12
// dataManagerTi.App.addEventListener('memoWillCreate', function(memo) { if (insertMemo(memo)) { Ti.App.fireEvent('memoDidCreated', memo); } else { // エラー処理 }});
データ保存失敗時には次のイベントを発生させない。
Saturday, March 24, 12
// memoListTi.App.addEventListener('memoDidCreated', function(memo) { updateMemoList(memo);});
メモ一覧の更新処理はイベントの発生だけを待っていればよい。
Saturday, March 24, 12
注意
アプリケーションレベルのイベントハンドラ内
で生成したオブジェクトは、ハンドラを明示的
に削除するまでメモリに残る。
Managing Memory and Finding Leaks - Documentation & Guideshttps://wiki.appcelerator.org/display/guides/Managing+Memory+and+Finding+Leaks
Saturday, March 24, 12
var doSomething = function() { var foo = new Foo(); // ハンドラを削除するまで残り続ける};Ti.App.addEventListener(‘doSomething’, doSomething);
// 不要になったタイミングでハンドラを削除するTi.App.removeEventListener(‘doSomething’, doSomething);
Saturday, March 24, 12
var win = Ti.UI.createWindow();win.addEventListener('userLoggedIn', function(user) { updateAvatar(user.avatar);});win.open();
var button = Ti.UI.createButton();button.addEventListener(‘click’, function(e) { win.fireEvent(‘userLoggedIn’, { avatar: ‘...’ });});
グローバルなスコープが不要ならば、
ビューオブジェクトにカスタムイベントを設定することも可能。
Saturday, March 24, 12
Summary
まとめ
Saturday, March 24, 12
Scalingを容易にする
コードの書き方
チーム開発
機能の追加・削除
対応デバイスの追加 }Saturday, March 24, 12
What’s Titanium Mobile
最近のTitanium Mobile
コードの分割
underscore.js
CoffeeScript
プラットフォーム依存コードの隠蔽
カスタムイベント
Agenda
Saturday, March 24, 12
コードの分割は常にCommonJSスタイル。
モジュール化で分業が容易に。
プロジェクト間でのコード再利用を促進。
CommonJS
Saturday, March 24, 12
underscore.js配列や関数に対する操作を簡潔に記述。
チーム内での配列操作スタイルを統一。
Saturday, March 24, 12
冗長なコードの削減。
JavaScriptの良い部分だけを利用。
チーム内でJavaScriptのコード品質を統一。
CoffeeScript
Saturday, March 24, 12
iOS/Android判定のコードはできるだけ隠蔽。
関数やモジュールに隠す。
再利用できるノウハウを蓄積。
状況が変わった際の対応を容易に。
プラットフォーム依存コードの隠蔽
Saturday, March 24, 12
コンポーネント間の通信はできるだけイベントで。
エラー処理を集約。
シンプルな処理フロー。
カスタムイベント
Saturday, March 24, 12
Thanks.
@junya
http://bit.ly/scale-ti
Saturday, March 24, 12