メインコンテンツまでスキップ

Expo SDK 51アップグレード

参考

以下の記事を参考にして、このアプリのExpo SDKを51にアップグレードしました。 主な変更点とこのアプリで実施したアップグレード手順を紹介します。

なお、使用される可能性の低いEASとReact Native Webに関する内容は記載しません。

Expo SDK 51の主な変更

React Native v0.74.5

詳細は、以下のリンク先を参照してください。

Reactのバージョンは変更されておらず、依然としてv18.2.0です。

Yoga 3.0

React NativeのレイアウトエンジンであるYogaのバージョンが3.0になりました。レイアウトの正確性が向上し、以下のような機能が利用可能になりました。

  • align-content: 'space-evenly'
  • position: 'static'

詳細は以下の記事を参照してください。

注意点

flexDirection: 'row-reverse'が設定されているコンテナで、margin/padding/borderの振る舞いが変更されています。left/right/start/endの扱いがWebと異なっていたのですが、同じように振る舞うよう修正されています。

影響を受けるコード例
import {View} from "react-native";
<View
style={{
flexDirection: 'row',
backgroundColor: 'red',
margin: 10,
width: 200,
height: 100,
}}>
<View
style={{
flexDirection: 'row-reverse', // row-reverseを設定
backgroundColor: 'blue',
flex: 1,
marginLeft: 50, // Yoga 2.0だと右側にマージンが取られていたが、Yoga 3.0では左側にマージンが取られるように修正された
}}>
<View
style={{
backgroundColor: 'green',
height: '50%',
flex: 1,
marginLeft: 50,
}}
/>
</View>
</View>
Yoga 2.0Yoga 3.0
Yoga 2.0 row-reverse edge handlingYoga 3.0 row-reverse edge handling

row-reverseが設定されているときにleft/right/start/endも反転して扱われていましたが、Webと同じ振る舞いになるように修正されています。

PropTypesの廃止

React Native 0.73まではReact 15.5で廃止予定とされていたPropTypesが含まれていましたが、0.74ですべて削除されました。

もしPropTypesを利用している場合は、TypeScriptの型システムに移行することが強く推奨されています。

PushNotificationIOSの廃止予定

React Native 0.74ではPushNotificationIOSが廃止予定となっています。

この機能は0.75で削除されており、次のアップグレードまでにコミュニティの@react-native-community/push-notification-iosに移行する必要があります。

Apple Privacy Manifest対応の追加

2024年5月1日から、App Storeにアプリを提出する際にプライバシーマニフェストが必要となっています。

Appleの定義する理由の宣言が求められるAPIをアプリのコードやSDKで利用している場合、それらの利用が承認される理由をプライバシーマニフェストファイルに記載する必要があります。

また、Appleの指定するサードパーティSDKを利用している場合もプライバシーマニフェストを含める必要があります。なお、これらのサードパーティSDKに関しては、SDKにプライバシーマニフェストが含まれるバージョンへの更新でも対応可能です。

プライバシーマニフェストが必要とされるにも関わらずApp Store Connectへアップロードしたアプリに含まれていない場合には、App Store Connect上で警告が表示されます。

ExpoのConfig Pluginを利用している場合、プライバシーマニフェストファイルに記載する内容をapp.config.jsで設定できるようになりました。設定方法の詳細については、Privacy manifests - Expo Documentationを参照してください。

APNsのエンタイトルメントが自動的には追加されないように変更

expo-notificationsを利用していない場合は、APNsのエンタイトルメントは自動では追加されないように変更になりました。

expo-notificationsを利用していない場合にiOSで通知機能を利用する場合は、以下のようにapp.config.jsに設定を追加する必要があります。

app.config.js
module.exports = {
expo: {
ios: {
entitlements: {
'aps-environment': 'development',
},
}
}
}

Expo Goのサポート対象が最新版のみに変更

Expo Goはこれまで複数バージョンのExpo SDKで実装されたアプリを動かすことができていました。例えば、SDK 50時点のExpo Goでは、SDK 49で実装されたアプリを動かすことができました。

SDK 51からは、このサポート対象が最新版のみに変更されました。そのため、Expo SDK 51がリリースされたあとのExpo GoではSDK 51のアプリのみ動作し、SDK 50のアプリは動作しなくなっています。

Expo Go - Expoから対応するバージョンのExpo Goをダウンロードして利用することで、iOS実機以外では古いSDKのアプリをExpo Goで動かすことができます。しかし、iOSの実機では古いバージョンのExpo Goを動かすことはできません。

そのため実際のアプリ開発では、Expo Goではなくdevelopment buildの利用が推奨されています。

CameraとSQLiteの新APIがデフォルトで利用されるように変更

Expo 50で追加されたexpo-camera/nextexpo-sqlite/nextがデフォルトになり、expo-cameraおよびexpo-sqliteとして公開されるよう変更されました。

Expo 50まではデフォルトで公開されていた旧APIはexpo-camera/legacyexpo-sqlite/legacyに変更されました。

旧APIはSDK 51の間は利用できますが、SDK 52では削除される予定となっています。リリースノートでは、SDK 51へのアップグレード時は旧APIを使うように修正し、アップグレードが完了してから新APIに移行する手順が推奨されています。

expo-symbolの追加

AppleのSF Symbolsを利用できるように、expo-symbolsというライブラリが追加されました。AndroidやWebなどSF Symbolsを利用できない環境では、fallbackとして指定したコンポーネントが表示されます。

詳細については、Symbols - Expo Documentationを参照してください。

Expoがサポートするライブラリや機能

以下に挙げる変更点の詳細やその他の変更については、ExpoのChangelogを参照してください。

ライブラリの更新

ライブラリの新機能

  • eslint-config-expo
    • Expoで開発するときのベースとなるESLint設定が追加されました
    • Expoプロジェクトでnpx expo lintを実行すると、ESLintの設定ファイルが存在しなければeslint-config-expoを使う設定ファイルが作成されます
    • 詳細はUse ESLint and Prettier - Expo Documentationを参照してください

このアプリで実施したアップグレードの手順

このアプリでは、Expo 51へのアップグレード方法を参考に、以下の作業を実施してExpo SDK 51にアップグレードしました。

アップグレードを実施したプルリクエストはPull Request #1322 · ws-4020/mobile-app-crib-notesです。

  1. ExpoとExpoが管理するライブラリのアップグレード
  2. npx expo-doctorでバージョン整合性を確認
  3. 既存パッチファイルの更新
  4. ビルドなどに利用するツールのバージョンを更新
  5. APNsのエンタイトルメントを追加
  6. defaultPropsの利用箇所を修正
  7. 静的解析エラーの修正
  8. 自動テストの失敗を修正
  9. Expoの更新履歴確認と対応
  10. React Nativeの更新履歴確認と対応
  11. expo-template-blank-typescriptの更新履歴確認と対応
  12. expo-template-bare-minimumの更新履歴確認と対応
  13. ライセンス情報を更新

ExpoとExpoが管理するライブラリのアップグレード

npx expo install expo@^51.0.0 --fixを実行して、Expo SDK 51のexpoパッケージをインストールします。また、あわせてExpoが管理するライブラリもアップグレードします。

アップグレードの実行後に以下のログが出力されたため、app.config.jsを修正しました。

Cannot automatically write to dynamic config at: app.config.js
Please add the following to your Expo config

{
"plugins": [
"expo-secure-store"
]
}
app.config.js
       [
'expo-build-properties',
// 〜〜〜中略〜〜〜
],
+ ['expo-secure-store'],
['@react-native-firebase/app'],
['@react-native-firebase/crashlytics'],
// このアプリで用意しているAndroid/iOS共通のプラグイン

npx expo-doctorでバージョン整合性を確認

npx expo-doctorで確認したところ、いくつかのライブラリのバージョンについて警告が出力されました。

Expected package @expo/config-plugins@~8.0.0
Found invalid:
@expo/config-plugins@7.9.2
(for more info, run: npm why @expo/config-plugins)
Advice: Upgrade dependencies that are using the invalid package versions.

The following scripts in package.json conflict with the contents of node_modules/.bin: orval.

The following packages should be updated for best compatibility with the installed expo version:
@expo/config-plugins@7.9.2 - expected version: ~8.0.0
babel-preset-expo@10.0.2 - expected version: ~11.0.0
jest-expo@50.0.4 - expected version: ~51.0.4
typescript@5.1.6 - expected version: ~5.3.3
Your project may not work correctly until you install the expected versions of the packages.
Found outdated dependencies
Advice: Use 'npx expo install --check' to review and upgrade your dependencies.

アドバイス通り、npx expo install --checkを実行してバージョンの不整合を解消しました。

既存パッチファイルの更新

このアプリでは、patch-packageを使用して、以下のライブラリにパッチファイルを適用していました。 パッチ内容の詳細は、こちらを参照してください。

  • @expo/config-plugins
  • expo-splash-screen
  • react-native-elements

expo-splash-screenは、Expo SDKのアップグレードに伴いバージョンが上がりました。しかし、適用していたパッチファイルはまだ必要な対応だったため、パッチファイルは削除せずに各ライブラリのバージョンに合わせてファイル名をリネームしました。

@expo/config-pluginsはまだ必要な対応だったのですが、バージョンの更新に起因してパッチが失敗するようになりました。修正内容には変更はなかったため、利用しているバージョンでのパッチが成功するようにパッチファイルを作成し直して対応しました。

react-native-elementsに関しては、バージョンが変わらなかったため変更はありません。

ビルドなどに利用するツールのバージョンを更新

Node.jsのActive LTSはすでに20系になっているため、このアプリでもNode.js 20系を利用するように修正しました。

APNsのエンタイトルメントを追加

APNsのエンタイトルメントは自動的に追加されなくなったので、app.config.jsで設定するように修正しました。

app.config.js
         UIBackgroundModes: ['fetch', 'remote-notification'],
},
associatedDomains: [`applinks:${DEEP_LINK_DOMAIN}`],
+ entitlements: {
+ 'aps-environment': 'development',
+ },
},
disabledPlugins: [
// default plugin を無効化するために patch-package を使用して機能拡張している

defaultPropsの利用箇所を修正

アップデート後にアプリを起動すると、以下のようなエラーが表示されるようになりました。

 ERROR  Warning: Overlay: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.
in Overlay (created by LoadingOverlayComponent)
in LoadingOverlayComponent (created by Component)

defaultPropsを利用している箇所については、以下のようにすべてデフォルト引数に変更して対応しました。

Overlay.tsx
 export const Overlay: React.FC<React.PropsWithChildren<OverlayProps>> = ({
visible,
onHideEnd,
- fadeDuration,
+ fadeDuration = 200,
style,
...props
}) => {
@@ -89,10 +89,6 @@
);
};

-Overlay.defaultProps = {
- fadeDuration: 200,
-};
-
const styles = StyleSheet.create({
overlay: {
flex: 1,

iOSでFullWindowOverlayを使用しているコンポーネントが表示されない問題の対応

アプリ起動後、スナックバーなどFullWindowOverlayを使用しているコンポーネントが表示されなくなっていました。
この事象は、アプリ起動後に以下のようなExpo DevClientのメニューが表示された場合のみ発生するようです。

Expo DevClientメニュー

Expo DevClientのメニューに表示されているReloadをタップして、アプリを再読み込みするとスナックバーなどは表示されるようになります。
そのため、このアプリではこの運用で回避することにしました。

静的解析エラーの修正

ソースコードフォーマットが微妙に変更されていたため、いくつかエラーが発生するようになっていました。

すべて自動修正可能なエラーだったので、npm run fixで修正して対応しました。

自動テストの失敗を修正

expo-constantsのモックからdefault以外もexportするように修正

Expo内部でexpo-constantsAppOwnershipなどを利用するようになっていたのですが、モックがそれらを公開していなかったため以下のようなエラーが発生しました。

TypeError: Cannot read properties of undefined (reading 'Expo')

モックファイルからデフォルトエクスポート以外もエクスポートするように修正して対応しました。

jest/__mocks__/expo-constants.ts
 import {wrapProperty} from '../utils/wrapProperty';

+export * from 'expo-constants';
+
export const Constants = wrapProperty(ExpoConstants, {
expoConfig: {
name: 'SantokuApp',

Reanimated.ViewのanimatedPropsに渡す値を修正

React Native ReanimatedのViewにはShared Valueを渡す必要があるため、useAnimatedPropsを利用するようガイドされています。

しかし、一部のテストコードでuseAnimatedPropsから取得した値を渡していない箇所があり、以下のようなエラーが発生するようになっていました。

TypeError: Cannot read properties of undefined (reading 'remove')

useAnimatedPropsで取得したものを渡すようにテストコードを修正して対応しました。

 describe('PickerContainer with all props', () => {
it('should be applied properly', async () => {
const afterSlideIn = jest.fn();
+ const {result: animatedProps} = renderHook(() =>
+ useAnimatedProps(() => {
+ return {
+ pointerEvents: 'none',
+ } as const;
+ }, []),
+ );
/**
* WithTimingConfigのeasingを取得できなかったため、以下のPropsは検証できていません。
* - slideIntConfig
* - slideOutConfig
*/
const sut = render(
<PickerContainer
isVisible
testID="animatedView"
- animatedProps={{pointerEvents: 'none'}}
+ animatedProps={animatedProps.current}
style={{backgroundColor: 'green'}}
slideInDuration={200}
afterSlideIn={afterSlideIn}

Expoの更新履歴確認と対応

ExpoのCHANGELOGを参照して、Expo SDKとExpoが管理するライブラリの更新内容を確認しました。

ここまでの対応以外に追加で必要なものはありませんでした。

React Nativeの更新履歴確認と対応

React NativeのCHANGELOGを参照して、React Nativeの更新内容を確認しました。

ここまでの対応以外に追加で必要なものはありませんでした。

expo-template-blank-typescriptの更新履歴確認と対応

expo-template-blank-typescriptの更新履歴を確認しました。

npx expo install expo@^51.0.0 --fixで更新される依存ライブラリのアップグレードが主な変更でした。そのため、このアプリで特別な対応は必要ありませんでした。

expo-template-bare-minimumの更新履歴確認と対応

このアプリではConfig Pluginsに対応しているので、expo-template-bare-minimumの更新に伴う個別の対応は基本的に必要ありません。 ただし、以下の場合は個別に対応する必要があるため、この観点に絞ってexpo-template-bare-minimumの更新履歴を確認しました。

  • このアプリで作成しているConfig Pluginsによる変更と、expo-template-bare-minimumの更新に伴う変更が競合した場合
  • Prebuild時に生成・更新されないファイル
  • android/, ios/以外のファイル(.gitignoreなど)

ここまでの対応以外に追加で必要なものはありませんでした。

ライセンス情報を更新

このアプリでは、使用しているライブラリのライセンス一覧を出力するスクリプトを用意しています。詳細は、こちらを参照してください。

managed-license.jsの更新

ライセンス情報が不足しており補完したい、あるいは、開発時のみ使用するため除外したいライブラリとバージョンをmanaged-license.jsで管理しています。

ライセンス情報が不足しているライブラリなどは、以下のコマンドを実行することで確認できます。

  • node .script/check-licenses.js

実行した結果、いくつかのライブラリのライセンス情報を更新する必要があったので以下を実施しました。

  • 使用ライブラリの名前更新
  • 使用ライブラリのバージョン更新
  • 使用ライブラリのライセンスファイルURL更新
  • 新規ライブラリ情報の追加
  • 使用しなくなったライブラリ情報の削除