Expoでネイティブコードを書く
概要
自分でネイティブコードを書いて、Expo から利用するところまでを書いていきます。
仕組みとか swift の使い方については書かず、サクッと Expo でネイティブコードを動かす部分にフォーカスして書いていきます。
今回利用したサンプルコード
事前準備 プロジェクトの作成
Expo のプロジェクトを作成します。
npx create-expo-app expo-native-code-sample
シンプルにするため今回はプロジェクトをリセットします。
cd expo-native-code-sample
npm run reset-project
動いてることを確認します。
npx expo start
「i」を入力して IOS Simulator で開きました。
.
› Press a │ open Android
› Press i │ open iOS simulator
.
.
i
本題
ネイティブコードを書く準備をする
ネイティブコードを書くための雛形の作成と設定をします。
※ Expo プロジェクトのルートディレクトリで実行。 (例では expo-native-code-sample)
npx create-expo-module --local
今のディレクトリの状態
tree コマンドの実行結果です
tree -I 'node_modules' -L 2
.
├── README.md
├── app
│ ├── _layout.tsx
│ └── index.tsx
├── app-example
│ ├── app
│ ├── components
│ ├── constants
│ ├── hooks
│ └── scripts
├── app.json
├── assets
│ ├── fonts
│ └── images
├── expo-env.d.ts
├── modules
│ └── my-module
├── package-lock.json
├── package.json
└── tsconfig.json
modules/my-module
が作成されました!
ここに swift や kotlin のコードを書いていきます。
※ my-module は自分がつけた名前
.
└── modules/my-module
├── android // Android用のネイティブコード
├── expo-module.config.json
├── index.ts
├── ios // iOS用のネイティブコード
└── src // ネイティブコードを呼び出すためのコード
ネイティブコードを Expo から使う
雛形として作成されたネイティブコードを index ページから利用します。
import { Text, View } from "react-native";
+import MyModule from "@/modules/my-module";
export default function Index() {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
- <Text>Edit app/index.tsx to edit this screen.</Text>
+ <Text>{MyModule.hello()}</Text>
</View>
);
}
コード完全版
import { Text, View } from "react-native";
import MyModule from "@/modules/my-module";
export default function Index() {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>{MyModule.hello()}</Text>
</View>
);
}
ネイティブプロジェクトを生成します。
npx expo prebuild --clean
/android
, /ios
ディレクトリが作成されました!
アプリを起動します。
npm run ios
「Hello world! 👋 」と表示されました!
ネイティブコードを編集する
iOS を対象として、swift のコードを編集していきます。
今回は swift の使い方ではなく、ネイティブコードを書いて Expo から利用することが目的なのでシンプルな修正のみとします。
modules/my-module/ios/MyModule.swift
にhello
関数が定義されているので書き換えてみます。
Function("hello") {
- return "Hello world! 👋"
+ return "Hello Zenn Article 😎"
}
アプリを再起動します。
npm run ios
swift コードの変更内容が反映されました!
Function()
で同期関数、AsyncFunction()
で非同期関数の定義ができます。
/modules/my-module/src/MyModule.ts
で型の定義がされています。
declare class MyModule extends NativeModule<MyModuleEvents> {
PI: number;
hello(): string;
setValueAsync(value: string): Promise<void>;
}
View はこんな感じで使えます。
import { Text, View } from "react-native";
import MyModule, { MyModuleView } from "@/modules/my-module";
export default function Index() {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>{MyModule.hello()}</Text>
<MyModuleView />
</View>
);
}
MyModuleView の Props の型はmodules/my-module/src/MyModuleView.tsx
で定義がされています。MyModuleViewProps
が Props の型になります
import { requireNativeView } from "expo";
import * as React from "react";
import { MyModuleViewProps } from "./MyModule.types";
const NativeView: React.ComponentType<MyModuleViewProps> =
requireNativeView("MyModule");
export default function MyModuleView(props: MyModuleViewProps) {
return <NativeView {...props} />;
}
おわり
ネイティブコードを書いて、Expo で利用することができるようになりました 👏
swift コードを書いて Expo から利用できるようになったので、Expo でも swift でのみ提供されている API などにアクセスできるようになります 🎉
これで Screen Time API なども利用できるみたいです!
まだまだ理解が浅かったり知識が少ないので、勉強していきます〜
その他
Expo Module のコマンドについて
expo modules はライブラリを作るための機能です。
npx create-expo-module
で作成される/android
,ios
ディレクトリと expo prebuild
で作成される/android
,ios
ディレクトリは別物なので注意してください。
(自分が最初記事探してた時に混乱した)
Expo アプリケーションにモジュール(ネイティブコード)を追加したい場合は --local
を使います。
npx create-expo-module --local
ルートの android・ios ディレクトリについて
こちらはexpo prebuild
で自動生成されるものなので、.gitignore
に追加するのがお勧めです。
/ios/
/android/
参考
Discussion