チャットなどリアルタイム更新が必要なスマフォアプリの構成について考えてみた

[今開発しているPlayer!](https://itunes.apple.com/jp/app/id897872395)で、チャット系のリアルタイム更新処理が必要となったので、色々調べています。


※「スマフォアプリ」として共通事項が多いのでそう表記しましたが、基本的にスマフォアプリはiOSアプリとして書いてます。Android・Windows Phoneなどは差異があったり読み替える必要があると思っています。

# 結論

後半で詳しく書きますが、今のところ以下の結論に至りました。

- PUSH通知に全て依存する構成でもそれなりに動く
- PUSH通知 + 双方向通信処理(WebSocketなど)の併用がベスト
    - 双方向通信処理は[Pusher](https://pusher.com)・[Firebase](https://www.firebase.com/)などのサービスに頼る or [ws](https://www.npmjs.com/package/ws)・[Socket.IO](http://socket.io)・[SignalR](http://signalr.net) + 対応クライアントライブラリなどで自前実装
    - Pusherなど有名サービスのクローンOSS導入も良さそうです
        - [stevegraham/slanger](https://github.com/stevegraham/slanger)
        - [edgurgel/poxa](https://github.com/edgurgel/poxa)
        - [websocket - Open source alternatives to Pusher - Stack Overflow](http://stackoverflow.com/questions/8395900/open-source-alternatives-to-pusher/8397339#8397339)

開発リソースが潤沢で無い場合は、以下の順でシフトしていけば良いのではと思います。
工数が少ない中、欲張って双方向通信取り入れたガタガタになるより、まずはPUSH通知に頼っちゃった方が賢明に思います。

1. まずはPUSH通知に全て依存
2. [Pusher](https://pusher.com)・[Firebase](Firebase)などのサービスに頼って双方向通信処理も併用
3. [ws](https://www.npmjs.com/package/ws)・[Socket.IO](http://socket.io)・[SignalR](http://signalr.net) + 対応クライアントライブラリなどで自前実装して双方向通信処理も併用

有名どころのアプリは大体、3の方法を取っていました。
([# 他のアプリの調査](http://qiita.com/mono0926/items/bb7fdd912bc338096f57#他のアプリの調査)で後述)


# AppleのPUSH通知サービス(APNS)について

こちらにまとめました:
[iOSのPUSH通知(APNS)の特徴・ノウハウまとめ(iOS 9まで対応) - Qiita](http://qiita.com/mono0926/items/df03c61adc56934e2e7a)
Stockもはてブも3-400以上いただけてありがたやです(　´･‿･｀)

以下で、基本的なリアルタイム更新の要件を満たせると思います。

- (サイレント)PUSH通知をトリガーとして、最新データを取得
- [iOS 8以降では、PUSH通知のペイロード自体にデータを含めて、レスポンス向上 + サーバー負荷軽減も可能](http://qiita.com/mono0926/items/df03c61adc56934e2e7a#通知ペイロードについて)

~~PUSH通知切られちゃうと動かないのと、~~ レスポンスタイム的にはやはり双方向通信有利かなと思います。

[iOSのSilent Notificationは許可画面が要らない?](http://qiita.com/yuki_okawa/items/238bf6b1dcccfa00f9ea)を見て、確かにと思い検証した結果、確かにその通りでした。
ユーザー体験考慮のためにも、デバッグにハマらないためにも、知っておいた方が良いですね。

- サイレント通知: バックグラウンド更新が許可されているか否か
    - こちらはデフォルトオンなので、PUSH通知の許可より有効率が高そうですね
- 通常のPUSH通知: PUSH通知が許可されているか否か


# 双方向通信

ざっくり、以下の歴史があります。

- 定期ポーリング
    - 定期的にリクエスト投げてデータ更新
- ロングポーリング
    - サーバーがリクエスト受けてからデータ更新されるまでレスポンスを遅延させる
- Server Sent Events
    - ロングポーリング改良版

ここまでがHTTPの規格に沿ってリアルタイム更新を実現する技術です。

今双方向通信と聞くとまず思い浮かぶであろうWebSocketは、HTTPの縛りを外して作られたWeb標準(RFC 6455 / W3C CR)です。

それまでのものと比べて以下のようなメリットがあります。

- ヘッダが軽量
- リアルタイム性が高い
- サーバー負荷が低い

デメリットは以下です。

- サーバー・クライアントともにWebSocket規格への準拠が必要
    - 特に旧ブラウザ(古いIE・Androidなど)で問題になる
- 既存の通信機器を通過できない可能性がある

特に前者は、サーバーとスマフォアプリで通信する場合にはまったく困らないですね。

[サーバPUSHざっくりまとめ](http://www.slideshare.net/mawarimichi/push-37869433)の資料が簡潔で分かりやすかったです。

このあたり、調べるとキリがないのですが、今回の要件では、このくらいざっくり理解した上で、性能も採用例も多いWebSocketベースに組めば良いと見なして良いと思います。
(僕はけっこう調べちゃいましたが)

## [XMPP](https://ja.wikipedia.org/wiki/Extensible_Messaging_and_Presence_Protocol)は？

メッセンジャー系の標準規格というようなイメージでしたが、最近は下火な感じなのでわざわざこれに従わなくて良いと思いました。

- [Google Finally Kills Off GoogleTalk and XMPP (Jabber) Integration - Disruptive Telephony](http://www.disruptivetelephony.com/2015/02/google-finally-kills-off-googletalk-and-xmpp-jabber-integration.html)
- [Facebook、メッセンジャーのXMPPサポートを終了 | スラド IT](http://it.srad.jp/story/15/07/21/0527224/)

## その他、参考になった資料

- WEB+DB 71号 
    - 雑誌・書籍はきれいに情報まとまっていて分かりやすいです
    - [WEB+DB PRESS 総集編〔Vol.1~72〕](https://www.amazon.co.jp/dp/477415783X?tag=mono0926-22)で買える電子版に含まれています
- [webSocket通信を知らないiOSエンジニアが知っておいて損はしない(経験談的な)軽い話](http://www.slideshare.net/mitolog/loco-moku-no6)
    - タイトル通りiOSアプリ実装観点での資料で良いです。
- [WebSocketのキホン](http://www.slideshare.net/You_Kinjoh/websocket-10621887)
    - 詳しいし分かりやすい。
- [YAPC::Asia2014 - O2O/IoT/Wearable時代におけるWeb以外のネットワーク技術入門](http://www.slideshare.net/recruitcojp/yapc2014-mqtt-btle)
    - かなり幅開く無線系技術全般
- [Socket.IO, Redisを使用し各ゲーム間でプッシュ通知するシステム | 株式会社サイバーエージェント](https://www.cyberagent.co.jp/techinfo/techreport/report/id=8858)
    - サーバーサイドの設計の話が詳しくて良い
- [リアルタイムWebを構築しやすくする「Socket.IO」とは (1/4)：CodeZine（コードジン）](http://codezine.jp/article/detail/7075)
- [WebSocketで目指せ！ リアルタイムWeb（1）：node.jsの衝撃とWebSocketが拓く未来 (1/2) - ＠IT](http://www.atmarkit.co.jp/ait/articles/1010/05/news133.html)

# サーバーサイド自前実装の際の選択肢

## 素のWebSocketライブラリ

一番素直なのは、後述の[Socket.io](http://socket.io)などでは無く、純粋なWebSocket仕様に即したライブラリを選定して使うことだと思います。
特に、スマフォアプリの場合は、WebSocket非対応ブラウザ問題が無いので、素のライブラリでも十分いけるかと思います。

参考: [WebSocket の導入: ウェブにソケットを実装する - HTML5 Rocks](http://www.html5rocks.com/ja/tutorials/websockets/basics/)

## [Socket.io](http://socket.io)

WebSocket系のライブラリとして一番有名かと思います。
Node.js発祥のリアルタイム通信モジュールです。
純粋なWebSocketモジュールではなく、トランスポート層の1つにWebSocketを利用しているものです。
WebSocket非対応環境の場合はロングポーリング動作に切り替えるなどフォールバックが用意されていたり、リッチなAPI(自動再接続・認証など)がセットになっていたりします。
なので、Socket.ioで組まれたサーバーに接続するクライアントは素のWebSocketクライアントライブラリでは無く、Socket.ioクライアントライブラリである必要があります。

### Socket.ioの場合の言語の選択

Socket.ioの場合、本家のNode.js一択だと思います。
Goでも性能出そうだなと思いましたが、まだ歴史が浅いのかこれに関してはまだ劣るようです。

- [Go lang勉強会でgo-socket.ioの話してきた - from scratch](http://yosuke-furukawa.hatenablog.com/entry/2014/08/11/095157)

## Socket.io vs SignalR

Microsoft系の技術にあまり馴染みの無い場合、[SignalR](http://signalr.net)のことはほとんど知らないかもしれませんが、ASP.NET上で動作するライブラリです。
Socket.ioと同様に、純粋なWebSocketモジュールではなく、トランスポート層の1つにWebSocketを利用しています。
海外ではそこそこ盛り上がっているようですが、国内では微妙ですよね（´・ω・｀）
Socket.ioに遜色の無いライブラリ(かつそれぞれ特徴あり)なので、検討しても良いと思います。
特にMicrosoft系技術に強い場合は、オススメですヽ(・ω・｀）

- [リアルタイム Web 最前線 ～ Socket.IO ＆ SignalR 徹底解説](http://www.slideshare.net/shibayan/build-insider-shibamura?ref=http://www.buildinsider.net/hub/bioff/a4)
    - 分かりやすい！
- [特集：ASP.NET SignalR入門（前編）：ASP.NET SignalRを知る (1/5) - ＠IT](http://www.atmarkit.co.jp/ait/articles/1303/19/news099.html)
- [DyKnow/SignalR-ObjC](https://github.com/DyKnow/SignalR-ObjC)
    - iOS・OSXクライアント
    - [SignalR-ObjC で iPhone アプリでも SignalR を使う - しばやん雑記](http://blog.shibayan.jp/entry/20121015/1350307567)


# 双方向通信系のサービスは何を選べば良い？

## 自前実装だと何が困難？

 双方向通信系のサービス比較の前にまずこちらです。

- 単純に技術選定・設計・実装コストがかかる
    - 双方向通信系のサービス以上に色々な言語・ライブラリ・構成など選択肢があるので自信を持って組むのが大変
    - かなり時間かけるか、ノウハウ持った経験者の助けが欲しい
- 特にスケールアウトが難しい
    - 特にこの記事が分かりやすかったです: [リアルタイム Web 最前線 ～ Socket.IO ＆ SignalR 徹底解説](http://www.slideshare.net/shibayan/build-insider-shibamura?ref=http://www.buildinsider.net/hub/bioff/a4)

## 双方向通信系のサービス比較

シェアでは[Pusher](https://pusher.com)、勢いとGoogleブランド的には[Firebase](https://www.firebase.com)ですかね。

- [Firebase vs PubNub vs Pusher Stackup | StackShare](http://stackshare.io/stackups/firebase-vs-pusher-vs-pubnub)
- [(5) What are the competitive advantages of Pusher vs. PubNub? - Quora](https://www.quora.com/What-are-the-competitive-advantages-of-Pusher-vs-PubNub)
- [websocket - Pusher vs Pubnub vs open source Socket.io / SignalR.net / Faye / jWebSocket - Stack Overflow](http://stackoverflow.com/questions/11314528/pusher-vs-pubnub-vs-open-source-socket-io-signalr-net-faye-jwebsocket)
    - これは自前実装含めた比較

Pusherは、アーキテクチャー的に、Pusher→クライアントへのPUSHのみで、他は通常のHTTPとなっていて特殊かもしれません。
しかし、「リアルタイム更新が必要なスマフォアプリ」の要件をシンプルに満たしていて良いと感じています。
自前実装にするにしても、この構成かなとイメージしてます。
流行りのマイクロサービスっぽく、APIサーバーとは切り離して、独立して双方向通信機構を開発しやすそうです。

![hero_howitworks-d5840af909cfca0b448c6b24fdfdf9af6a95263d79ada6a789607b8f6bc7a6e1.png](https://qiita-image-store.s3.amazonaws.com/0/19398/89befc9a-55dc-36ee-dea5-fcfd1be474e6.png)


Pusherは簡単にGoとSwiftでサンプル組んでみましたが、けっこうシンプルでした。

- [Documentation | Pusher](https://pusher.com/docs)
- [WebSocketで目指せ！ リアルタイムWeb（2）：WebSocketの現状と技術的課題 (1/2) - ＠IT](http://www.atmarkit.co.jp/ait/articles/1010/20/news105.html)
- [WebSocket通信を簡単に利用するためのクラウドサービスPusherが便利すぎて泣いた - (ﾟ∀ﾟ)o彡 sasata299's blog](http://blog.livedoor.jp/sasata299/archives/52026554.html)
- [Pusherでお手軽WebSocket – iPhoneから加速度をリアルタイム送信 | KRAY Inc](http://kray.jp/blog/pusher_websocket/)
- [PUSHERを使ってWebsocket経由のPush通知を行ってみる - テノニッキ (@hideack 's diary)](http://hideack.hatenablog.com/entry/20120129/1327821318)

Firebaseはドキュメントしか読んでないですが、リッチな印象です。

- [Developer Docs - Firebase](https://www.firebase.com/docs/)
- [準リアルタイムなバックエンド開発を支援するGoogleのFirebase](http://www.infoq.com/jp/news/2015/04/google-firebase)
- [Titanium - Firebaseを使ってリアルタイムサービス - Qiita](http://qiita.com/yagi_/items/21b84d934420a5e3bf26)

## 双方向通信系のサービスデメリット

- ロックインされてしまうかもしれない
    - 実装切り替えることを見据えて、ちゃんと疎結合にすれば大丈夫そう
    - 特にPusherの構成はすり替えしやすそう
- お金がかかる
    - [Pricing | Pusher](https://pusher.com/pricing)
    - ただ、自前実装の場合もサーバー代はけっこうかさむはず

# iOSアプリの双方向通信実装

## ライブラリ選定

[webSocket通信を知らないiOSエンジニアが知っておいて損はしない(経験談的な)軽い話](http://www.slideshare.net/mitolog/loco-moku-no6)が参考になりました。

- Pusher・Firebaseなど使う場合は、そこで提供してあるSDKを利用
- 自前実装の場合はサーバーサイドの実装によって選択肢が異なる
    - Socket.io
        - [pkyeck/socket.IO-objc](https://github.com/pkyeck/socket.IO-objc)
        - [MegaBits/SIOSocket](https://github.com/MegaBits/SIOSocket)
            - [Swiftでソケット通信するチャットアプリ - Qiita](http://qiita.com/ytakzk/items/c0a3af0f1b9e5a349d05)
        - [socketio/socket.io-client-swift](https://github.com/socketio/socket.io-client-swift)
        - [lukabernardi/AZSocketIO](https://github.com/lukabernardi/AZSocketIO)
            - [スマホのネイティブアプリでSocket.IOを利用する (1/4)：CodeZine（コードジン）](http://codezine.jp/article/detail/7076)
    - Signal
        - [DyKnow/SignalR-ObjC](https://github.com/DyKnow/SignalR-ObjC)
    - 素のWebSocket(wsなど)
        - [square/SocketRocket](https://github.com/square/SocketRocket)
            - 大手アプリがよく採用している
            - [Using websockets in native iOS and Android apps — Elabs](http://www.elabs.se/blog/66-using-websockets-in-native-ios-and-android-apps)
        - [daltoniam/Starscream](https://github.com/daltoniam/starscream)
            - Swift製

## 接続確立・切断のタイミング

こんなフローを実装に落とし込めば良いと思います。

- アプリ起動時に接続確立し、その後はずっと接続を保つ
    - `beginBackgroundTaskWithExpirationHandler`での、バックグラウンド動作延命措置も入れる
    - オフラインになったりして切断された後の復帰など例外系も考慮
- アプリが完全に終了したタイミングで切断される
    - この状態ではPUSH通知頼り
- 接続中は、サーバー側でPUSH通知発行する必要無し

# 他のアプリの調査

[他社製iOSアプリの実装の探り方 - Qiita](http://qiita.com/mono0926/items/bac367f20d418b4c64a5#%E3%83%A9%E3%82%A4%E3%82%BB%E3%83%B3%E3%82%B9%E4%B8%80%E8%A6%A7%E3%82%92%E8%A6%8B%E3%82%8B)に書きましたが、以下の通り4つのうちLINE以外はWebSocket系でした。
Socket.ioのような、WebSocket・ロングポーリングをくるんだライブラリのクライアントライブラリではなく、素のWebSocketライブラリですね。

- Slack
    - [square/SocketRocket](https://github.com/square/SocketRocket)
- Facebook Messenger
    - [square/SocketRocket](https://github.com/square/SocketRocket)
- 755
    - [UnittWebSocketClient](https://code.google.com/p/unitt/wiki/UnittWebSocketClient)
    - [微妙なニュースが流れています](http://netgeek.biz/archives/44021)が新しめのアプリかつたまたまライセンス目にしたので
- LINE
    - よく分からないけど、ロングポーリング→SPDY？
        - [ライセンス - iOS版LINEアプリの使っているライブラリを調べた - Qiita](http://qiita.com/mono0926/items/cd8518fc7f8f220d6935)
    - SPDYは双方向通信目的のものじゃない気が
        - [WebSocketと、SPDYってどう違うのよ? - かせいさんとこ](http://d.hatena.ne.jp/kasei_san/20130217/p1)


# 議論の参考になったもの

「リアルタイム更新が必要なスマフォアプリの構成」についてぐぐって上位に引っかかる、有名どころのやり取りは大体目を通したと思っています。
日本語は技術解説記事などは十分ありましたが、こういった議論についてのものはほぼ見当たらなかったです。

本記事は、技術的な調査 + こういった議論を読んだ上で、僕なりの感触を示したものです。

- [Strategies for an iOS messaging app? : iOSProgramming](http://www.reddit.com/r/iOSProgramming/comments/1a8ogr/strategies_for_an_ios_messaging_app/)
- [iphone - Push Notification vs. Web Sockets for implementing a reai-time chat app? - Stack Overflow](http://stackoverflow.com/questions/22736913/push-notification-vs-web-sockets-for-implementing-a-reai-time-chat-app)

# 少しチェックしたものの、不要と判断した情報

- [Stream Programming Guide](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Streams/Streams.html)
    - 上位レイヤーのライブラリが充実しているのでそれ使えば良いと判断
- [あなたにWebSocketは必要ないかも](http://postd.cc/you-might-not-need-a-websocket/)
    - 不安にさせるだけで結局どうすれば良いのか分からない
    - WebSocket系のライブラリや利用例が充実しているから、それ使うのは悪い選択肢じゃないと判断