🤯

Expoで始めるiOS/Android/Web対応Universal App開発!

2023/05/17に公開

こんにちは!テラーノベルでiOS/Android/Webとフロントエンド周りを担当している @kazutoyoです!

この頃React Native、Expo熱が高まっております🔥

最近ではDiscordのAndroid版もReact Native化されたり、ツイッターの後継として注目されているBlueskyのiOS/AndroidとWebがExpoで作られていたりと、React Nativeの波がまた来ているのを感じています。

今回はそんなReact NativeをExpoというフレームワークを利用し、Web/ネイティブで動作するInstagramのようなアプリケーションを作ってみます。

先に完成品を載せますが、このような Web / iOS / Android で動作するアプリケーション(Universal App)を構築できます🥰

Expoとは

ExpoはReact Nativeをベースにしたフレームワークで、iOS/AndroidのネイティブアプリとWebをサポートしています。
また、Expo Application Services(EAS)と呼ばれるクラウドサービスを利用することで、アプリケーションのビルドやストアへの配布を行うサービスも提供されています。

素のReact Nativeで扱いにくい部分をExpoはいい感じに隠蔽してくれることで快適な開発体験を提供してくれます!

React Nativeってどう?

個人的にはとても気に入っています。

現在テラーノベルではWebViewで作られている一部機能をリニューアルするプロジェクトを進めています。
その部分をマルチプラットフォームで対応できるフレームワークとしてReact NativeとFlutterで検討していました。
Flutterも非常に勢いがあり、コミュニティも活発であると感じていましたが、以下のようなこともあって私たちはReact Nativeを採用することにしました。

  • 元々WebViewで表示していた部分はReact.jsで作られているので、React Nativeに移植がしやすい
    • チームとしてのReactの経験者が多い
  • Web版も提供予定
    • React Native for WebとFlutter on the Webを比較したとき、RNはReact.jsのように変換されるためハマりどころが少なそう
  • Dart/FlutterとTypeScript/ReactだとTypeScript/Reactのほうが好み
  • フロントエンドチームで扱う言語がSwift/Kotlin/TypeScriptがあり、さらにDartも追加することでスイッチングコストが更に高くなる

ExpoでUniversal Appを作ってみよう!

セットアップ

今回はこれらのライブラリを使いながら、Instagramのようなアプリケーションを作ってみます。

  • Expo
  • Expo Router
    • Next.jsのようなファイルシステムでのルーティングライブラリ
  • Tamagui
    • デザインシステム、スタイリング、UIコンポーネントライブラリ
  • React Native Reanimated
    • React Nativeのアニメーションライブラリ。今回はシーン間のShared Element Transitionに利用しています。
  • Next.js
    • Webで実行時に利用
      • 今回は利用していませんが、場合によってはSSRも可能
  • Solito
    • React NativeのnavigationとNext.jsのnavigationを抽象化して扱うためのライブラリ

まずはcreate-tamaguiを使い、 next-expo-solito プロジェクト構成でアプリケーションを作成します。

以下のコマンドを実行し、Project name、Git管理するか、 Pick a templateNext + Expo + Solito を選択します。

npm create tamagui@latest

セットアップ完了後、プロジェクトに移動し yarn webyarn native を実行してみましょう!
yarn web ではWebが起動、 yarn native ではReact Nativeでのネイティブアプリケーションが実行されます。

実際に動かしてみたところこんな感じです。WebとNativeで若干動きが異なりますが綺麗に動いていますね!

プロジェクト構成

create-tamaguiで作成されたプロジェクト構成はこのようなmonorepoの構成となっています。

  • apps/
    • expo : Expo(React Native)でのアプリケーション。Expo Routerにより、 packages/app の画面を配置している。
    • next : Next.jsのアプリケーション。expoパッケージと同様に、Next.jsのルーティングで packages/app の画面を配置している。
  • packages/
    • app :
      • features: apps で利用される機能ごとの画面が実装されている
      • provider : apps で利用される、Providerなどが実装されている
    • ui : TamaguiによるUIコンポーネントやカスタムコンポーネント、 app で利用されるコンポーネントや、デザインシステムの定義などUIに関する実装

また、これら以外にも必要があれば packages にmonorepoとして追加して利用することができます。
詳細につきましてはこちらをご参照ください。
https://tamagui.dev/docs/guides/create-tamagui-app#-add-new-dependencies

コンポーネントの作成

次のようにtamaguiを使いコンポーネントを作成していきます。
Reactのchakra-uiなどを利用されたことがある方でしたら、そこまで違和感がなくスタイリングが出来るかと思います。
https://github.com/kazutoyo/tenostagram/blob/main/packages/ui/src/PhotoGridItem.tsx

Screenの作成

作成したコンポーネントを使い、スクリーンを作成します。
https://github.com/kazutoyo/tenostagram/blob/main/packages/app/features/user/detail-screen.tsx

Expo / Next.jsに配置

コンポーネント、スクリーンを作成したあとは、 apps 配下の exponext のルーティングに作成したスクリーンを配置します。

Expo

例として /app/user/[userId] のページでは次のようにします。
Stack.Screen を使い、スクリーンのタイトルをセットします。

https://github.com/kazutoyo/tenostagram/blob/main/apps/expo/app/user/[userId]/index.tsx

Next.js

Next.jsも同様に行います。
/pages/user/[userId]/index.ts では next/head でタイトルをセットします。

https://github.com/kazutoyo/tenostagram/blob/main/apps/next/pages/user/[userId]/index.tsx

完成品 🎉

このようにWeb/ネイティブで動くアプリケーションができました!

こちらのソースコードはこちら
https://github.com/kazutoyo/tenostagram

まとめ

このように非常に簡単にUniversal Appを構築することが出来ました。
TamaguiでのレイアウトでネイティブとWebで若干挙動が異なることもあり、その点だけ微調整が必要ではありましたが、基本的に1度書けばネイティブアプリとWebの両方が対応できて素晴らしかったです。

先日行われました App.js conf 2023でExpo SDKやExpo Routerの新バージョンが発表されており、今後よりUniversal Appの開発体験や機能が向上していくと予想されます。
https://twitter.com/Baconbrix/status/1656634363847507968?s=20

FlutterやJetpack Composeなど、React Nativeの競合として様々なマルチプラットフォームのUIフレームワークが登場していますが、それぞれが相互にフィードバックを与えながらより良い進化をしていくと良いなと考えています。

テラーノベル テックブログ

Discussion