はじめに
前回までで、モノレポ構成にしたManaged workflowアプリにカスタムネイティブモジュールを組み込むところまで試しましたので、次は「プロプライエタリなライブラリを導入する」をトライします。
こういったライブラリは非公開であるため、パッケージマネージャ(Gradle、CocoaPods/Swift Package Manager)経由でのインストールではなく、バイナリ(Androidならaar、iOSならxcframework)での提供になると思いますので、この形式のライブラリをカスタムモジュールとして作成してみます。
非公開のライブラリでは試せないので、今回はマーケティングサービスであるReproのSDKを使います。
実際にReproをReactNativeアプリに組み込む場合はreact-native-reproを利用してください。
準備
Android用ライブラリとして、以下からrepro-android-sdk-5.8.0.aar
をダウンロードしておきます。
iOS用は、以下からアーカイブをダウンロードしておきます。
(バージョンはどれでもOKです)
Reproモジュール作成
新しくExpo Moduleを準備する
以下コマンドで新しくモジュールを作成します。
packages % yarn create expo-module my-repro --no-example
--no-example
オプションをつけてもexample
は生成されるようです・・・。
(一応、オプションなしだとBare workflowだったのがManaged workflowにはなる)
他にも不要ファイルがあるので消します。
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側を作成します。
import MyReproModule from './MyReproModule';
export function hello(): string {
return MyReproModule.hello();
}
export function setup(): void {
MyReproModule.setup();
}
Reproの導入ガイドで上記セットアップ処理はAndroidではApplication#onCreate
で、iOSではapplication:didFinishLaunchingWithOptions:launchOptions
で、それぞれ呼び出すよう記載されていますが、今回はJSからの呼び出しとしたいため、あえて従っていません。
また、本来は契約すると発行されるトークンを利用するIFですが、適当な文字列で・・・。
アプリから呼び出すコードも用意します。
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
に依存として追加します。
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
関数を用意します。
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")
}
}
}
ビルドして確認してみましょう。
labapp % yarn eas build -e development -p android
ビルド完了したらエミュレータへインストールして、yarn start
で確認します。
画面上は何も変わりませんが、ビルド・起動成功なのでOKのはず。
iOS
次はiOSです。
my-repro/ios
にSDK
ディレクトリを作成し、準備していたアーカイブの中からRepro.xcframework
を取得しSDK
へ入れます。
MyRepro.podspec
で利用する設定を行います。
s.dependency 'ExpoModulesCore'
s.vendored_frameworks = 'SDK/Repro.xcframework'
s.private_header_files = 'SDK/**/*.h'
private_header_files
設定を入れないとumbrellaヘッダが生成されてビルドエラーの原因になります。
setup
関数を作成します。
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")
}
}
}
ビルドします。
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
にも利用フレームワークとして記載がありました。
これを追加して再ビルドします。
s.dependency 'ExpoModulesCore'
s.frameworks = 'SystemConfiguration'
s.vendored_frameworks = 'SDK/Repro.xcframework'
s.private_header_files = 'SDK/**/*.h'
・・・今度は成功しました。
シミュレータへのインストールが完了したら、yarn start
で実行します。
まとめ
バイナリ形式で提供されるライブラリをカスタムモジュールとしてラップし、Expoアプリから利用できました。
次は・・・Expo Routerを試してみようと思います。
つづく