[オフラインで使いたい]スマホで Indexed Database API まだですか [iOSに実装はいつ?]

2013
05.09
このエントリーをはてなブックマークに追加

皆様、おはようございます。今回は Indexed Database API についてです。

そう言えば今年は蛇年ですね、蛇。

最近すっかり見なくなってしまいましたが、昔はシマヘビなんかを見かける度に追いかけ回したものです。

 

Indexed Database API ってどうなの?

さて、久々にガッチリと プログラム関係の技術情報 で行きたいと思います。初心忘れるべからずです。

当社は今年も引き続きスマホやタブレットの web アプリを推して行きたいと思っているのですが、必ずネックになる部分があります。

それはやはり「 ローカルでデータベースを操作する 」、これでしょう。

当サイトでも色々検証しました マニフェスト と組み合わせれば、オーディオ / ビデオ系の再生を除けばかなり ネイティブアプリ のようにゴキゲンな動作をするアプリが作成可能になります。

 

黎明期は JS から「 SQLite 」を扱う事が出来る Web SQL Database API 一択だったので、何も悩むこと無く使っていたのですが、 2010 年だか 2011 年だかにこの API が HTML5 の仕様策定範囲から外されてしまいました。(出典 : Livecast 様)

よって去年( 2012 年)は今後メンテナンスが無く、いつ消えても文句は言えない状態の Web SQL Database API なんて使ってらんないぜ!推奨されている Indexed Database API ってのに乗り換えよう!

と色んな解説サイトを巡りながらサンプルを作ってみたのは良かったのですが…

iOS で動かないのよ、コレが。

http://labs.vividworks.jp/indexedDB/

Indexed Database API

プログラムの中身はこんな感じ

※jQueryを読み込んでます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
var IDB = window.indexedDB || window.webkitIndexedDB;
var cursorIDB = window.IDBCursor || window.webkitIDBCursor;
var transactionIDB = window.IDBTransaction || window.webkitIDBTransaction;
var keyrangeIDB = window.IDBKeyRange|| window.webkitIDBKeyRange;
var DB_NAME = 'TEST';
var DB_VERSION = '1.0';
var DB_TODO = 'todo';
var DB_SORT = false;
var odb;

alert(IDB);

//=================================================
// DBのバージョン別 Object Store 初期化オブジェクト
//=================================================
var DB_OBJECT_STORES = {
    '1.0': function(odb) {
        var objectStore = initObjectStore(odb, DB_TODO, { keyPath: "id", autoIncrement: true });
        objectStore.createIndex('flg', 'flg');
    }
};

//=================================================
// Object Storeのイニシャライズ
//-------------------------------------------------
// DBスキーマが変更されていたら既存の Object Storeを破棄して再生成する。
// 存在し無い場合は新規で生成する。
//-------------------------------------------------
// @param:  object
// @param:  string
// @param:  object
//-------------------------------------------------
// @return: object
//=================================================
var initObjectStore = function(odb, name, option) {
    var check = odb.objectStoreNames;
   
    for(var key in check) {
        if(check[key] == name) odb.deleteObjectStore(name);
    }
   
    var objectStore = odb.createObjectStore(name, option);
    return objectStore;
};


//=================================================
// DBのバージョンを比較
//-------------------------------------------------
// @param:  string
// @param:  string
//-------------------------------------------------
// @return: bool
//=================================================
var checkVersion = function(current, latest) {
    if(current != latest) return false;
    else return true;
};


//=================================================
// DBオープン
//-------------------------------------------------
// @param:  object
//=================================================
var openDB = function(callback) {
    var req = IDB.open(DB_NAME);
   
    // オープンに成功
    req.onsuccess = function(event) {
        alert("success");
        odb = req.result;
        var current = odb.version || 0;
        var latest = DB_VERSION;
       
        if(!checkVersion(current, latest)) {
            odb.setVersion(latest).onsuccess = function(event) {
                for(var key in DB_OBJECT_STORES) DB_OBJECT_STORES[key](odb);
                typeof callback == "function" && callback();
            };
        } else {
            typeof callback == "function" && callback();
        }
       
        getTodo(function(todo) {
            showTodo(todo);
        }, DB_SORT);
    };
   
    // オープンに失敗
    req.onerror = function(event) {
        alert("error");
    };
};


//=================================================
// ObjectStore の取得
//-------------------------------------------------
// @param:  string
// @param:  string
//-------------------------------------------------
// @return: object
//=================================================
var getObjectStore = function(name, mode) {
    var transaction;
   
    switch(mode) {
        case 'READ_WRITE': transaction = odb.transaction(name, transactionIDB.READ_WRITE);  break;
    }
   
    return transaction.objectStore(name);
};


//=================================================
// データ書き込み
//=================================================
var putTodo = function() {
    if($('#title').val().length > 0) {
        var data = {
            flg: 0,
            title: $('#title').val()
        };
        var todoObjectStore = getObjectStore(DB_TODO, 'READ_WRITE');
        var put = todoObjectStore.put(data);
       
        put.onsuccess = function() {
            $('#title').val('');
            getTodo(function(todo) {
                showTodo(todo);
            }, DB_SORT);
            alert('登録しました。');
        };
       
        put.onerror = function() {
            alert('登録に失敗しました。');
        };
    }
};


//=================================================
// データ読み込み
//=================================================
var getTodo = function(callback) {
    var todoObjectStore = getObjectStore(DB_TODO, 'READ_WRITE');
    var res = new Array();
    var get;
   
    switch(DB_SORT) {
        case 0:
            get = todoObjectStore.index('flg').openCursor(0);
            break;
       
        case 1:
            get = todoObjectStore.index('flg').openCursor(1, cursorIDB.PREV);
            break;
       
        default:
            get = todoObjectStore.openCursor();
            break;
    }
   
    get.onsuccess = function() {
        var cursor = get.result;
       
        if(!cursor) {
            typeof callback == "function" && callback(res);
            return;
        }
       
        res.push(cursor.value);
        cursor.continue();
    };
   
    get.onerror = function() {
        alert('読み込みに失敗しました。');
    };
};


//=================================================
// データ更新
//=================================================
var updateTodo = function(id, mode) {
    var todoObjectStore = getObjectStore(DB_TODO, 'READ_WRITE');
    var update = todoObjectStore.openCursor(keyrangeIDB.lowerBound(id, false));
   
    update.onsuccess = function() {
        var cursor = update.result;
       
        if(cursor) {
            var data = cursor.value;
            data.flg = mode;
            cursor.update(data);
            cursor.continue();
        }
       
        getTodo(function(todo) {
            showTodo(todo);
        }, DB_SORT);
    };
   
    update.onerror = function() {
        alert('更新に失敗しました。');
    };
};


//=================================================
// データ削除
//=================================================
var deleteTodo = function(id) {
    var todoObjectStore = getObjectStore(DB_TODO, 'READ_WRITE');
    var del = todoObjectStore.delete(id);
   
    del.onsuccess = function() {
        getTodo(function(todo) {
            showTodo(todo);
        }, DB_SORT);
        alert('削除しました。');
    };
   
    del.onerror = function() {
        alert('削除に失敗しました。');
    };
};


//=================================================
// データ表示
//=================================================
var showTodo = function(todo) {
    var container = document.getElementById('todoList');
   
    for(var i = container.childNodes.length - 1; i >= 0; i--) {
        container.removeChild(container.childNodes[i]);
    }
   
    for(var key in todo) {
        var li = document.createElement('li');
        var update = document.createElement('input');
        var del = document.createElement('input');
        var html = '';
       
        update.type = 'button';
        update.value = '変更';
        update.id = 'todoUpdate_' + todo[key].id;
        del.type = 'button';
        del.value = '削除';
        del.id = 'todoDel_' + todo[key].id;
        del.onclick = function() {
            if(confirm("削除しますか?")) deleteTodo(parseInt(this.id.replace('todoDel_', '')));
        };
       
        if(todo[key].flg == 0) {
            html += '[未処理]';
            update.onclick = function() {
                updateTodo(parseInt(this.id.replace('todoUpdate_', '')), 1);
            };
        } else {
            html += '[処理済]';
            update.onclick = function() {
                updateTodo(parseInt(this.id.replace('todoUpdate_', '')), 0);
            };
        }
       
        html += todo[key].title;
       
        li.appendChild(document.createTextNode(html));
        li.appendChild(update);
        li.appendChild(del);
        container.appendChild(li)
    }
};


//=================================================
// ソート切り替え
//=================================================
var sortTodo = function(id) {
    switch(id) {
        case 'sort1':   DB_SORT = false;    break;
        case 'sort2':   DB_SORT = 0;        break;
        case 'sort3':   DB_SORT = 1;        break;
    }
   
    getTodo(function(todo) {
        showTodo(todo);
    }, DB_SORT);
};


//=================================================
// メイン処理
//=================================================
$(function() {
    $('#register').click(function() { putTodo(); });
    $('#sort1').click(function() { sortTodo($(this).attr('id')); });
    $('#sort2').click(function() { sortTodo($(this).attr('id')); });
    $('#sort3').click(function() { sortTodo($(this).attr('id')); });
    openDB(function() {
        //
    });
});

いやマジで、上記サンプルを iOS 系で開いてもらえば分かりますが、11 行目の DB Object 取得部分で既に undefined が返ってくる。( =未実装 )

こっち使えと言いつつ実際は使えないってどうなん?

ねぇねぇ?どうして欲しいの??

と、割と本気で思います。

 

2013 年には…と淡い期待も有ったのですがやはりダメなようです。

iPhone4 、 iPad Retina のそれぞれ最新の iOS ファームで確認( 2013/01/08 )。ちなみに Chrome for  iOS でも当然ダメだった、ベースの webkit が同じなんだから当たり前か。

勿論 PC や Mac の webkit 系なら動くよ!多分 IE 10 とかも大丈夫。

 

結局「 無いものは仕方がない 」と言う事で、引き続き SQLite でゴリゴリ作るしか無いのかなぁ。

私は別に代替えで推奨の物があるなら、別に SQL だろうが key / value 型であろうが何でも構わないと思っています。

だって、欲しいものは「 要件の動きが実現可能 」「 動作の安定感 」「 アップデートによる保守性 」じゃないですか?

 

今市場を見れば「 全力で HTML5 を使えるのは スマホ / タブレット だけ! 」と言うのは火を見るより明らかです、ブラウザが webkit 系しか無い訳だし。

じゃあ、さっさと何とかしてちょうだい!と思うのはワガママなのでしょうか。

 

どう考えても PC or  Mac より、スマホ or タブレットの方が ローカル DB の恩恵が大きい と言う事実が追い打ちをかけています。

据え置き機がオフライン状態でしか使えないなんて事、回線トラブルとか引越し直後以外に有るの?って感じですし。

個人が買うマシンは明らかに 過剰なオーバースペック になって行ってるし、回線だって ISDN が珍しくなってきているこのご時世に「 トラフィックを減らす 」「 高速ブラウジング 」なんてのは、本当に大きな大きなサイトを運営していて、かなりサーバがしんどいよ、きっついよって思ってる極々一握りの世界なんじゃ無いかって思うんです。

 

実際は色々無茶が出来る据え置き機で開発を重ね、仕様が安定してから iOS 等のコンパクト OS に移植するって事なんでしょうけどね。

 

今年も悶々としながら続報を待っています、もう少し勉強を進めるべきか。

なんとなく下書きが残っていたので投稿しました。

それではまた。

Indexed Database API について(へっぽこプログラマーの日記様)
Indexed Database API(HTML クイックリファレンス様)
Indexed Database APIによるデータベース(Libro.様)

などなど、参考にさせて頂きました。

Tags: ,

Your Reply