ReactNativeのAsyncStorageをNodeのREPLから操作する

背景

ReactNativeにはAsyncStorageというkey-valueストレージシステムがある。 valueにはstringしか入れられない本当に簡素なものだけど、JavaScriptのプレーンなオブジェクトはJSONシリアライズ可能であるので、さほど困らない。

クライアントサイドで永続化したい情報はAsyncStorageに突っ込んでおく。アプリケーションを作っている過程において、値を少しづつ変更しながら開発を進めたい場面が結構あった。

ところでRailsにはrails consoleというものがあり、railsアプリケーション内のinitialize処理を通した後のREPLを立ち上げることができる。このREPLでは、railsアプリ内のクラスはすでにロード済みであるため、その全てを呼び出すことができる。その中でデータベースアクセスを担うクラス群も呼び出せるため、rails consoleから自由にデータの参照や改変を行える。

普段Railsで開発していることもあり、そんな感じにNodeのREPLからReactNativeのAsyncStorageにアクセスしたいな〜、と思ってそんなツールを作った。

github.com

動作

https://cloud.githubusercontent.com/assets/4954534/26222189/09924ee2-3c54-11e7-8247-ed15afa7cd31.gif

このアプリケーションは、コメントの投稿と同時にAsyncStorageと同期し、コメントリストを永続化している。 REPLからAsyncStorageにコメントを追加し、その後アプリケーションをリロードした時に、コメントが追加されていることを確認している。

仕組み

NodeとReactNativeとの通信はwebsocketを利用している。(ReactNativeは標準でwebsocketをサポートしています。)

REPL起動時にchild.spawnで子プロセスとしてwebsocket serverを立ち上げて、ReactNativeアプリケーションからのレスポンスを受け取る。 REPLのプロセスとwebsocket serverはプロセス間通信を行う。 通信のイメージは以下

f:id:joe-re:20170519070432p:plain

データフローの最後の部分で、websocket serverが結果をファイルに書き込み、それをREPLのプロセスで監視しているところがある。なんで??感がすごい。

今回の構成ではWebSocketを挟んでいたり、そもそもAsyncAtorageはその名の通り非同期なAPIを提供していたりするので、REPLから発行したコマンドがAsyncStorageのAPIを叩いて結果が返るまでは、どう頑張っても非同期処理になる。これを無理やり同期処理にするために、ここでファイルを見ている。

REPLで操作しているのにPromiseが返ってくるのは使いづらい。しかし、Node.jsの世界では、プロセスを止めて処理の結果を待つことが基本的にはできない。(bindingを書けばその限りではないけど。) top levelでawaitする方法があればどうにかなりそうだけど、今のJavaScriptの世界ではそれは許されていない。

Top-level `await` is a footgun · GitHub

Clarifying question: can await be used in top-level code? · Issue #9 · tc39/ecmascript-asyncawait · GitHub

というわけで以下のようになった。

出力結果の監視 is ファイルが書き込まれたかどうか。node-sleepでプロセスを停止させつつ、一定周期でファイル出力がないか監視している。internalなネットワークの通信なので、大抵はほとんど待たずに結果を受け取れるはず。ReactNative側とwebsocketの接続ができていなかったりした場合には、一定時間後にタイムアウトになる。 無理やり感が漂っているので、もっといい方法をご存知の方はぜひ教えてください。

結構便利です

結構便利に使えている。こいつにstorageの状態のスナップショットの取得/復元をする機能とかあってもいいかなー。 興味のある方はぜひ使ってみてください。

CafePitchの区切り文字にheader要素を指定できるようにした

CafePitchはElectron製のMarkdownで書けるプレゼンテーションツールです。久々のアップデートです。

github.com

久々ということでAngularをrc versionから脱却してみたりとか、TypeScriptのバージョンを上げたりとか色々した。 Angularはbootstrap周りをごっそりと書き換えたけど、それ以外はあんまり変えなくても動いた

しかしテスト周りは変わりすぎていて厳しかったので、ユニットテストは全部捨てた。もともとあまり書いていなかったし、僕の不勉強からかなりギリギリで動いている感じだったので、まぁいい機会だったかもしれない。

Spectronで書いているE2Eテストは無傷なので、そこで最低限は担保している。(はず)

今回のアップデートでは、以下のように設定用の吹き出しを出せるようにしていて、そこから区切り文字を設定できるようにした。

f:id:joe-re:20170320205829p:plain

id:catatsuy さんが以下のつぶやきをしていて、お話を聞いてみると、見出し要素を区切り文字にしたいということだった。

理由を聞いてみると、自然な文章を書いて勝手にスライドができて欲しい(水平線は普通に文章を書く時にはあまり使わない)というのが理由で、なるほどーという感じだったのでやってみた。

区切り文字を増やすにあたっては、markdownのparse結果に行番号が付いていると理想的だったんだけど、cafepitchの中で使っているmarkedでは位置情報までは取れなかったので、必要最小限の構文のみを、行単位でlexingする処理を実装した。

一応markedの実装を見て齟齬がないようにはしたつもりだけど、markdownのparseと違うロジックを使うのはバグりそうであんまり良くないよなー、という感じはあるので、本当は一緒にしたい。どなかたいい感じの実装があれば教えてください。

実際に試してみると、区切り文字を意識しなくて書けるのは結構いい感じ。 もし興味があれば是非お試しください。

FlowtypeでFluxアーキテクチャに型付けをするという発表をした

もう2週間ぐらい前になってしまいますが、ランサーズさん主催の勉強会で、Type-Safe Flux Using Flowtypeというタイトルで発表させてもらいました。

eventdots.jp

発表資料

speakerdeck.com

ここ半年ぐらいReact + Flux + Flowtypeを使った環境で開発しているのですが、Fluxにおける型付けのパターンは自分の中では大分固まってきた感覚があります。

今年の弊社AdventCalendarでも、Qiitaの記事として書きました。

qiita.com

自分の中で固まってきたとはいえ、FlowtypeもTypeScriptもどんどん進化しているし、もっと良いパターンもありそうだなー、という感覚もあります。

ぜひ知見をお持ちの方はお話ししましょう。

主催のランサーズさん、登壇者の皆様、ご参加いただいた皆様、ありがとうございました。