React NativeでCodepushもネイティブAPIも対応(Expo Bare Workflow)


React Nativeエンジニアのわくてか(@wktq)です。
今回は僕の所属する207株式会社および合同会社ヒトベースにて採用している、
Expo Bare Workflow」についてご紹介します。


ExpoのUpdates APIによって、審査を通さずにOTAアップデートでいつでもコードの更新(codepush)社内テストを行えるようにしつつ、Expoでよく問題となる独自のネイティブコード対応(課金や外部SDKなど)も可能にします。

ExpoのUpdates (code push)

Expoはよく使う共通パッケージを盛り込んだ、Xcode/Android Studioビルド不要でReact Nativeアプリを開発できるツールです。

アプリを更新する時に、毎回審査を通すのは大変です。
そんなときにExpoのUpdatesを使うとこんなことができます。

# production-0.24チャンネルへcodepush
expo publish --release-channel production-0.24

↑のコマンドで、AppStoreに提出済みのアプリ上のJavaScriptがまるっと更新されました。
これによってアプリ開発においてコスト・ボトルネックとなるAppStore / GooglePlayの審査が不要になります。

ここまではExpoを使用したことがある方なら理解していると思います。ここで、問題点もあります。

Expoの仕組みとしては、react-native-unimodulesがpod経由でインストールされます。こちらはexpoの機能で使用するネイティブ関連のpodとそのリンクをラップしたものです。

Expo単体だとunimodulesに入っていないネイティブ機能(例えば課金や外部SDKなど)に対応できないことがあります。

Expo Bare Workflowについて

そこで、ExpoのBare Workflowの出番です。

OTAアップデートを含むExpoの機能を残したまま、XcodeおよびAndroid Studioでのビルドが可能になります。
これによって、独自のpodやネイティブコードの注入が可能になります。

もちろんこれに関しては、react-native-code-pushや、そもそもReact Native以外のソリューションもありますが、全体がwrapされシームレスに(ややこしいappcenter等に依存せずに)、しかも安定して動くのでこちらを使っています。

実際のコード&コマンド

import * as Updates from 'expo-updates';
import { AppLoading } from 'expo';

async expoUpdateReload() {
  const update = await Updates.checkForUpdateAsync();
  if (update.isAvailable) { // アップデートが利用可能か
    await Updates.fetchUpdateAsync(); // JSバンドルをダウンロード
    Updates.reloadAsync(); // アプリをリロード(一瞬白くなります)
  }
}

render() {
  const { store, persistor, isReady } = this.state;
  if (!isReady) {
    return (
      <AppLoading
        startAsync={this.expoUpdateReload}
        onFinish={() => this.setState({ isReady: true })}
        onError={console.warn}
      />
    );
  }
  return <Main />
}

アップデートを行う場合は、expo publishコマンドを利用します。

# production-0.24チャンネルへcodepush
expo publish --release-channel production-0.24

これだけで、としてビルド・提出したReact NativeアプリのJavaScriptが書き換えられます。

もちろんCIツール等と組み合わせて自動デプロイも可能です。弊社207incではGithub Flowでmasterが更新された際に、CircleCIでproduction-.*へ自動的にデプロイされる仕組みになっています。

でもExpoって評判悪いよね。

eject※や挙動の不安定さから、僕も昔は嫌いでした。

※ Expoでは非対応のネイティブ機能を実装する場合に、「eject」という作業をする必要があります。
弊社でもExpoのejectを行いましたが、エンジニア1人まるまる1ヶ月以上の稼働でやっと完了しました。

ただし、Expo Bare Workflowを導入すると、
すでにejectされた状態でinitし、さらにExpoの機能を使えます

(Expo Bare Workflow + OTAアップデートは2020/03 [SDK v37]より使用可能になりました。)

また、関連APIや環境の不具合も昔はよくありましたが、最新のSDKではかなり改善しています。

本当に審査がいらないの?

いくつか審査が必要なタイミングがあります。

  • ネイティブモジュールを追加する場合
  • リンクのSchema変更などで、Info.plist / Manifestを書き換えたい場合
  • expoのSDKバージョンを上げたい場合

ざっくりこういった場合です。

またhttps://qiita.com/muzou/items/5b990e03cb1a4084e33d こちらの記事で触れられているように、OTAアップデートのバージョン管理方法については注意が必要です。間違ったバージョンに向けてexpo publishを行えば、壮大にクラッシュしかねません。

リリースチャンネル(バージョン)管理方法

後ほどアップデート予定
管理方法については長くなるので別途記事を書こうと思います。

Flutterよりいいの?

今のところはFlutterだとOTAが公式対応されていないことや、まだthird partyがReact Nativeの方が充実している(60% - 70%のイメージ)ことから、現状はReact Nativeを採用しています。

また、僕はフロント出身でReactの方が直感的(視覚的に)に書けるので、個人的にはReact Nativeが好きです。
要件によってはFlutterの採用も検討しているとかいないとか。

KotlinかけるならMPPも選択肢としてありそうです。
https://www.wantedly.com/companies/wantedly/post_articles/282562

Join Us

弊社207, Incでは、エンジニアを絶賛募集中です!このExpo Bare WorkflowおよびGraphQLでゴリゴリ開発したいReact Nativeエンジニアの方はぜひ募集を見てみてください!

急成長中の物流Techベンチャーを牽引するフロントエンドエンジニアを募集!

会社概要:
https://207-inc.com

まとめ

React Nativeで新規プロジェクトを作る場合で、特にパッケージ容量などのこだわりがなければBare Workflowで始めることをお勧めします。

開発の速度は間違いなく上がりますし、expo publishのバージョン管理や命名規則だけしっかり行っていれば、問題なく運用できるかと思います。

詳しく聞きたい方はTwitterのDMまで!
https://twitter.com/wktq_tenova