Titanium MobileアプリとMongoDBをSleepy.Mongooseを経由で接続させてみた

MongoDBSleepy.Mongoose経由で、Titanium Mobileで作ったiPhoneアプリから接続する簡単なアプリを作ってみました。

Sleepy.Mongooseを経由したのは、MongoDBのRESTインターフェースは読み取り専用で外部ツールを使うことが推奨されていたからです。*1

The mongod process includes a simple read-only REST interface for convenience. For full REST capabilities we recommend
using an external tool such as Sleepy.Mongoose.

MongoDB、Sleepy.MongooseそしてTitanium Mobileのインストールや設定などにつきましては、それぞれの公式サイトに書いてありますので、割愛させていただきます。あえて注意する事と言えば、MongoDB起動時「--rest」オプションを間違って付けないことでしょうか。

Todoアプリ

タスクの一覧(query)を表示・追加(insert)をするだけのアプリです。あくまでも勉強用なので、実用性は全くありません。また、Titanium Mobileを使ってHello World以外を作ったのもこれが初めてなので、変なコードを書いていたらどうかご勘弁を。


ソース

Titanium Developerを起動し、New ProjectでTodoプロジェクトを作成。Titanium SDKは1.5.1、iPhone SDKは4.2を選択しました。MongoDB、Sleepy.Mongoosそして本アプリは、すべてローカルで動かすことを前提にしています。


APP_ROOT/Resources/app.js

Ti.UI.setBackgroundColor('#000');

var win1 = Ti.UI.createWindow({  
  url: 'table_view.js',
  title:'List',
  backgroundColor:'#fff'
});

var tab1 = Ti.UI.createTab({
  window:win1
});  
win1.hideTabBar();

var tabGroup = Ti.UI.createTabGroup({});
tabGroup.addTab(tab1);  
tabGroup.open();


タスクを追加するウィンドウ「taskWindow」に「done」というカスタムイベントを追加しているのは、「close」イベントを追加すると、「Back」ボタンをタップしたのか「Done」ボタンをタップしたのかわからないからです。

「Done」ボタンをタップしてタスクを追加したら、MongoDBからオブジェクトIDが戻りますので、doneイベントを発動する際、引数にこのIDを渡して、一覧を更新する方がいいんですが、面倒なんで全部再取得してtableViewに詰め込んでます。


APP_ROOT/Resources/table_view.js

var win = Ti.UI.currentWindow;
var xhr = Ti.Network.createHTTPClient();

var tableView = null;
function updateTable() {
  var find = 'http://localhost:27080/todo/tasks/_find?sort=' +
             encodeURIComponent('{"_id": -1}');
  xhr.open('GET', find);
  xhr.onload = function() {
    var todo = JSON.parse(this.responseText);
    var tasks = todo.results;
    var data = [];
    for(var i = 0; i < tasks.length; i++) {
      var row = Ti.UI.createTableViewRow();
      var label = Ti.UI.createLabel({
        text: tasks[i].text
      });
      row.add(label);
      data.push(row);
    }

    if(tableView == null) {
      tableView = Ti.UI.createTableView({
        data: data
      });
      win.add(tableView);
    } else {
      tableView.data = data;
    }
  };
  xhr.send();
}

var taskButton = Ti.UI.createButton({
  systemButton: Ti.UI.iPhone.SystemButton.ADD
});
taskButton.addEventListener(
  'click',
  function() {
    var taskWindow = Ti.UI.createWindow({
      url: 'task_window.js',
      title: 'Add Task',
      backgroundColor: '#fff'
    });
    taskWindow.addEventListener(
      'done',
      function() {
        updateTable();
      }
    );
    Ti.UI.currentTab.open(taskWindow);
  }
);
win.rightNavButton = taskButton;

var conn = 'http://localhost:27080/_connect';
xhr.open('POST', conn);
xhr.onload = function() {
  updateTable();
};
xhr.send({"data":"localhost:27017"});


APP_ROOT/Resoruces/task_window.js

var win = Ti.UI.currentWindow;

var textArea = Ti.UI.createTextArea({
  height: 180,
  width: 300,
  top: 10,
  font:{
    fontSize:20
  },
  borderWidth: 1,
  borderColor: '#bbb',
  borderRadius: 5,
  autocapitalization: Ti.UI.TEXT_AUTOCAPITALIZATION_NONE,
  suppressReturn: false
});
win.addEventListener('open', function(e) {
    textArea.focus();
});
win.add(textArea);

var doneButton = Ti.UI.createButton({
      systemButton: Ti.UI.iPhone.SystemButton.DONE
});
doneButton.addEventListener(
  'click',
  function() {
    if(textArea.value) {
      var url = 'http://localhost:27080/todo/tasks/_insert';
      var xhr = Ti.Network.createHTTPClient();
      xhr.open('POST', url);
      xhr.send({"docs": '[{"text": "' + textArea.value + '"}]'});
      xhr.onload = function() {
        win.fireEvent('done');
        win.close();
      };
    }   
  }
);
win.rightNavButton = doneButton;

動作確認

Titanium Developer経由でiPhoneシミュレータを起動し、タスクを追加。MongoDBのシェルでinsertされているか確認。試しに、日本語の文章をテキストエリアに貼付けて追加してみましたが、文字化けはしませんでした。なお、Androidのemulatorでは動きません。

$ mongo todo
MongoDB shell version: 1.6.5
connecting to: todo
> db.tasks.find().sort({id:-1})
{ "_id" : ObjectId("4d5b3b3a5a7a992baf000005"), "text" : "Go to Ginza." }
{ "_id" : ObjectId("4d5b3b4d5a7a992baf000006"), "text" : "Visit my girl friend." }
{ "_id" : ObjectId("4d5b50045a7a993380000000"), "text" : "日本語のテスト" }

余談

JavaScriptiPhone/Androidアプリが作れちゃうってすごいですね。

このアプリもそうですが、やはりプラットフォーム固有の設定・動作があり、Googleでドキュメントを検索していた時、if文で分岐させているソースを見ました。でも、例えばretainCountを気にしなくてよいとか、Objective-Cでを書いてた時と比べたら格段に楽かなぁ...

MongoDBのスケーラビリティについては、また後日、時間をとって勉強してみたいと思っています。セキュリティまわりは、MongoDBにユーザ認証がありますし、Sleepy.MongooseはSSLに対応してますから大丈夫なんじゃないかと。ただ、Titanium.Network.HTTPClientHTTPSに対応しているのかは未確認です。