モバイル界隈はあんまり知らないんですが、React Nativeの開発環境としてExpoというものの存在を最近知ったと思ったらたまたまReact Native + Expoで作られているものを手元で動かしたい要件が。僕は普段、手元のMacには言語系のランタイムとかは入れておらずVS CodeとDocker for Macだけ入れてRemote Containersの環境で開発しています。
というわけで今回はRemote ContainersでReact Native + Expoの環境が使えるか試してみたときのメモ。
とあるReact Native + Expoで作られてるアプリを手元で動かしたいんだがこっち方面は知見がなさすぎて手探り状態。これRemote Containersでやれるんかな
— Keisuke Nishitani (@Keisuke69) 2020年12月24日
とTwitterでつぶやいたらこんなリプが。
スマホ実機かシミュレータのExpo ClientからExpo立ち上げてるPCに対してローカルIPで読みに行くので、IPが解決できてポートフォワードされてればいけるはず?です
— しんのき (@konoki_nannoki) 2020年12月24日
やれそうな気がしてきた。
とりあえず、Expoを使うのに必要なのは以下ってことなのだが、Remote Containersで使ってるベースのコンテナイメージがUbuntuベースのNode.js公式イメージなのでそれでいけるかな、と。
- Node.js LTS release or greater
- Git
- Watchman
Watchmanだけ入ってなかったのでこれはあとで入れる必要があるかも。
ひとまず、普段リモートコンテナ用のテンプレートリポジトリを用意しているのでそれをコピって試してみます。
これはシンプルに以下のDockerfileを使っているものです。
FROM node:15.3.0 AS dev RUN apt-get update && apt-get install vim -y && apt-get clean
Remote Containersの設定ファイルである.devcontainer/devcontainer.jsonもこんな感じで至って普通。
{
"name": "Existing Dockerfile",
"context": "..",
"dockerFile": "../Dockerfile",
"build": {
"target": "dev"
},
"settings": {
"terminal.integrated.shell.linux": null
},
"extensions": [
"visualstudioexptteam.vscodeintellicode",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"hookyqr.beautify",
"alefragnani.bookmarks",
"lacroixdavid1.vscode-format-context-menu",
"eamodio.gitlens",
"oderwat.indent-rainbow",
"ionutvmi.path-autocomplete",
"chrmarti.regex",
"humao.rest-client",
"wayou.vscode-icons-mac"
]
}
このフォルダをVSCodeで開いてこのリモートコンテナに接続してターミナル開きます。Remote Containersそのものはこちらで以前取り上げています。
とりあえずExpoをインストールします。あわせて手元のiPhoneにもExpoクライアントを入れてついでにサインアップを済ませておきます。
$ npm install --global expo-cli
インストール終わったらとりあえずサンプルアプリを作ってみます。テンプレートはひとまずblankを選択。
$ expo init my-app $ cd my-app
おもむろにDevelopment Serverを起動します。チュートリアルではexpo startだけどyarn startでもいける。
実行するとMetro Bundlerとかいうのが起動してきてブラウザが開きました。

QRコードが表示されているのでこれをiPhoneのカメラで読み取るとiPhoneに入れたExpo Clientでアプリを実行できるそうだ。素晴らしい。
接続タイプはTunnel、LAN、Localの3種類あるのだがLANが最初から選択されてるのでこれが標準なのかと。LANってことなのでもちろん起動している端末とiPhoneが同一ネットワーク上もしくはLANで通信可能な状況になっている必要があります。
早速やってみますが、この時点でLAN接続として表示されているIPアドレスがなんか変な感じでした。全く心当たりのないアドレスというか。もちろんMacに振られてるIPでもDocker用のIPでもない。172で始まるアドレスになってるが、自端末に振られてるIPは192.168のものだ。これはRemote Containersの影響なのかな?
ひとまずダメ元でアクセスしてみます。もちろんダメでした。
それなら、とTunnelモードで接続してみます。だがしかしこれもダメ。
次はLocalにしてみるがこれは明らかにダメだ。アドレスがlocalhostになってる。きっとシミュレータとかでアクセスなんだろうな。
というわけでiPhoneのExpoクライアントからの起動は諦めて、シミュレータでやってみようとRun on iOS simulaterをクリックしてみます。シミュレータって何を使うのかな?と思いつつクリック。
押したらちょろっと右隅にこんなメッセージが出て失敗しました。

Metoro logってどこに出るのか。ターミナル側見てもログらしきものは表示されてないです。
とりあえずXCodeすら入ってないので入れてみると何か改善されるかもしれないと入れてみますが、XCodeをインストールしても状況変わらず。
実はRun in web browserてのもあってこれでやるとアプリがブラウザ上で実行されるのですが、このときのURLをiOSシミュレータ上のSafariでアクセスしてみたところ同じように表示されるのですが、これはなんか違う気がする。
というわけでもう一回考え直してみます。そもそもLAN接続用のアドレスに謎のIPがセットされているのでこれを明示的に自分のIPにしてみます。
これは環境変数でセットできます。
export REACT_NATIVE_PACKAGER_HOSTNAME=192.168.xx.xx
で、この状態で改めてQRコードを読み取ってみるもののどうにもうまくいかない。そもそも自分のMac上で起動しているサービスに別端末からアクセスできるのかな?と怪しく思ってMac上に最初から入ってるApacheを起動してみてiPhone上からアクセスを試みてみるもダメ。もちろんローカルからはアクセスできる。
ちゃんと80番ポートでListenもしてます。

Firewallとかいろいろ疑ってみたものの正直原因がよくわからず…。この端末は仕事で使ってる端末なので自分で購入したものではあるが会社のMDM配下にあるのです。もしかしたらその辺の影響なのかもしれない。
最後にもう一度、今度はTunnelを使ってみる。行けたっぽい。
先程との違いはIPアドレスをセットしただけだ。なお、このTunnelの仕組みはnglokを使ってるみたいでこのあたりに詳細が載ってます。
How Expo Works - Expo Documentation
この後、本来の目的のとあるプロジェクトでも試したのだけど、そちらも問題なく動きました。
というわけで、僕の環境ではTunnel接続しないとダメだったけど特に細かい設定をするとかしなくてもRemote Containersの環境でReact Native + Expoで動かすことはできそうです。IPアドレスの設定をするだけでした。普通の環境ならLAN接続も問題なくいくのではないかと思われます。でもちゃんと開発をしたわけではないのでいざ開発しようとするといろいろあるかもしれない。watchman入れてないし。
それにしてもRemote Containersやはり最高だ。