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