最近、技術系のネタをQiitaに集中していたのですが、ブログのアクセス解析の状況見てたらTitanium Mobile alloy」「ACSとは」 という意図したもので流入しはじめてきたので、Alloyネタをこっちにも書いておきます。
こんな感じの簡単Qiitaビューワーアプリを Alloy + napp.alloy.adapter.restapiで作る方法について簡単に解説します
自分の環境
- OS X
- 10.9.5
- Titanium SDK
- 3.4.0.GA
- XCode
- 6.0.1
- Node.js
- v0.10.13
- alloy
- 1.4.1
- CoffeeScript
- 1.7.1
開発前の事前準備
Alloy用の RestAPI Sync Adapter の配置
- PROJECT_FOLDER/app/assets配下に、alloyフォルダを作成して、その中にsyncフォルダを作成します
- Alloy用の RestAPI Sync AdapterというのがGitHubにあるのでダウンロードして、上記作成したsyncフォルダに配置します。最終的には、PROJECT_FOLDER/app/assets/alloy/sync/restapi.js
alloy.jmkの準備(任意)
自分はCoffeeScriptで書くことが多いので、PROJECT_FOLDER/app/alloy.jmkを作成して、以下のように記述してます。
task("pre:compile", function(event,logger) { logger.info('pre compile start!'); var wrench = require("wrench"), fs = require("fs"), path = require("path"), controller_root = event.dir.controllers, model_root = event.dir.models, 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 })); } }); wrench.readdirSyncRecursive(model_root).forEach(function(model){ if (model.match("coffee$")) { fs.writeFileSync( path.join(model_root,model.replace("coffee", "js")), coffee.compile( fs.readFileSync(path.join(model_root, model)).toString(), { bare: true })); } }); }); task("post:compile",function(event,logger){ logger.info('compile finish'); });
PROJECT_FOLDER/appのフォルダ構成で関連しそうなところだけ抜粋すると以下のようになります。
├── alloy.jmk ├── alloy.js ├── assets │ ├── alloy │ │ └── sync │ │ ├── restapi.js │ ├── android │ ├── blackberry │ ├── iphone │ └── mobileweb ├── config.json ├── controllers │ ├── index.coffee │ └── index.js ├── models │ ├── qiita.coffee │ └── qiita.js ├── styles │ └── index.tss └── views └── index.xml
実際の開発の流れ
WebAPIと連携するアプリ開発はTitaniumの得意な所かと思うのですが、Alloy用の RestAPI Sync Adapterを使うことで、これまで以上に開発が捗るかと想います。
実際の開発手順を順番に示していきます。
QiitaのWebAPIにアクセスするためのModelの開発
RestAPI Sync Adapterを使うことで、Web APIとの連携処理がだいぶスッキリ書けるようになります。
Alloy用の RestAPI Sync AdapterのHow To Useを見ればおおまかに利用方法がわかるかと思いますがシンプルにアクセスするだけというような状況でしたら
- Web APIのエンドポイントURLを指定
- collection_nameを指定
という所がポイントになります。
今回は、
- Web APIのエンドポイントURL
- collection_name
- qiita
という形にして、app/models/qiita.coffeeを以下のように作成しました。
exports.definition = config: URL: "https://qiita.com/api/v1/items" adapter: type: 'restapi' collection_name: 'qiita' extendModel: (Model) -> _.extend Model.prototype, () -> return Model extendCollection: (Collection) -> _.extend Collection.prototype, () -> return Collection
注)Modelのファイル名とcollection_nameは同じ形にしておかないとエラーになります。
Controllerを作成
Controllerから上記作成したModelを呼び出すには
qiitaItems = Alloy.createCollection 'qiita' qiitaItems.fetch success: () -> # 任意の処理 error: () -> # エラーの時の処理
という形にします。今回は簡易的なビューワーアプリを目指して作ったので、QiitaのWebAPIからパブリックな投稿情報を取得して、TableView使って表示することにしたので以下のようにしました。
$.index.open() $.activityIndicator.show() qiitaItems = Alloy.createCollection 'qiita' qiitaItems.fetch success: () -> $.activityIndicator.hide() _stringify = JSON.stringify(qiitaItems) # 注1 items = JSON.parse(_stringify) return refreshMainMenu items error: () -> $.activityIndicator.hide() Ti.API.info "error" # Qiitaから取得したデータをTableViewにセットする処理 refreshMainMenu = (items) -> rows = [] for item in items Ti.API.info item.title row = $.UI.create "TableViewRow", classes: "itemRow" data: item titleLabel = $.UI.create "Label", text:item.title classes: "titleLabel" bodyLabel = $.UI.create "Label", text:item.raw_body classes: "body" row.add titleLabel row.add bodyLabel rows.push row return $.mainMenu.setData rows
注1
本来はstringify→parseは全く意味がない処理なんですが、restapi Adapter使うと何故かわからないのですが、JSONのパース処理でエラーになります。
何か不要な文字か制御コードが含まれているようなのですがJSON.stringifyで文字列化して、その文字列化したものをJSON.parseすることで回避できます。
Viewの生成
取得した投稿情報を表示するためにXMLとTSSを以下のように準備しました。
index.xml
<Alloy> <TabGroup> <Tab id="tabOne"> <Window id="mainWindow" class="container" title="Qiita"> <ActivityIndicator id="activityIndicator" message="Loading.." /> <TableView id="mainMenu" /> </Window> </Tab> </TabGroup> </Alloy>
index.tss
"#mainWindow":{ statusBarStyle:0, translucent:false, navTintColor:"#0066ff", backgroundColor:"#fcfcfc", tabBarHidden:true } "#mainMenu":{ backgroundColor:"#fcfcfc", separatorColor: '#cccccc', width:Ti.UI.FULL, height:Ti.UI.FULL, left:0, top:0, zIndex:1 } "#activityIndicator":{ top:"50%", left:"20%", textAlign:'center', backgroundColor:"#222", font:{ fontSize:18 }, color:'#fff', zIndex:10 } ".itemRow":{ width:Ti.UI.FULL, height:"15%", hasChild:true, backgroundColor:"#fcfcfc" } ".titleLabel":{ width:"90%", height:"20%", top:"5%", left:"5%", textAlign:'left', color:'#59BB0C', font:{ fontWeight:'bold', fontSize:16 } } ".body":{ width:"90%", height:"70%", top:"25%", left:"5%", textAlign:'left', color:'#222', font:{ fontSize:12 } }
最後に
QiitaのようなWeb APIや、Titaniumの開発元が提供してるACSと連携するようなアプリ開発をする場合のサーバーとの通信部分の処理でAlloy+napp.alloy.adapter.restapiを使うことですごくスッキリ書けるように思います。
投稿数を絞って取得したい場合にはクエリーパラーメーターを付与する必要があるのですが、その辺りについてはまた別の機会に書きたいと思います。