ReactNative 0.77でのアプリ作成

いつの間にか色々変わっていて大分面倒臭くなっている

~~一番の変更はローカルビルドが出来なくなって~~
~~ビルドにものすごい時間がかかるようになっている~~
ローカルビルド出来るっぽい


# 共通

```shell
npx create-expo-app@latest [option]
```
option部分にアプリ名を入れると対話形式での作成が不要になる

ちなみに`npx react-native init [アプリ名]`は動かなくなっている


```
cd [アプリ名]
npx expo prebuild
```
これを行わないとandroidディレクトリは作成されない
ちなみにiosディレクトリはMacOS環境で無いと作成されない

## ソースコード

ルートにある`App.tsx`が無くなっていて、`react-navigation`もうまく動作しない
`app`ディレクトリ内の`_layout.tsx`が開始位置と思われる

`react-navigation`の代わりには`expo-router`を使う

### expo-router
例)
`_layout.tsx`がこんな感じにあったとしたら
```react
    <ThemeProvider value={DefaultTheme}>
      <Stack>
        <Stack.Screen name="index" />
        <Stack.Screen name="device" options={{title: 'Unknown Device Name'}} />
      </Stack>
    </ThemeProvider>
```

indexからdeviceへは
`Link`の`href`にこんな感じで記載する

```react
      <Link style={styles.line} onPress={() => { startScan(false) }} href={"./device?address=" + item.address + "&name=" + item.name}>
...
      </Link>
```

# android

実行方法は変化なし
```
npm run android
```

## Native Module

以前のNativeModuleは動作しない

以前の例)
```react
import { NativeModules } from "react-native";

const native = NativeModules.BLEModule

export const ble = { 
    register: async (keyno, uuid, input, cb) => {
        native.register(keyno, uuid, input, cb) 
    },  
    open: async (cb) => {
        const { key_no, uuid, key } = await getKeyInfo()
        native.open(key_no, uuid, key, cb) 
    },  
...
```

[TurboModule](https://reactnative.dev/docs/turbo-native-modules-introduction)というのを使う必要がある

詳細はリンク先の通りだが

specファイルを適切に記載する必要がある

例) app/specs/NativeBLE.ts
```react
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  start_scan(): void
  stop_scan(): void
  connect(address: string): void
  disconnect(): void
  send_data(val: string): void
}

export default TurboModuleRegistry.getEnforcing<Spec>(
  'BLEModule',
);
```
getEnforcing内のモジュール名はNativeModuleの名前に依存する
以下のgetName部分
```kotlin
class BLEModule(reactContext: ReactApplicationContext?) : NativeBLESpec(reactContext),
    BLEInterface {

    private var mManager: BLEManager? = null
    private val foundedDevices: HashMap<String, BluetoothDevice> = HashMap()
    private var lastConnected: String? = null

    init {
        ContextHolder.init(reactContext)
    }

    override fun getName() = NAME
...
```    

package.jsonには以下のように記載する
```json
  "codegenConfig": {
    "name": "NativeBLESpec",
    "type": "modules",
    "jsSrcsDir": "app/specs",
    "android": {
      "javaPackageName": "com.yano.interplan.imble3_app"
    }
  },
```
package.jsonの"name"欄にはspecファイルのファイル名に依存するので注意
ファイル名(除くsuffix)+'Spec'

### EventEmitteer

EventEmitterは以前と同じ様に使用出来る

android側の例
```kotlin
    private fun sendEvent(data: String) {
        val params = Arguments.createMap()
        params.putString("json", data)

        val context = ContextHolder.context
        val module = context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)

        if (module == null) {
            dlog("")
            return
        }

        module.emit(EVENT_NAME, params)
    }

```

受け側の例
```react
  useEffect(() => {
    const eventEmitter = new NativeEventEmitter(NativeModules.DeviceFound)
    let eventListener = eventEmitter.addListener(EVENT_NAME, event => {
      if (event && event.json) {
      ...
      }
    })
    return () => {
      eventListener.remove()
    }
  })
```

## アイコンの変更

`assets/images/icon.png`と`assets/images/splash-icon.png`を変更して
`npx expo prebuild`するだけ

サイズ別のファイルを用意する必要も無くなって
すごい楽になった

なお、以前と同じ方法をすると`prebuild`で上書きされるので注意

<details><summary>以前の方法</summary>

android/app/src/main/res/mipmap-xxx内の
ic_launcher.webp
ic_launcher_foreground.webp
ic_launcher_round.webp
をそれぞれ
ic_launcher.png
ic_launcher_foreground.png
ic_launcher_round.png
に置き換える

</details>

## apkファイルの作成

コード更新後に必ずこれを実行する
```
npx expo prebuild
```

androidディレクトリに移動して`build`ディレクトリを削除
```
cd android
rm -rf build
```

NativeModuleを使用している場合は以下も実行する
```
./gradlew generateCodegenArtifactsFromSchema
```

ルートに戻って
```shell
cd ..
```

eas.jsonを作成して
```json
{
  "build": {
    "preview": {
      "android": {
        "buildType": "apk"
      }
    },
    "preview2": {
      "android": {
        "gradleCommand": ":app:assembleRelease"
      }
    },
    "preview3": {
      "developmentClient": true
    },
    "preview4": {
      "distribution": "internal"
    },
    "production": {}
  }
}
```

expo-cliは必須
```shell
npm install -g expo-cli
```

[expo](https://expo.dev/)にアカウントを作る
メールとパスワードだけなので簡単だと思う

アカウントを作成してメール認証したらログインする

```
eas login
```
対話形式なのでメールアドレスとパスワードでログインする

easでローカルビルド
```shell
eas build --local -p android --profile preview --clear-cache
```

こんな感じで結果が表示されるので
```
Build successful
You can find the build artifacts in /home/tyano/workspace/react_native/sample_app/build-1738129212397.apk
```

普通にインストール
```
adb install /home/tyano/workspace/react_native/sample_app/build-1738129212397.apk
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786381/728c91e1-f57e-7eaa-fa45-e9212f9df38e.png" alt="sample.png" width="300px">




<details><summary>old</summary>

ローカルでのビルドは出来ないらしい



ビルド(普通に数10分かかる)
```shell
eas build --platform android
```
</details>

# ios

## expo

MacOS以外で作ったプロジェクトなら
expoをインストールする

```shell
npm install expo
```

iosディレクトリを作成してもらう
```
npx expo prebuild
```

## Gemfile
なぜかGemfileは存在しないので自分で作る

```shell
cd ios
bundle init
vim Gemfile
```

`gem "cocoapods"`を追記

## cocoapodのインストール
```
bundle install

bundle exec pod install
```

## NativeModule

iosのNativeModuleはあんまり変わっていない

以下の部分が変更になったくらい
```
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
  return std::make_shared<facebook::react::NativeLocalStorageSpecJSI>(params);
}
```

詳細は[公式](https://reactnative.dev/docs/turbo-native-modules-introduction?language=typescript&platforms=ios)の通り

<details><summary>作成中</summary>

</details>