Umi Uyuraのブログ

プログラミング関連の作業ログ

iOS/AndroidでiBeaconを扱うTitaniumモジュールについて調べてみた

Titaniumで、iOSAndroid両方でiBeaconを扱うアプリを作ってみようかと思い、それらしいモジュールを探してみました。

さらに、どうせTitaniumでやるのであれば、なるべく処理を共通化できると良いなということで、まずはそれぞれで提供されているAPIを比較してみることに。

対象モジュール

今回使おうとしているのは以下のモジュールです。

プラットフォーム モジュール バージョン オリジナル
iOS Sensimity/TiBeacons 0.11.1 jbeuckm/TiBeacons
Android Sensimity/android-altbeacon-module 1.4.0 dwk5123/android-altbeacon-module

探していたところ、どちらも Sensimity というBeacon Management Systemのサービスを提供している会社の GitHub で公開されているものに行き着きました。

どちらのモジュールもオリジナルは別の人が開発したものなのですが、それらをフォークして改善が加わっているバージョンが公開されていて、さらにBeacon Managementしている会社のものであれば信頼性も高いのではということで、これを使ってみることにしました。

APIを比較

それぞれのモジュールに用意されているAPIを比較。

主要なAPIは同じ構成になっているので、挙動の差異が少なければ、共通化できそう。

それ以外は、直接iBeaconを扱えないAndroid用モジュールに、そのあたりをカバーするためのAPIが用意されている感じ。

API TiBeacons android-altbeacon-module 関連イベント
アドバタイズ開始 TiBeacons.startAdvertisingBeacon() Altbeacon.startBeaconAdvertisement() ※1 advertisingStatus
アドバタイズ停止 TiBeacons.stopAdvertisingBeacon() Altbeacon.stopBeaconAdvertisement() ※1 -
モニタリング開始 TiBeacons.startMonitoringForRegion() Altbeacon.startMonitoringForRegion() enteredRegion, exitedRegion, determinedRegionState
全てのモニタリング停止 TiBeacons.stopMonitoringAllRegions() Altbeacon.stopMonitoringAllRegions() -
ビーコンとの距離測定 TiBeacons.startRangingForBeacons() Altbeacon.startRangingForBeacons()
(Altbeacon.startRangingForRegion())
beaconRanges, beaconProximity
全ての距離測定を停止 TiBeacons.stopRangingForAllBeacons() Altbeacon.stopRangingForAllBeacons() -
BLEサポート有無 TiBeacons.isBLESupported() Altbeacon.isBLESupported() -
BLE有効無効 - Altbeacon.checkAvailability() -
Bluetoothの状態確認 TiBeacons.requestBluetoothStatus() - bluetoothStatus
Beaconサービスへ接続 - Altbeacon.bindBeaconService() -
Beaconサービスへの接続を解除 - Altbeacon.unbindBeaconService() -
Beaconサービスとの接続状態確認 - Altbeacon.beaconServiceIsBound() -
バックグラウンド動作設定 - Altbeacon.setBackgroundMode() -
ビーコンレイアウト追加 - Altbeacon.addBeaconLayout() -
ビーコンレイアウト削除 - Altbeacon.removeBeaconLayout() -
距離測定間隔設定 - Altbeacon.setProximityBounds() -
自動測定を有効化 ※2 Altbeacon.enableAutoRanging() -
自動測定を無効化 ※2 Altbeacon.disableAutoRanging() -
自動測定の有効無効 - Altbeacon.setAutoRange(boolean autoRange) -
サービス内動作設定 - Altbeacon.setRunInService(boolean runInService) -
サービス内動作確認 - Altbeacon.isRunInService() -
測定間隔の設定 - Altbeacon.setScanPeriods(scanPeriods) -

補足

  • ※1 Altbeacon.start(stop)BeaconAdvertisement() は完全にテストされていないもよう
  • ※2 TiBeacons.enable(disable)AutoRanging() はあったが0.8で廃止されたもよう(APIだけ残っている)

イベント

iBeaconを扱う際に発生するイベントは以下のとおり。

イベント名は双方同じものを使っているようなので、この点も挙動の差異次第で共通化できそう。

イベント 内容
advertisingStatus アドバタイズ状態
bluetoothStatus iOSのみ。Bluetooth状態
enteredRegion 領域侵入
exitedRegion 領域退出
determinedRegionState 領域状態を測定
beaconRanges 領域検出
beaconProximity ビーコンとの距離測定結果
changeAuthorizationStatus iOSのみ。パーミッション状態の変化

まとめ

当然細かい挙動は違ってくるのだと思いますが、API構成などはほぼ同じようなので、基本的な流れは共通化できそうな印象。

次は実際に組み込んでみて検証してみる予定。

iBeacon ハンドブック

iBeacon ハンドブック

BacklogのSubversionリポジトリをバックアップ

お仕事ではプロジェクト管理ツールとして Backlog を使っています。

年度末ということもあってプロジェクトの整理をしていたところ、過去に使っていたSubversionリポジトリをバックアップする必要がでてきたので、やり方を調べてみました。

リポジトリをエクスポート

いくつかやり方はあるなかで、もっとも一般的と思われる svnadmin dump をしてみようと思ったのですが。

$ svnadmin dump <REPO_PATH> > <DUMP_FILE>

ひとつ問題があり、それは svnadmin dump 時に指定するエクスポート元のリポジトリは、ローカルのパスでないとダメだということ。

数が多くなければそれでも良いのですが、やはり複数リポジトリをチェックアウトしてダンプして…というのを繰り返すのは面倒なので、できればBacklog上のリポジトリURL( https://<SPACE_KEY>.backlog.jp/subversion/<PROJECT_KEY> )からエクスポートしたいところです。

そこで、Backlogでエクスポートする機能などないのかな、と思って調べてみたところ、それ自体はないものの、FAQでバックアップ方法について紹介されているのを発見しました。

Subversionのリポジトリのエクスポートサービスはありますか? | FAQ | Backlog [バックログ]

紹介されているスクリプトを使うと、内部的に svnsync というミラーリングツールを使って、リポジトリの複製を作ってくれます。

作られた複製はリポジトリのフォルダ構造が再現されている状態なので、そのフォルダをリポジトリパスとして指定することで、 svnadmin dump にてダンプファイルにまとめることもできました。

リポジトリのインポート

念のため、ダンプファイルからの復元手順も確認。

svnadmin create で新しいリポジトリを用意し、 svnadmin load でダンプファイルからインポートすることで、ローカルにリポジトリを復元することができました。

$ svnadmin create <NEW_REPO>                    # リポジトリを作成
$ svnadmin load <NEW_REPO> < <DUMP_FILE>        # ダンプファイルからインポート

ただし、この方法は通常Subversionサーバー上で実施するらしく、少し調べてみましたが、例えばBacklogのような公開されているSubversionリポジトリへ直接インポートする方法は見つけられませんでした。

Backlogでは有料(税抜き30,000円)にてSubversionリポジトリをインポートするオプションを提供しているので、やはりそういった手段はなさそうな印象。

まあ今後はGitを主流に使うでしょうし、バックアップしたSubversionリポジトリは何か合ったときに参照に使う程度なので、今のところ、自分的には問題はなさそうです。

スクリプトをちょっと改良

というわけで、紹介されていたスクリプトをベースに、取り回ししやすいようにダンプファイルを出力して、念のためダンプファイルが読み込めるかを確認するために新しいローカルリポジトリを作成するという処理を追加してみました。

BacklogのSubversionリポジトリをバックアップする

引数にスペースIDとプロジェクトキーを指定して実行すると、ローカルに以下の3つを生成します。

  • <PROJECT_KEY> フォルダ ... svnsyncミラーリングしたSubversionリポジトリの複製
  • <PROJECT_KEY>.dump ファイル ... 上記をもとに出力したダンプファイル
  • <PROJECT_KEY>_CHECK フォルダ ... 上記ダンプファイルをインポートした新しいリポジトリ

<PROJECT_KEY>_CHECK フォルダをsvnXなどで開いてみると、履歴を読み込むことができたので、問題なさそう。

という感じで一通り必要なリポジトリはバックアップできました。

参考

チーム開発実践入門 ~共同作業を円滑に行うツール・メソッド (WEB+DB PRESS plus)

チーム開発実践入門 ~共同作業を円滑に行うツール・メソッド (WEB+DB PRESS plus)

R-BROS. FEST 2016でGRIM SPANKYとLIFE IS GROOVEを観てきました

3/18(木)、ニコ生で放送している「ロック兄弟」という番組のイベント「R-BROS. FEST 2016」に行ってきました。

一昨年くらいに車のCMで流れていたジャニス・ジョプリンの「MOVE OVER」、てっきり外人さんのカバーなのかなーと思っていたんですけど、歌っているのはGRIM SPANKYという日本人のバンドということを知りまして。

それで聴いてみた彼らのファースト・アルバム「SUNRISE JOURNEY」がカッコよく、さらに先日出たミニアルバム「ワイルド・サイドを行け」が5曲ながら名曲ばかりな感じで、最近のお気に入りアーティストのひとつでした。

これはライブで観てみたいな―とウォッチしていたところ、今回のイベントが行けそうだったので、チケットを申し込むことに。

で、そのイベントは対バン形式で、もう一組アーティストが出演するというので名前を見てみたら、なんとLIFE IS GROOVEではないですか!

実はこちらも以前から気になっていて、かつてTBSで放送されていた「さんまのスーパーからくりテレビ」で天才少年ギタリストとして紹介されていた山岸竜之介君が参加しているバンド。しかもメンバーがRISEのベーシストKenKenに、レジェンド・ムッシュかまやつ氏!年齢差60歳、どんな音を出すのか、非常に気になる!


当日は本編開演時間ギリギリに到着したら、オープニングアクトを務めていたarko lemmingというバンドが終わる頃でした。残念。

ステージ準備中は、OKAMOTO'Sのドラマー・オカモトレイジがDJとして会場を盛り上げていました。

「ロック兄弟」の番組MCの方々の諸々が終わって、いよいよGRIM SPANKY登場!

GRIM SPANKYの魅力のひとつは、やっぱりヴォーカル・松尾レミの声だと思いますが、ライブでのシャウトもかっこ良い。あの声にあの音、ドンピシャです。

疾走感のある「ワイルド・サイドを行け」から始まって、コール&レスポンスが盛り上がる「NEXT ONE」、バラード「大人になったら」などなど、少ない曲数でしたが、堪能しました。

それにしても、ほとんど直立で無表情な感じなのに、指がめっちゃ動いてブイブイやっていたベースの人はどなたなんだろう。

でもって、後半はLIFE IS GROOVE!

ドラム、キーボードに2名のホーン・セクションにお三方という編成。

のっけからKenKenのベースがブリブリ鳴って超ファンキー!ボリュームメーターが2、3個あがったような音圧。

山岸竜之介君は、まだ16歳ですかー。なんだかさわやか好青年になっていてビックリ。

そして、そんな2人を見守るというより見守られながら、飄々とマイペースにステージを彷徨うお茶目なムッシュ。

体を動かさずにはいられなくなる感じのバンドサウンドと、ゆる〜い感じのKenKenのMCとの落差もまた面白かった。

さらにアンコール。

GRIM SPANKYとLIFE IS GROOVEがステージにあがると、KenKenが「GRIM SPANKYとぜひこの曲をやってみたかった」といって始まったのは、あのドラム!そう、MOVE OVER!イベントだし、対バン形式で曲数少ないし、まさか演奏するとは思っていなかった!

あの声でMOVE OVER、やっぱりカッコよい。

しかも何がスゴいって、下北沢のジャニスの息子と同じステージにいるんですよね。

その後もスパイダースの「バン・バン・バン」を全員で演奏!そのどさくさに紛れてムッシュにサインをもらうオカモトズ・レイジ(笑)

いやー、とっても濃厚なイベントでした。

GRIM SPANKYは4月に「“ワイルド・サイドを行け”ツアー」をはじめ、イベントにも多数出演予定のようなので、詳しくは 公式サイト をどうぞ。

LIFE IS GROOVEは4/27にシングルが出るそうで、発売記念的なイベントもあるようなので、こっちも LIFE IS GROOVE OFFICIAL SITE をチェック。

どちらも改めて単独公演で観てみたいなー。

f:id:umi-uyura:20160317221019j:plain

ロック兄弟、番組自体は観たことがないけど、最高なイベントをありがとうございました。

ロック兄弟

ワイルド・サイドを行け(初回限定盤)(DVD付)

ワイルド・サイドを行け(初回限定盤)(DVD付)

Generations

Generations

Titaniumもくもく会に参加しました

先週のことですが、久々にTitanium仲間のゆる〜い集まりがありました。

ひさびさ〜と思ったら、去年の Twilio & Titaniumミートアップに行ってきました - Umi Uyuraのブログ 以来ぶりだったんですね。

1月には新年会があったようですが、私は都合があわず参加できなかったので、半年以上ぶりの集まりでした。

Titaniumもくもく会 - Titanium | Doorkeeper

今回の会場は大日本印刷株式会社(DNP)様でした。

以下、発表された方のメモです。

Titanium周りの最新動向 - @kaz_konnoさん

まずは @kaz_konnoさん からTitaniumの最新動向について・・・ということだったのですが、特にないとのこと(笑)

とは言え、やはり年初に飛び込んできたAxway社によるAppceleratorの買収のニュースは驚きでした。

Axway Acquires Appcelerator—And Why This is Great News for All

Axwayという会社についてはよく知らないのですが、エンタープライズ向けの製品をいろいろと持っているところのようです。

まさかTitanium終了か!?という懸念もありましたが、今のところ製品・サービスラインナップはこれまでと変わりなく提供されるようなので、一安心。

Co-FounderのJeffもそういった相手を選んでの決断だったのではないか、ということでした。

Indie foreverプランについて

昨年、Appcelerator Platformという基本有料のサービス体系に変わってから、それ以前にTitaniumアカウントを持っていれば、Indieを永久無料で使えるというプランが期間限定で提供されていました。

このプランへの切り替えは、本来は昨年の6/30までに以降手続きが必要で、もう終了しています。

ただ、この切替方法が少々わかりにくく、実は新しいアカウントに切り替わった時点では Developer プランという状態で、アカウントの設定から Indie に手動でプランを変更しないといけないのです。

これに気づかずに Indie へ切り替えそこねてしまった人も多いようで、期限を過ぎてしばらくは直接サポートに連絡するなどしてIndieに切り替えてもらったようなケースもあるようですが、その救済措置も終了ということのようです。

Developer Portal

Developer向けのポータルページが新しくなりました。

Appcelerator Developer

最新情報は Appcelerator Developer Updates というページに集約されているようなので、Titanium/Appcelerator Platformに関して情報が欲しい人は、定期的にこちらをチェックすると良いでしょうとのこと。

公式のDeveloper Blogや、Webinarやチュートリアル動画を掲載しているAppcelerator University、そしてMediumに投稿されたTitanium関連の記事をピックアップしているAll Titaniumというコーナーがあります。

以前あったQ&Aフォーラムについては、Stack Overflow上でサポートに移行したそうです。

Newest 'appcelerator' Questions - Stack Overflow

投稿する際は appcelerator タグを付けておくと、Appceleratorの人も見てくれているようです。

ただ上記は英語のサイトになってしまうので、日本語でサポートが必要な方は Titaniumユーザー会のサポートBBS へ投稿するか、Twitterハッシュタグ #TitaniumJP を付けて投稿すると、どなたか拾ってくれるかもしれません。

最新のSDK情報

最新のSDK 5.2.0がリリースされていて、3/10の時点で5.2.1のRCも出ています。

SDK 5.2.0で提供される新機能などは、以下のラップアップ記事にある動画が非常にわかりやすいので、サンプルと合わせて見てみると良いと思います。

Titanium Mobile DNPの取り組みについて - DNP山下さん

続いて、会場を提供していただいた大日本印刷株式会社の山下さんから、DNPのTitanium Mobileへの取り組みについて紹介していただきました。

DNPでは、特に2011〜2012年ころ、Titanium meetup TokyoやTitanium mobile 2.0ローンチ記念イベントなどで会場提供などでTitaniumコミュニティ活動を支えてくれていました。

DNP自体もネイティブ以外の開発プラットフォームも幅広く取り入れているということで、そのひとつとしてTitaniumも実践活用されているとのこと。

法人向け業務支援やカスタマイズアプリなど、特にタブレット向けアプリのUI構築などで使っていて、分野としてもサイネージやアンケート、コンシェルジュ、教育など多岐に渡るそう。

最近の事例としては、京都のギャラリーなどで鑑賞補助用のアプリに使っていたり、DNPオリジナルタブレット向けにAndroidホームアプリなどに使っているそうです。

TitaniumでAndroidのホームアプリも作れるというのは知らなかったので、ちょっとやってみたい。

今後は、Titanium×Windowsも視野に入れて研究中ということで、特にWindowsタブレットをターゲットに活用していきたいということでした。

ただ、このあとのWindows開発の発表にもありましたが、SDK 5.2.1時点ではまだまだ使い物にならない状態ということで、前途多難な道のりにはなりそうな感じ。

最近は、特に企業から改まってTitaniumの事例などを耳にすることは少ないので、適材適所を見極めて活用されているところがあるというのを聞けたのは心強いですね。

faster-titanium - @shinoutさん

続いて CureApp@shinoutさん の発表は、TiShadow/LiveViewを凌ぐ新たな爆速開発ツール「faster-titanium」の紹介。

どういうものかは、GitHubリポジトリのアニメーションGIFを見ていただくとイメージしやすいかと思います。

CureApp/faster-titanium: Accelerate Titanium development

TiShadowやLiveViewは便利ですが、落ちやすかったり、なんだか動かないということが多かったので、LiveViewをベースにしつつ、中身をES6ベースにして徹底的にリファクタリングしたそうで、安定性とReact Native並のリロード速度を実現されたそうです。

現状はiOSでしか動いていませんが、ベースとなるLiveViewがAndroidについてはできているので、同様にできるであろうということと、のちのちはWindowsにも対応していきたいという話でした。

Titaniumの特徴として、各プラットフォーム向けのUIをJavaScriptAlloyフレームワークなど簡略化された構文で構築できる点にあると思いますが、どうしてもビルドして動作確認という部分に時間がかかってしまいます。この部分がfaster-titaniumで短縮できると、さらに開発効率があがるので、Titaniumの強みがより活かせるツールだと思います。

まだ出来たてということもあり、絶賛フィードバック募集中ということなので、ぜひ使って行きたいと思います。

Titanium x Windows開発 - @flat_8_kikiさん

最後は @flat_8_kikiさん による、TitaniumによるWindowsアプリ開発についての発表。

もはや涙無くしては語れない物語で、メモを取る手も止まってしまうほどでした。

この過酷な闘いについてはQiitaに記録されていますので、ぜひご覧ください。

そもそもユニバーサルWindowsアプリ開発自体の情報もまだまだ少ない気がするので、そういった点でも貴重な情報だと思います。

SDK 5.4.xおよび5.5.xあたりで改修されている見込みのようなので、最低限の開発ができるようになるのは、その頃からという感じでしょうか。

まだまだ闘いは始まったばかりということで、ガンガン地雷を踏み抜いてくださる覚悟ということなので、今後のWindows×Titaniumの進展も期待できそうです。

※2016/03/25追記

資料公開していただいたので、追記しました。

おわり

終わってみると、非常に濃い話が多くてもくもくどころじゃなかった会でした。

最近は同じマルチプラットフォーム開発でも、React NativeやMSに買収されたXamarinなどに話題を持って行かれている感がありますが、Titanium自体も着実に進化していて、実績という点では他に負けていない製品だと思います。

そして表に出てきてはいないけど、実は使っているという人もけっこういるようなので、そういった人にもぜひもくもく会などに遊びに来ていただいて、情報交換させてもらえると嬉しいですね。

Google Play Musicにアップロードした楽曲を再ダウンロードする

定額の音楽配信サービスは、Google Play Musicを使っています。

umi-uyura.hatenablog.com

特に邦楽は聴きたいアーティストの作品がないことも多いわけですが、Google Play Musicには、手元の音源をアップロードしておくことで、登録しているデバイスに同期して聴くことができる機能があります。

Google Play ミュージック マネージャで音楽を追加する - Google Play ヘルプ

アップロードは50,000曲までできるということなので、ざっくりアルバム4,000〜5,000枚分くらいと考えても、自分の持っているCD全て入ってしまうくらいアップできます。(ちゃんと数えたことないけど、たぶん500枚くらい?多くても1,000枚はない程度)

それならGoogle Play Music上に自分のライブラリを集約させてしまうことも可能なわけですが、もしそうした場合に気になるのが、アップロードした楽曲を再び手元にダウンロードすることはできるのか?ということ。

これができるのであれば、バックアップとしても使えるので、自分的に使い勝手があがるかもと思い調べてみたところ、ちゃんと?その手段が用意されていました。

設定画面の ライブラリのダウンロードダウンロード ボタンを押すと…

f:id:umi-uyura:20160314235849p:plain

おもむろにダウンロードが始まりました。

f:id:umi-uyura:20160314235900p:plain

途中でブラウザを閉じるなどしても、またログインするとダウンロードが再開されるようで、進捗状況は左下に表示されています。

f:id:umi-uyura:20160314235912p:plain

ダウンロードされる場所は、設定画面(スクリーンショット一枚目)にある ダウンロードフォルダ で指定できます。

個別ダウンロードはできない

残念ながら、今のところアップロードした楽曲を個別にダウンロードする手段はなさそうです。

もし50,000曲をアップロードしていたと仮定すると、1曲5MBとしても250GBにもなってしまうので、これを一括ダウンロードしかないとなると不便。

とは言っても、それだけの曲数をアップロードするような頃には何かしら改善されていそうな気もしますし、それまで使い続けているかもわからないので、今のところは問題なし。

Alloy Modelの学習・その4(REST API)の続き

前回、REST APIのData Bindingを使ってみたのですが、

umi-uyura.hatenablog.com

そのときに課題として残していたエラーハンドリングなどレスポンスに対する処理に関してと、開発中にAPIエンドポイントを分離したい場合の方法についてメモ。

レスポンスに対する処理

napp.alloy.adapter.restapi を使う際の、エラー処理を含むレスポンスの処理方法を調べてみました。

napp.alloy.adapter.restapiのリポジトリに掲載されている例では引数を受け取っていませんでしたが、各メソッドでは、以下のように successerror のコールバックを受け取ることができます。

function doOpen() {
  memos.fetch({
    success : function(models, response){
      Ti.API.debug('memo fetch() - success = ' + models + ' / ' + response);
    },
    error : function(models, response){
      Ti.API.debug('memo fetch() - error = ' + models + ' / ' + response);
    }
  });
}

successerror コールバックは、サーバーからのレスポンスのHTTPステータスコードによって判断されて呼びだされるため、基本的に意識する必要はありません。

受け取ったモデルの加工だったりエラーに対する処理のほか、通信中のローディング画面の制御などもここでハンドリングできますね。

コールバックの形

操作対象がCollectionなのかModelなのかによって、コールバックには大きく2種類の形があるようです。

メソッド コールバック コールバックの形
Collection.fetch() success
error
function(models, response)
Model.save()
Model.destroy()
success
error
function(model, response)

各コールバックの第1引数は、主に操作したモデルデータを受け取るようです。

次に、共通している response には、Titanium.Network.HTTPClientの responseText が入ってくるようです。通信をJSONでやりとりする場合は、必要に応じて JSON.parse() するなどして扱うことになると思います。

コールバックの形を見るに、 responseText 以外の情報は受け取っていないようなので、例えばもしアプリ側でもHTTPステータスコードが必要な場合は、あらかじめレスポンスボディに含めるなどする必要がありそうです。

iOSAndroidでエンドポイントを分ける

特にPCローカルのサーバーと、iOSAndroidのシミュレータ/エミュレータで通信確認をする場合のメモです。

iOSのシミュレータでは 127.0.0.1 で開発しているMac側で立ち上げているAlloyModelStudyRestStubにアクセスできるのですが、AndroidエミュレータやGenymotionの場合、 127.0.0.1 は端末自体を示すらしく、 10.0.2.210.0.3.2 といったIPになるようです。

モデルの URL を書き換えれば対応できますが、その都度実施するのも面倒なので、あらかじめ config.json に設定しておき、そのURLを使うようにしました。

app/config.json

{
  ...
  "os:android env:development": {
    "API_URL": "http://10.0.3.2:4321/memos"
  },
  "os:ios env:development": {
    "API_URL": "http://127.0.0.1:4321/memos"
  },
  ...
}

config.json で設定できる値は、 os:androidos:ios といったプラットフォームをあらわすオブジェクトに、動作環境をあらわす env:development などを組み合わせることで、「Androidエミュレータの場合」「iOSエミュレータの場合」に使う値、というようにバリエーションをつけることができます。

それをモデルの中で参照することで、環境ごとに異なるURLを使えるようになりました。

app/models/memo.js

exports.definition = {
  config: {
    columns: {
      "id": "integer",
      "contents": "text",
      "priority": "text"
    },
    adapter: {
      type: "restapi",
      collection_name: "memo",
      "idAttribute": "id"
    },
    "URL": Alloy.CFG.API_URL,           // <== config.jsonに定義したURL
    "debug": 1
  },
  ...
};

env:production などと組み合わせれば、本番環境向けビルドで使うURLも設定できます。

なお、以前はモデルの中でグローバルな Alloy オブジェクトを参照できなかったので、こちらの記事( AlloyのModelでREST APIからデータを取得する - Qiita )のように一手間必要だったようですが、Alloy 1.4.0?あたりから参照できるようになったようです。

Backbone.js的な対処方法

モデル側のURLの指定を変更する方法とは別に、Backbone.js的に、モデル操作時にオプションとしてURLを渡すという対処方法もあります。

その場合は、 Collection.fetch({url: <url>})Model.save(<params>, {url: <url>})Model.destroy({url: <url>}) といったあたりのメソッドを使えば良いようです。

エンドポイントを動的に変更するようなケースがあれば、使いみちがあるかもしれません。

まとめ

APIのレスポンスに関しては、自前で用意するAPIであれば設計段階で調整できますが、既成サービスのものなどを使う場合はいじりようがないので、実戦使用するときに、また色々と検証しようと思います。

Appcelerator Titanium Smartphone App Development Cookbook - Second Edition

Appcelerator Titanium Smartphone App Development Cookbook - Second Edition

Alloy Modelの学習・その4(REST API)

引き続き、Alloy Modelを勉強中。

umi-uyura.hatenablog.com

クロスプラットフォームなUIを素早く構築しつつWeb APIと連携するというのは、Titaniumが活用できるパターンだと思います。

というわけで、今回はREST APIとのData Bindingを試してみました。

REST API Sync Adapter

標準で用意されているSync Adapterは、SQLite用のSql AdapterとTitanium.App.Propertiesを使うProperties Adapterの2種類しかありませんが、Sync Adapterは自作することもできます。

Custom Sync Adapters - Alloy Sync Adapters and Migrations

REST API用のSync Adapterを作ってくれている人がいますので、これを採用することにします。

viezel/napp.alloy.adapter.restapi

今回作るもの

アプリ自体は簡単にするため、 その2 で作ったものと同じ仕様とします。

それとは別に、連携先のAPIが必要です。

適当なWeb APIを使っても良かったのですが、CRUDを一通り触りたかったのと、サーバー側に渡ってくるリクエストの内容も把握したかったので、今回はスタブAPIサーバーを立てることにしました。

スタブAPIサーバー

スタブAPIサーバーについては、シンプルで使いやすそうだったMockyを使ってみることにしました。

okv/mocky: http mocking server with simple config written on nodejs

テーブル

スタブなので実際のデータベースは用意しませんが、 その2 と同じ仕様ということで、以下のようなテーブル構造を扱うAPIを用意します。

内容 カラム
レコードの識別子 id INTEGER
メモの内容 contents TEXT
メモの優先度 priority TEXT

API

上記のテーブルをもとに、CRUDをひととおり呼び出せるよう、以下のようなAPIにすることにしました。

操作 Method URL
メモを読み込む GET http://127.0.0.1:4321/memos
メモを追加 POST http://127.0.0.1:4321/memos
メモの内容を更新 PUT http://127.0.0.1:4321/memos/{ID}
メモを削除 DELETE http://127.0.0.1:4321/memos/{ID}

スタブAPIPサーバー自体の実装は省略しますが、ダミーデータをメモリ上に保持しておいて、APIの呼び出しに合わせてダミーデータを操作しています。

例えばメモの読み込みAPIを叩くと、こんな感じにJSONのレスポンスを得られる感じです。

f:id:umi-uyura:20160227114353p:plain

データベースを使っていないので、サーバーを再起動するとデータは初期状態に戻ります。

ソースは umi-uyura/AlloyModelStudyRestStub にありますので、こちらをご覧ください。

Qiitaにあった mocky を使ってちょっと賢いスタブAPIサーバを作る - QiitaNode.js の mocky を使用し スタブREST API を作成してみる、その1 - Qiita あたりを読めば簡単に作れました。

アプリ

アプリ側のソースは umi-uyura/AlloyModelStudyRestApi です。

プロジェクト作成

毎度おなじみCLIでプロジェクト作成。

$ titanium create --type app --id com.example.titanium.alloymodelstudyrestapi --name AlloyModelStudyRestApi --platforms iphone,android --url http://www.example.com --workspace-dir .
$ alloy new AlloyModelStudyRestApi

これをベースに、今回もUIはJade & Stylusに、FokkeZB/UTiL/xp.ui を使っています。

REST API Sync Adapter (napp.alloy.adapter.restapi) の準備

GitHubリポジトリから restapi.js をダウンロードして、プロジェクト内の app/assets/alloy/sync/ へ配置しておきます。

モデル作成

先の想定テーブル構造から、ベースとなるモデルを生成します。このとき、 restapi タイプというのは Alloy コマンドがサポートしていないため、いったん sql として生成しました。

$ alloy generate model memo sql 'id:integer' contents:text priority:text

生成されたモデルファイルをもとに、REST API用に編集します。

exports.definition = {
  config: {
    columns: {
      "id": "integer",
      "contents": "text",
      "priority": "text"
    },
    adapter: {
      type: "restapi",
      collection_name: "memo",
      "idAttribute": "id"
    },
    "URL": "http://127.0.0.1:4321/memos",
    "debug": 1
  },
  extendModel: function(Model) {
    _.extend(Model.prototype, {
    });
    return Model;
  },
  extendCollection: function(Collection) {
    _.extend(Collection.prototype, {
      comparator: function(data) {
        return (0 - data.get('priority').length);
      }
    });

    return Collection;
  }
};

編集した箇所としては、以下のとおりです。

  • config.adapter.typerestapi に変更
  • URL を追加し、スタブAPIのURLを設定
  • debug を追加、これでAPIを使う際のリクエスト/レスポンスがデバッグ出力されてわかりやすい

他に headersparentNode をといった設定もありますが、今回は未使用。

以下はデバッグ出力の例。

f:id:umi-uyura:20160227114411p:plain

Data Binding

ビューの部分は 前回 と同じもので、コントローラもほぼ一緒で動きました。

一箇所修正したのは、メモを追加したあとに呼び出していた memos.fetch() の部分で、通信が発生してタイミングが変わるせいか、これを呼び出してしまうと追加したはずのメモが消えてしまう現象が発生しました。

その上の行の memos.add(m) までで、ListViewには新しいメモが追加されていたので、 memos.fetch() は削除することにしました。

app/controllers/index.js

...

function addText() {
  var txt = $.memoText.getValue();
  var params = {
    contents: txt,
    priority: ''
  };

  var m = Alloy.createModel('memo', params);

  m.save();
  memos.add(m);
  //memos.fetch();      <== 削除した

  $.memoText.setValue('');
}

...

エラーハンドリングについて

今回やっていないこととして、通信のエラーハンドリングがあります。

データ構造を同じにしたこともありますが、SQL用からREST API用にAdapterを入れ替えて、ほぼそのまま動かせてしまいました。

しかし、ローカルにあるデータベースなどと比べて、インターネットを介した通信は不安定だったり、APIサーバー側で何らかの問題が発生することなども考えられます。

そのため、napp.alloy.adapter.restapiには、以下のような感じで通信成功時とエラー時で処理をハンドリングできるようになっています。

var collection = Alloy.createCollection("MyCollection"); //or model
//the fetch method is an async call to the remote REST API.
collection.fetch({
    success : function(){
        _.each(collection.models, function(element, index, list){
            // We are looping through the returned models from the remote REST API
            // Implement your custom logic here
        });
    },
    error : function(){
        Ti.API.error("hmm - this is not good!");
    }
});

napp.alloy.adapter.restapiを使ってきちんと実装する場合は、この点も考慮する必要があると思います。

参考

モックサーバー

Appcelerator Titanium Smartphone App Development Cookbook - Second Edition

Appcelerator Titanium Smartphone App Development Cookbook - Second Edition