ReactNativeでのアプリ開発、ExpoSDKがいい感じになってきたようなので試してみた：その５

# はじめに
[前回](https://qiita.com/sakai-y/items/07ce11355f561a1eeea9)までで、モノレポ構成にしたManaged workflowアプリにカスタムネイティブモジュールを組み込むところまで試しましたので、次は「プロプライエタリなライブラリを導入する」をトライします。

こういったライブラリは非公開であるため、パッケージマネージャ（Gradle、CocoaPods/Swift Package Manager）経由でのインストールではなく、バイナリ（Androidならaar、iOSならxcframework）での提供になると思いますので、この形式のライブラリをカスタムモジュールとして作成してみます。

非公開のライブラリでは試せないので、今回はマーケティングサービスであるReproのSDKを使います。

:::note info
実際にReproをReactNativeアプリに組み込む場合は[react-native-repro](https://www.npmjs.com/package/react-native-repro)を利用してください。
:::

# 準備
Android用ライブラリとして、以下から`repro-android-sdk-5.8.0.aar`をダウンロードしておきます。

https://github.com/reproio/repro-android-sdk

iOS用は、以下からアーカイブをダウンロードしておきます。
（バージョンはどれでもOKです）

https://github.com/reproio/repro-ios-sdk/releases

# Reproモジュール作成
## 新しくExpo Moduleを準備する
以下コマンドで新しくモジュールを作成します。

```shell
packages % yarn create expo-module my-repro --no-example
```

`--no-example`オプションをつけても`example`は生成されるようです・・・。
（一応、オプションなしだとBare workflowだったのがManaged workflowにはなる）

他にも不要ファイルがあるので消します。

```shell
packages % cd my-repro
my-repro % rm -rf example
my-repro % rm android/src/main/java/expo/modules/myrepro/MyReproView.kt
my-repro % rm ios/MyReproView.swift
my-repro % rm src/MyRepro.types.ts src/MyReproModule.web.ts src/MyReproView.*
```

## JS/TS
JS/TS側を作成します。

```ts:src/index.ts
import MyReproModule from './MyReproModule';

export function hello(): string {
  return MyReproModule.hello();
}

export function setup(): void {
  MyReproModule.setup();
}
```

:::note warn
Reproの導入ガイドで上記セットアップ処理はAndroidでは`Application#onCreate`で、iOSでは`application:didFinishLaunchingWithOptions:launchOptions`で、それぞれ呼び出すよう記載されていますが、今回はJSからの呼び出しとしたいため、あえて従っていません。
また、本来は契約すると発行されるトークンを利用するIFですが、適当な文字列で・・・。
:::

アプリから呼び出すコードも用意します。

```tsx:labapp/App.tsx
import * as MyRepro from 'my-repro';
import { useEffect } from 'react';

export default function App() {
  useEffect(() => {
    MyRepro.setup();
  }, [])
```

### Android
`my-repro/android`に`libs`ディレクトリを作成し、準備しておいた`repro-android-sdk-5.8.0.aar`を置きます。
これを`build.gradle`に依存として追加します。

```gradle:my-repro/android/build.gradle
dependencies {
  implementation project(':expo-modules-core')
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"

  implementation files('libs/repro-android-sdk-5.8.0.aar')
}
```

`setup`関数を用意します。

```kotlin:my-repro/android/MyReproModule.kt
package expo.modules.myrepro

import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition

import io.repro.android.Repro;

class MyReproModule : Module() {
  private val currentActivity
  get() = appContext.activityProvider?.currentActivity

  override fun definition() = ModuleDefinition {
    Name("MyRepro")

    Function("hello") {
      "Hello world! 👋"
    }

    Function("setup") {
      Repro.setup(currentActivity?.getApplication(), "not-a-valid-token")
    }
  }
}
```

ビルドして確認してみましょう。

```shell
labapp % yarn eas build -e development -p android
```

ビルド完了したらエミュレータへインストールして、`yarn start`で確認します。
画面上は何も変わりませんが、ビルド・起動成功なのでOKのはず。

### iOS
次はiOSです。

`my-repro/ios`に`SDK`ディレクトリを作成し、準備していたアーカイブの中から`Repro.xcframework`を取得し`SDK`へ入れます。
`MyRepro.podspec`で利用する設定を行います。

```ruby:my-repro/ios/MyRepro.podspec
  s.dependency 'ExpoModulesCore'

  s.vendored_frameworks = 'SDK/Repro.xcframework'
  s.private_header_files = 'SDK/**/*.h'
```

:::note warn
`private_header_files`設定を入れないとumbrellaヘッダが生成されてビルドエラーの原因になります。
:::

`setup`関数を作成します。

```swift:my-repro/ios/MyReproModule.swift
import ExpoModulesCore
import Repro

public class MyReproModule: Module {
  public func definition() -> ModuleDefinition {
    Name("MyRepro")

    Function("hello") {
      return "Hello world! 👋"
    }

    Function("setup") {
      Repro.setup(token: "not-a-valid-token")
    }
  }
}
```

ビルドします。

```shell
labapp % yarn eas build -e development-simulator -p ios
```

・・・エラーになりました。

```
❌  Undefined symbols for architecture x86_64
┌─ Symbol: _SCError
└─ Referenced from: -[RPRNetworkStatus startStateListenerInternal] in Repro(RPRNetworkStatus.o)

❌  ld: symbol(s) not found for architecture x86_64


❌  clang: error: linker command failed with exit code 1 (use -v to see invocation)
```

`SCError`というのはiOSのSystem Configuration Frameworkで提供されるものです。
確かに`Repro.podspec`にも利用フレームワークとして記載がありました。
これを追加して再ビルドします。

```ruby:my-repro/ios/MyRepro.podspec
  s.dependency 'ExpoModulesCore'
  s.frameworks     = 'SystemConfiguration'
  s.vendored_frameworks = 'SDK/Repro.xcframework'
  s.private_header_files = 'SDK/**/*.h'
```

・・・今度は成功しました。

シミュレータへのインストールが完了したら、`yarn start`で実行します。

# まとめ
バイナリ形式で提供されるライブラリをカスタムモジュールとしてラップし、Expoアプリから利用できました。

次は・・・Expo Routerを試してみようと思います。

つづく