SPAのサイトをアプリにするメリットと開発での問題点について

# 最初に
Webの技術を使って、iOS、Androidアプリを作ることができるPhoneGapというツールを知っている人は多いと思います。
PhoneGapはAdobeが提供しているのですが、そのオープンソース版として[Apache Cordova](http://cordova.apache.org/)というツールがあります。

Apache Cordovaを使った事例として、[第2回Cordova勉強会](http://7b543bd41af4571f0bdafff1b5.doorkeeper.jp/events/17138)でWebサービスとしてリリースした「smartFX」をハイブリットアプリとしてリリースすることになった背景やその効果についてと、ハイブリットアプリ化するにあたって出てきた問題点と対応について発表をしてきました。

詳しい内容はスライドを見てください。
[smartFXにおけるApache Cordovaの活用について](https://speakerdeck.com/takeshy/smartfxniokeruapache-cordovafalsehuo-yong-nituite)

ここではスライドの補足をしていきたいと思います。

# ハイブリットアプリをリリースすることになった背景やその効果について
smartFXは、サイト全体をSingle Page Application(SPA)で作成し、WebSocketを使ってリアルタイム性をもつことで、ネイティブと変わらない操作感を実現しています。
アプリに比べて、Webで出す利点として下記があり、リリース前はWebでいけると思っていました。

* インストールしてもらう必要がない。
* 審査が不要で、いつでもアップデートできる。
* 検索経由でユーザに来てもらえる。
* 同時に複数の画面を開けたり、お気に入りでダイレクトに目標のページが開ける。

もちろんWebの最大のメリットとして、開発が楽(Web技術に慣れている & OSごとに言語を変えなくていい)というのはあるのですが、Apache Cordovaの存在があるのでここでは無視します。

それに対してアプリの利点ですが、ネイティブの機能が使えるので、Push通知ができてうらやましいぐらいに捉えていました。

ネイティブでサクサク動くのは当たり前ですが、Webサイトで実現しているとなるとウォーとなってクールと思ってもらえるのではと内心期待していました。

まあ残念ながらそうとはならなかったのはスライド通りです。
そしてアプリ化することで、危機的状況を脱出できたのもスライド通りです。
アプリはリリースをウォッチしている人がそれなりにいるので、Webサイトのリリースよりもニュースになりやすく、圧倒的にSNSで拡散してくれます。
また、リリース時期がお盆だったため、他にリリースするような変わった人がいなかったお陰でGunosyに載せてもらえたというラッキーもありました。

# ハイブリットアプリ化するにあたって出てきた問題点と対応について

## Webサーバーとのやり取りがAjaxに限定
WebサイトでのSPAとの違いは、最初のHTMLがサーバから取得できなくなったことによる影響が結構あります。

1. AjaxでPATHだけでなく、ホスト名を指定する必要
1. MetaタグについているCSRFのtokenの取得が別途必要
1. ハッシュ値つきのJSやCSSのファイル名の取得が別途必要

また外部のHTMLを表示できないということで、そのままではソーシャルログインができないという問題があります。

1.は今まではPathだけでよかったので、Production環境でも開発環境でも同じでよかったのですが、Host名も必要となるので設定でその部分を書き換える仕組みが必要となります。Railsだとjs.erbのようにしてerbでRailsの環境によってHost名を書き換えます。

2.のCSRFのtokenはAjaxで取得します。RailsだとControllerからform_authenticity_tokenの呼び出しで取得できます。

3.のJSやCSSのハッシュ値つきのJSやCSSのファイル名の対応とソーシャルログインの対応の実装は[Apache Cordova(PhoneGap)開発におけるTips](http://qiita.com/takeshy/items/e35595fa65ec7d8d3b99)を参照してください。

## cookieが使いものにならない
Androidの場合にCordovaだと、LocalDomainのせいかcookieの内容が起動時に初期化されてしまいます。
またそもそもSPA自体でもCookieを使うことには問題があって、理由はPushStateでURLを書き換えても、CookieのPathは最後にサーバから読み込んだURLのPathのままなので、jsでCookieを書き換えると、どのPathで保存されたかわからない状態になるからです。
そのため、最初にアクセスしたURLによって、Cookieから取得する内容が違うということがありました。
なので、LocalStrageを使うのがいいのですが、Web版でも提供して同じプログラムを共通で使っていると、Safariのプライベートモードでアクセスされた場合にLocalStrageの書き込みがエラーになるという問題に対応する必要があります。

対応としては、LocalStrageよりも先に大域変数に保存しておき、LocalStrageの書き込みのエラーを許容するようにしました。
起動時にLocalStrageの値を見て、なければデフォルト値にして、以降の参照は大域変数のほうを使用することで、プライベートモードでもアプリを開いている間は状態を保存できるようにしています。

## リロードができない
SPAで作る場合、Webサーバ側では基本的にSPA内のPathであれば同じ内容のHTMLを返します。
クライアント側でPathを見て、表示内容を決定します。
Railsで言えば、route.rbで'/demos/*path'のようにワイルドカードを使って同じ処理を呼び出しています。
そのため、色々なPath上でwindow.reloadを呼び出しても、サーバ側は意識せずとも、ちゃんとPathに相応しい画面が表示されます。
ただ、Cordovaの場合、reloadするとpushStateで書き換えたURLをもとに直接ファイルシステム上のファイルを参照してしまうので Not Foundになってしまいます。

## ブラウザで動いてもアプリだとエラーになるコードがある
基本はないのですが、iOSのWebViewだとJITが効かないせいか、下記のコードがSafariだと動くけど、WebViewでは"undefined is not a function"で動かないということがありました。

```js
var ExampleRouter = Backbone.Router.extend({
  initialize: function(options){
    this.setUp()
  },
  setUp: function(){
    console.log("various procedure")
  }
})
```

## 端末によって動作が違う
この問題はCordovaの問題という訳ではなく、普通のWebでもそうでした。。
ただ、Cordovaを使うということは、ネイティブと争うことになるので、スムーズな動きを実現するために、Canvasを使う可能性が高いのですが、そのCanvasがAndroid4.0だとDomの一部を書き換えただけで、canvasがクリアされるという致命的なBugがあります。
でも、最近CordovaでAndroidに対してCrosswalkが使えるようになったことで事態が変わろうとしています。
実際[xz](http://xz-closet.jp/appli/)さんが今回の発表である[～新しい着回しと出会おう～ 『XZ（クローゼット）』 を支える技術 －Cordova編－](http://www.slideshare.net/yossy222/xz-cordova)でCrosswalkを使用した場合と使用していない場合の動きをプレゼンで見せてもらいましたが、iOSなみに動作も速くなっていて驚きました。しかも端末ごとの差がなくなるというのは画期的です。
smartFXでも導入しようとしたのですが、ビルドエラーで苦戦して諦めてしまっていました。
今回見せてもらって俄然やる気がでたので近いうちに対応できると思います。

# まとめ
論より証拠なので、ぜひ触ってみたください。
[smartFXバーチャルトレード(アプリ)](http://smartfx.minkabu.jp/s/virtual-trade.html)
[smartFXバーチャルトレード(Webバージョン)](http://smartfx.minkabu.jp/demos)
無料で使えるので、FX取引をしなくても、為替情報はショッピングや海外旅行に役立ちます
インストールしただけで使えるようになりますし、ログインすれば、実際にデモ取引もできるようになります。

# 補足

[第2回Apache Cordova勉強会の感想 & AngularJSとBackbonejsの利用シーンの違いについて](http://takesy.cocolog-nifty.com/atico/2014/12/2apache-cordova.html)