React Native + Expo + WSL で、エミュレータと実機で動かす

## やりたいこと
1. WSL上で作成したReact Nativeプロジェクトを、Windows側のエミュレータで動作させる。
2. 実機（Androidスマホ）でも動作させる。

## この記事で書いていないこと
1. Mac、iOS環境での話
2. EAS Build関連の話

## 環境
Windows 11
WSL2（Ubuntu 24.04.1）

## Windows側設定
### Android Studio
使用バージョン：Android Studio Ladybug(2024.2.1-Patch2)

#### SDK Manager
SDK Managerにて必要なものをインストール。デフォルト選択に加えてAndroid SDK Command-line Toolsを選択した結果、以下のようになってました。

##### SDK Platformsタブ
* [x] Android 14.0 ("UpsideeDownCake")
    * [x] Android SDK Platform 34
    * [x] Sources for Android 34

##### SDK Toolsタブ
* [x] Android SDK BUild-Tools36-rc1
    * [x] 35.0.0
    * [x] 34.0.0
* [x] Android SDK Command-line Tools (latest)
    * [x] Android SDK Command-line Tools (latest)
* [x] Android Emulator
* [x] Android Emulator hypevisor driver (installer)
* [x] Android SDK Platform-Tools

#### AVD作成
AVDを適当に作成。今回はPixel_6aで作成しました。

#### パス設定
`%USERPROFILE%\AppData\Local\Android\Sdk\platform-tools` にパスを通します。

### Androidエミュレータ起動
エミュレータを起動します。バッチとか作っておくと楽かも。
```Powershell:エミュレータ起動
# AVDの名前確認
%USERPROFILE%\AppData\Local\Android\Sdk\cmdline-tools\latest\bin\avdmanager.bat list avd

# ↑で表示されたNameの部分を[Name]に入れて、エミュレータ起動
# メモリ指定してないと固まったので指定したほうが良いのかも
%USERPROFILE%\AppData\Local\Android\Sdk\emulator\emulator.exe "@[Name]" -memory 4096
```

## WSL側準備
node.jsとJDKが必要です。

### node.js
```shell:nodeバージョン確認
$ node -v
v22.11.0
```

### JDK
```shell:Javaバージョン確認
$ java -version
openjdk version "21.0.5" 2024-10-15 LTS
OpenJDK Runtime Environment Temurin-21.0.5+11 (build 21.0.5+11-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.5+11 (build 21.0.5+11-LTS, mixed mode, sharing)
```

### コマンドラインツール ～ SDK  Manager 設定
[https://developer.android.com/studio#command-line-tools-only](https://developer.android.com/studio#command-line-tools-only)
このページの下のほうにある、「コマンドラインツールのみ」のLinux用のリンクをクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564751/830d7441-bf4d-97b5-391c-07923956e8e4.png)

利用規約が出てくるので、同意をチェック。ダウンロードボタンが有効になるので、ここからURLを取得する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564751/908aa36d-5446-3dce-54dd-08a05336c68b.png)

zipを取得して展開する。
```shell:コマンドラインツール設定
# コマンドラインツールの取得
~$ wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
# 展開
~$ unzip commandlinetools-linux-11076708_latest.zip -d Android
# 展開したらzipは削除
~$ rm -rf commandlinetools-linux-11076708_latest.zip

#ディレクトリ構成を変更
~$ cd Android
~/Android$ mv cmdline-tools latest
~/Android$ mkdir cmdline-tools
~/Android$ mv latest cmdline-tools/
```

SDK Managerで必要なパッケージをインストールする。
```shell:SDK Manager設定
$ cd ~/Android/cmdline-tools/latest/bin/
$ ./sdkmanager --install "platform-tools" "platforms;android-34" "build-tools;34.0.0"
$ ./sdkmanager --update
$ ls ~/Android
build-tools  cmdline-tools  licenses  platform-tools  platforms
```

Windows側adbを使うように変更する。
```shell:adbの設定
$ cd ~/Android/platform-tools
$ mv adb adb2
$ ln -s /mnt/c/Users/<username>/AppData/Local/Android/Sdk/platform-tools/adb.exe adb
```

~/.bashrcに以下を追記して反映します。
```bash:~/.bashrc
# Android用設定
export ANDROID_HOME=$HOME/Android
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/tools/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
```

## エミュレータで動作確認
### プロジェクト作成
`create-expo-app`で作成します。TypeScriptを使いたいので`--template`で指定しています。作成完了まで少し時間がかかります。`Your project is ready!`のメッセージが表示されればOKです。
```shell:プロジェクト作成
$ npx create-expo-app@latest wsl-sample-app --template blank-typescript
```

### アプリ実行
WindowsのVSCodeでWSL上のプロジェクトを開き、実行します。
```bash:実行
$ cd wsl-sample-app
$ npm run android
```
Windows側で起動しているエミュレータにExpo Goがインストールされ、無事アプリが起動しました。
<img alt="アプリ起動画面" width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564751/2b74f28d-9aa4-223a-202b-49fed1f1bcc9.png">

少し編集してみます。

```react:App.tsx
import { StatusBar } from 'expo-status-bar';
import { Pressable, StyleSheet, Text, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Pressable style={({ pressed }) => [
        { opacity: pressed ? 0.5 : 1.0 }
      ]}
        onPress={() => alert('Hello world!!')}
      >
        <Text>Press</Text>
      </Pressable>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
```
ホットリロードも効いてるようです。
<img alt="アプリ起動画面_変更後" width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564751/ec26871b-8e9a-ed6c-9de0-101b3c77688c.png"> <img alt="アプリ起動画面_変更後_ボタン押下" width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564751/3f3752b3-cae4-1ed1-ea71-07cdcf2cba51.png">

## 実機で動作確認
### 前提
* Android端末とWindowsは同ネットワーク。
* Android端末にPlayストアから[Expo Goをインストール](https://play.google.com/store/apps/details?id=host.exp.exponent&hl=ja)しておく。

### 方針
`npm start` するとQRコードが表示されます。こちらをAndroid端末のExpo Goアプリからカメラで読み込むと本来は起動できるのですが、今回の場合、WSL側のIPアドレスに向けて接続を試みようとしてしまうため、直接接続はできない状態になっています。
この接続先をWindows側のIPアドレスに変更し、Windowsでポートフォワーディングしてやることで、Android → Windows → WSL という流れにします（--tunnelオプションという手もありますが、今回は使いません）

### ポートフォワーディング設定
WindowsでPowerShellを管理者権限で起動し、以下コマンドを実行します（IPアドレスやポートは適宜読み替えてください）
```powershell:管理者権限にて実行
# 転送設定を追加
netsh interface portproxy add v4tov4 listenport=8081 listenaddress=0.0.0.0 connectport=8081 connectaddress=WSL側(Ubuntu)のIPアドレス};

# 確認
netsh interface portproxy show all
```

### ファイアウォール設定
続いて、ファイアウォールを設定します。
```powershell:管理者権限にて実行
# 受信の規則に、ポート指定での許可設定を追加
New-NetFireWallRule -DisplayName 'WSL Rule' -Direction Inbound -LocalPort 8081 -Action Allow -Protocol TCP

# 確認
Get-NetFirewallRule -DisplayName 'WSL Rule'
```

誤って設定した時などは以下コマンドで削除可能です。
```powershell:管理者権限にて実行
# ポートフォワーディング設定削除時
netsh interface portproxy delete v4tov4 listenport=8081 listenaddress=0.0.0.0

# ファイアウォールルール削除
Remove-NetFireWallRule -DisplayName 'WSL Rule' -Direction Inbound -LocalPort 8081 -Action Allow -Protocol TCP
```

### 動作確認
上記設定後、Android端末のExpo Goで、QRコードは読み込まずにWindowsのIPアドレス（exp://192.168.x.xx:8081等）を直接指定することで、アプリが起動します。

## おわりに
React Native、楽しいのでぜひ触ってみてください！


## 参考リンク
https://midorimici.com/posts/react-native-wsl