suin.io

ローカルストレージを使ってiPhoneのcookie健忘症対策: ウェブアプリ開発者の立場から

suin2009年9月13日

iPhoneはcookieがしばしば勝手に消えるようだ。そのせいで、セッションが切れる。すると、GmailやDropboxなどのウェブアプリはログアウトしていまい、IDとパスワードを入力し直す手間が出るので、たちまち不便になる。

セッションが落ちるのは仕方がないとする。それでも、IDやパスワードの記憶ぐらいはしておきたい。iPhoneのキーボードでメールアドレスとパスワードを入力するのは、えらく苦痛だからだ。iPhone向けウェブアプリの開発者としては、できるだけユーザの不便を解消してやりたいはずだ。

iPhoneのsafariにはパスワードの記憶機能がない。その代替策として、パスワードなどをcookieに保存する方法が考えるかもしれない。mixiなどもこの方法だ。しかし、上で述べたが、iPhoneのcookieはよく無くなる。そこで、さらにcookieの代替策として、safariのローカルストレージを使う手がある。

ローカルストレージとは?

iPhoneのsafariにはjavascriptで使えるSQLiteが用意されている。ローカルストレージはcookieと違い、有効期限がない。つまり、勝手に消えることは永久的にない。ただし、次のような制約がある。

  • データベースはドメインごと。したがって、他のドメインのデータベースを参照することはできない。(逆にできたら、セキュリティ的に怖い)
  • データベースの容量は5MBまで。

ローカルストレージの詳細は、分かりやすくまとめている「Safari 3.1 に実装された「Client-side database storage (SQL API)」とは何か? - IT戦記:」をご覧いただきたい。

では、具体的にローカルストレージを使った、ID・パスワードの記憶機能の実装を紹介する。コード量は多いが、やっていることは大して複雑ではない。

まずは、HTMLは次のように書く。

<form name="login" action="login.php" method="post" onsubmit="return emai_pass_remaind()">
<input type="text" name="email" value="" placeholder="メールアドレス" autocorrect="off" autocapitalize="off" /><br />
<input type="password" name="pass" value="" placeholder="パスワード" autocorrect="off" autocapitalize="off" /><br />
<input type="checkbox" name="save_pass" value="1" />メアドとパスワードを記憶<br />
<input type="submit" value="送信" />
</form>

つぎに、javascriptは次のように書く。

// ロード時に、データベースからメアドとパスワードを取得、フォームにセットする関数
window.onload = function() {
  var db = openDatabase('mydatabase', '1.0');

  db.transaction(
    function(tx)
    {
      tx.executeSql("SELECT email, pass FROM login WHERE id = 1", [],
        function(tx, rs)
        {
          // ロードに成功したら、フォームに値をセット
          document.login.email.value = rs.rows.item(0).email; // htmlspecialchars?
          document.login.pass.value  = rs.rows.item(0).pass;
          document.login.save_pass.checked = true;
        }
      );
    }
  );
}

// フォーム送信時に、メアドとパスワードをデータベースに保存・削除する関数
function emai_pass_remaind()
{
  // フォームから値を取得
  var email = document.login.email.value;
  var pass  = document.login.pass.value;

  /* ここらへんにvalidationの処理を入れたり... */

  var db = openDatabase('mydatabase', '1.0');

  if ( document.login.save_pass.checked == true )
  {
    db.transaction(
      function(tx) {
        // テーブルがあるかな?
        tx.executeSql("SELECT count(*) FROM login", [],
          function(tx, rs) {
            // テーブルあるよ
            if ( rs.rows.item(0) == 0 )
            {
              // テーブル初利用の場合は、追加
              tx.executeSql('INSERT INTO login VALUES(1, ?, ?)', [email, pass], // escape?
                function() {},
                function(error) {
                  alert('save failed: ' + error.message);
                }
              );
            }
            else
            {
              // テーブル初利用じゃない場合は、更新
              tx.executeSql('UPDATE login SET email = ?, pass = ? WHERE id = 1', [email, pass], // escape?
                function() {},
                function(error) {
                  alert('update failed: ' + error.message);
                }
              );
            }
          },
          function(tx, error) {
            // テーブルないよ、テーブルつくろ
            tx.executeSql('CREATE TABLE login (id INTEGER PRIMARY KEY, email TEXT, pass TEXT)', [],
              function() {
              // テーブル初利用だから、追加
                tx.executeSql('INSERT INTO login VALUES(1, ?, ?)', [email, pass], // escape?
                  function() {},
                  function(error) {
                    alert('save failed: ' + error.message);
                  }
                );
              },
              function(error) {
                alert('Database creation failed.' + error.message);
              }
            );
          }
        );
      }
    );
  }
  else
  {
    // チェックボックスにチェックがないときは、テーブル削除
    db.transaction(
      function(tx) {
        tx.executeSql('DROP TABLE login', [],
          function() {},
          function(error) {
            alert('delete failed: ' + error.message);
          }
        );
      }
    );
  }
}

これで、あなたのウェブアプリも、かなり便利になると思う。

RELATED POSTS