Expo SDK 49アップグレード
以下の記事を参考にして、このアプリのExpo SDKを49にアップグレードしました。 主な変更点とこのアプリで実施したアップグレード手順を紹介します。
- Expo SDK 49. Today we’re announcing the release of… | by Brent Vatne | Exposition
 - React Native 0.72 - Symlink Support, Better Errors, and more · React Native
 
なお、使用される可能性の低いEASとReact Native Webに関する内容は記載しません。
Expo SDK 49の主な変更
React Native v0.72.4
詳細は、以下のリンク先を参照してください。
Reactのバージョンは変更されておらず、依然としてv18.2.0です。
非推奨となっていた以下のコンポーネントは、React Native v0.72.0で削除されたことに注意してください。
コミュニティパッケージへの移行が推奨されています。
デバッグ機能の改善
ネットワークデバッグ
アプリがexpo-dev-clientまたはExpo Goで実行されるときのネットワークリクエストをJSデバッガーで確認できるようになりました。
React devtoolsとExpo CLIの連携
Shift+mを押して「Start React devtools」を選択することで起動できるようになりました。
VS Codeでのデバッグ
Expo Tools Extensionを使用して、VS CodeからアプリのJavaScriptコードを直接デバッグするための実験的なサポートが追加されました。
TypeScript バージョン ^5.1.3
SDK 48までは^4.9.4が使用されていました。
TypeScript ^5の詳細は、以下のリンク先を参照してください。
v5.2.2が使用されないようにするため、^5.1.3ではなく~5.1.3指定を推奨します。@typescript-eslint/typescript-estree(v5)がv5.1.xまでしかサポートしていません。
const SUPPORTED_TYPESCRIPT_VERSIONS = '>=3.3.1 <5.2.0';
関係するライブラリの依存関係は以下のとおりです。
expo-module-scripts(v3.0.x)eslint-config-universe(v11)@typescript-eslint(v5)
Expoがサポートするライブラリや機能
ライブラリの更新
Expoが管理しているライブラリの内、メジャーバージョンアップなど大きなリリースがあったものを記載します。
- expo-router 
v2.0 - sentry-expo 
v7.0 - @react-native-community/datetimepicker 
v7 - react-native-reanimated 
v3- Reanimated API 
v1のサポートが削除されました。v2とは互換性があります - 参考:Announcing Reanimated 3. Reanimated 3 release candidate is out…
 
 - Reanimated API 
 - expo-blur
- デフォルトで静的レンダリング時にぼかしが有効になりました
 
 - expo-face-detector
- Expo Goでの
expo-face-detectorのサポートが削除されましたが、これまでと同様に、Expo Goの外部でライブラリを引き続き使用できます 
 - Expo Goでの
 - expo-gl
- ReanimatedワークレットからGLViewを使用するには、明示的なプロパティ
enableExperimentalWorkletSupportが必要になりました - 参考:Pull Request #22613 · expo/expo
 
 - ReanimatedワークレットからGLViewを使用するには、明示的なプロパティ
 
詳細やその他の変更は、以下のリンク先を参照してください。
Expo Module APIの改善
ローカル モジュールが導入されました。 これにより、ライブラリを作成したり、アプリのネイティブ プロジェクトを管理したりせずに、アプリ内でネイティブ コードを作成できるようになります。 SDK 49のプロジェクトで以下のコマンドを実行することで試すことができます。
npx create-expo-module --local
さらに、コンポーネント参照にネイティブの非同期関数を追加できるようになりました。 同期関数はJavaScriptValue、JavaScriptObject、JavaScriptFunctionなどのJS型の引数を受け取ることができるようになりました。
詳細は、JavaScript valuesを参照してください。
(iOSシミュレータ)AppleシリコンによるExpo Goのネイティブ実行サポート
TestFlightビルドを使用して、シミュレータを使用せずにAppleシリコンMac上でExpo Goを直接実行もできます。 ただし、UIの問題がいくつかあるようで、修正が行われています。
Appleシリコン上でネイティブ実行の詳細は、以下のリンク先を参照してください。
開発ビルドフラグ(--dev-client)の削除
npx expo start --dev-clientの--dev-clientフラグは必要なくなりました。
expo-dev-clientパッケージがプロジェクトにインストールされている場合、デフォルトで開発ビルドがターゲットになります。
キーボードショートカットs(switch)を使用して、開発ビルドとExpo Goターゲットを切り替えることができます。
これにより、QRコードが指すURLと、キーボードショートカットa(Android)とi(iOS)で起動するURLが変更されます。
expo export:embed
Bundle React Native code and imagesビルドフェーズの@react-native-community/cliバンドルコマンドが置き換わりました。
これにより、カスタムエントリポイントのサポートを追加できるようになりました。
package.jsonのmainを変更して、任意のソースファイルを指すようにできます。
Expo Routerを使用していない場合は、新しいエントリファイルでregisterRootComponentを使用する必要があります。
Expo CLIによるMetro環境変数のサポート
React Nativeエコシステムの既存のアプローチには、変数を更新するためにBabelキャッシュをクリアする必要があるなどの問題がありました。
新しいアプローチでは、プロセスをExpo Metro構成に統合し、JavaScriptエコシステムで一般的な規則に従って環境変数名の接頭辞にPUBLICを含めて、これらの問題が解決されます。
SDK 49では、EXPO_PUBLIC_プレフィックスを付けて.envや.env.localなどのファイルで変数を定義でき、それらが使用される場合はアプリのJavaScriptに組み込まれます。
詳細は、以下のリンク先を参照してください。
パッケージバージョン検証の選択的オプトアウト
環境によっては、Expoが推奨するバージョンとは異なるパッケージのバージョンを使用したい場合があります。
package.jsonのexpo.install.excludeのキーがサポートされるようになり、expo startなどで行われる検証から除外するパッケージを指定できるようになります。
詳細は、以下のリンク先を参照してください。
scheme (app config)
以前は、追加のschemeを設定するにはConfig Pluginsを使用する必要がありました。 文字列の配列または文字列を使用できるようになりました。
scheme: ['myapp', 'fb1234'],
// or
scheme: 'myapp',
New Architecture/Fabricのサポート
expo-dev-clientでFabricが実験的にサポートされました。
npx create-expo-app@latest -e with-new-arch で試すことができます。
これにより、新しいアーキテクチャをサポートしていないモジュールはexpo-updatesのみになりました。
Android Expo Modules
Gradle 8を使用するようになりました。
マイグレーションする方法については、以下のリンク先を参照してください。
Expo CLI
すべてのプロジェクトでデフォルトのポート19000ではなく8081を使用するようになりました。
JSCのリモートデバッグ無効化(Expo Goおよびexpo-dev-client)
JSCリモートデバッグは、Hermesによるデバッグに比べて特にうまく機能することはなく、時間の経過とともに信頼性が低くなってしまいました。
詳細については、以下のReact Nativeへのプル リクエストを参照してください。 これは次のReact Nativeリリースに含まれる予定です。
Expo Constants
Constants.manifestが非推奨になりました。代わりに、Constants.expoConfigを使用してください。
詳細は、以下のリンク先を参照してください。
expo-auth-session
expo-auth-sessionのuseProxyとAuthSession.startAsyncが削除されました。
認証プロバイダーがカスタムschemeにリダイレクトしない問題を回避するために認証プロキシを使用していた場合は、Expo Goから開発ビルドに切り替え、ネイティブSDKの使用が推奨されています。
android:usesCleartextTraffic
システムのデフォルト値を使用するようになりました。
デバッグ ビルドでは明示的にtrueが設定されますが、他のビルドタイプ(例:リリースビルド)では指定されていません。
つまり、API 27以下ではデフォルトでtrue、API 28以降ではデフォルトでfalseになります。
セキュリティで保護されていないエンドポイントへのネットワークリクエストが存在する場合は、expo-build-propertiesを使用してtrueにする必要があります。
iOS用アイコンの簡素化
Xcode 14以降では自動的にサイズ変更される単一の1024x1024画像を使用してアプリアイコンを簡素化できます。
そのため、Expoではprebuild時に1024x1024画像のみを生成するようになりました。
- Xcode 14 Release Notes | Apple Developer Documentation
 - feat(prebuild): Generate universal 1024x1024 iOS icon only. by EvanBacon · Pull Request #22833 · expo/expo
 
Expo SDK 46のサポート終了
Expo SDK 46がサポート対象外になりました。Expo SDK 46を使用している場合は、Expo SDK 47以降にアップグレードする必要があります。
なお、次のリリースではExpo SDK47と48のサポートが終了する見込みのようです。
このアプリで実施したアップグレードの手順
このアプリでは、Expo SDK 48 から 49 にアップグレードする方法を参考に、以下の作業を実施してExpo SDK 49にアップグレードしました。
npm install expo@^49.0.0を実行して、Expo SDKをアップグレードnpx expo install --fixを実行して、Expoが管理するライブラリのアップグレード- TypeScript 
v5.2.xではなくv5.1.xを使用するためnpm install typescript@~5.1.3を追加で実行(参考) 
- TypeScript 
 - 既存パッチファイルの更新
 jest-expoのアップグレード- プロジェクトの依存関係に問題がないか確認
 - TSCエラーへの対応
 - テストコードの修正
 - 警告ログへの対応
 npm run prebuildを実行してネイティブプロジェクトの再生成- ビルドエラーの修正
 - renovate除外設定更新
- 使用しなくなった
androidx.swiperefreshlayout:swiperefreshlayoutを削除 
 - 使用しなくなった
 - Config Plugins
withAndroidRemoveUsesClearTextTrafficForReleaseを削除 - Proguardルールの更新
 - Expoの更新履歴確認と対応
 - React Nativeの更新履歴確認と対応
 expo-template-blank-typescriptの更新履歴確認と対応expo-template-bare-minimumの更新履歴確認と対応- React Native Upgrade Helperを参照して、React Nativeの更新を確認
- このアプリで必要な対応は、
expo-template-bare-minimumの更新で対応されていました 
 - このアプリで必要な対応は、
 - 手動管理しているライセンスの更新
 
アップグレードを実施したPull Requestはws-4020/mobile-app-crib-notes#1221です。
既存パッチファイルの更新
このアプリでは、patch-packageを使用して、以下のライブラリにパッチファイルを適用していました。 パッチ内容の詳細は、こちらを参照してください。
react-native@expo/config-pluginsexpo-splash-screenreact-native-elements
react-nativeに適用していたパッチは、以下のPRで修正されたためパッチフィルを削除しました。
@expo/config-pluginsとexpo-splash-screenは、Expo SDKのアップグレードに伴いバージョンが上がりました。しかし、適用していたパッチファイルはまだ必要な対応だったため、パッチファイルは削除せずに各ライブラリのバージョンに合わせてファイル名をリネームしました。
react-native-elementsに関しては、バージョンが変わらなかったため変更はありません。
jest-expoのアップグレード
jest-expoだけはnpx expo install --fixでバージョンが上がらなかったので、手動で^49.0.0に変更しました。
参考: Expo SDK 48アップグレード #jest-expoのアップグレード
プロジェクトの依存関係に問題がないか確認
npx expo-doctor@latestを実行したところ、以下の実行結果のように@expo/config-pluginsバージョンの問題が検知されました。
✔ Check Expo config for common issues
✖ Check package.json for common issues
✔ Check dependencies for packages that should not be installed directly
✔ Check npm/ yarn versions
✔ Check for common project setup issues
✔ Check Expo config (app.json/ app.config.js) schema
✔ Check for legacy global CLI installed locally
✔ Check that packages match versions required by installed Expo SDK
✔ Check that native modules do not use incompatible support packages
✖ Check that native modules use compatible support package versions for installed Expo SDK
Detailed check results:
Expected package @expo/config-plugins@~7.2.2
Found invalid:
  @expo/config-plugins@5.0.4
  @expo/config-plugins@5.0.4
  (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.
One or more checks failed, indicating possible issues with the project
しかし、前回実施したExpo48への更新から変わらず@react-native-firebaseの更新が必要であるため保留としました。
$ npm ls @expo/config-plugins
santoku-app@1.0.0 /path/to/mobile-app-crib-notes/example-app/SantokuApp
├── @expo/config-plugins@7.2.5
├─┬ @react-native-firebase/app@16.4.6
│ └── @expo/config-plugins@5.0.4
├─┬ @react-native-firebase/crashlytics@16.4.6
│ └── @expo/config-plugins@5.0.4
├─┬ expo-splash-screen@0.20.5
│ └─┬ @expo/prebuild-config@6.2.6
│   └── @expo/config-plugins@7.2.5 deduped
└─┬ expo@49.0.8
  ├─┬ @expo/cli@0.10.11
  │ └── @expo/config-plugins@7.2.5 deduped
  ├── @expo/config-plugins@7.2.5 deduped
  └─┬ @expo/config@8.1.2
    └── @expo/config-plugins@7.2.5 deduped
TSCエラーへの対応
npm run lint:tscでTypeScriptの型をチェックしました。
DimensionValueの厳密化
react-nativeのv0.72.0ではDimensionの型定義が厳密になりました。
-width?: number | string | undefined;
+width?: DimensionValue | undefined;
export type DimensionValue =
  | number
  | 'auto'
  | `${number}%`
  | Animated.AnimatedNode
  | null;
style定義の型推論で${number}%ではなくstringとして認識されてしまう問題が起きたので、as constを追加しました。
       case FaderPosition.TOP:
         return {
           containerStyle: {...staticStyles.containerTop, height: size},
-          imageStyle: {height: size, width: '100%'},
+          imageStyle: {height: size, width: '100%'} as const,
           imageSource: gradientTopImage,
         };
WebView.onScrollの型定義変更
react-native-webviewのFabric対応でonScrollの型定義が変更されました。
-onScroll?: (event: WebViewScrollEvent) => void;
+onScroll?: ComponentProps<typeof NativeWebViewComponent>['onScroll'];
これは整理すると以下になります。
-onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
+onScroll?: (event: NativeSyntheticEvent<ScrollEvent>) => void | Promise<void>;
onScrollの型不一致エラーになったので、以下のように対応しました。
-  const handleScroll = useCallback(
-    (event: WebViewScrollEvent) => {
+  const handleScroll = useCallback<Exclude<WebViewProps['onScroll'], undefined>>(
+    async event => {
       if (isUriChanged && event.nativeEvent.contentOffset.y > 0) {
FunctionComponentの型定義の変更
React.FC(FunctionComponent)の返り値の型定義がReactElementからReactNodeに変更されました。
    interface FunctionComponent<P = {}> {
-        (props: P, context?: any): ReactElement<any, any> | null;
+        (props: P, context?: any): ReactNode;
    type ReactNode =
        | ReactElement
        | string
        | number
        | Iterable<ReactNode>
        // 略
FlatListのrenderItemはReactNodeではなくReactElementを要求しているため、型不一致エラーとなりました。
そこで、renderItemに渡す関数にはReact.FCを使用しないように変更しました。
return <FlatList data={items} renderItem={AppInfoListItem} keyExtractor={keyExtractor} testID={testID} />;
  renderItem: ListRenderItem<ItemT> | null | undefined;
export type ListRenderItem<ItemT> = (
  info: ListRenderItemInfo<ItemT>,
) => React.ReactElement | null;
-export const AppInfoListItem: React.FC<AppInfoListItemProps> = ({item}) => {
+export const AppInfoListItem = ({item}: AppInfoListItemProps) => {
テストコードの修正
react-native-reanimated v3対応
react-native-reanimated v3でディレクトリ構造が変わりました。
それに合わせてimportや型定義ファイルを変更しました。
-react-native-reanimated/lib/reanimated2/
+react-native-reanimated/lib/module/reanimated2/
また、setUpTests()のimportが短縮できるようになっていたので変更しました。
 // https://docs.swmansion.com/react-native-reanimated/docs/guides/testing
-require('react-native-reanimated/lib/reanimated2/jestUtils').setUpTests();
+require('react-native-reanimated').setUpTests();
react-native Dimensionsのmock失敗への対応
DimensionsがES moduleに変更されました。
-module.exports = Dimensions;
+export default Dimensions;
   get Dimensions(): Dimensions {
-    return require('./Libraries/Utilities/Dimensions');
+    return require('./Libraries/Utilities/Dimensions').default;
   },
そのため、mockする際は__esModule: trueを指定するように変更しました。
 jest.doMock('react-native/Libraries/Utilities/Dimensions', () => ({
-  get: jest.fn().mockReturnValue({width: 400, height: 1000}),
-  set: jest.fn(),
-  addEventListener: jest.fn().mockReturnValue({remove: jest.fn()}),
+  __esModule: true,
+  default: {
+    get: jest.fn().mockReturnValue({width: 400, height: 1000}),
+    set: jest.fn(),
+    addEventListener: jest.fn().mockReturnValue({remove: jest.fn()}),
+  },
 }));
jest.advanceTimersByTimeをactでラップ
以下の警告が出るようになっていたため、actでラップするように変更しました。
- Warning: An update to ForwardRef inside a test was not wrapped in act(...).
 - When testing, code that causes React state updates should be wrapped into act(...):
 
   it('SnackbarComponent表示中にpropsでhideを指定した場合、SnackbarComponentが消えることを確認', async () => {
     render(<SnackbarComponent message="テストメッセージ" />);
 
-    jest.advanceTimersByTime(FADE_IN_DURATION);
+    await act(() => {
+      jest.advanceTimersByTime(FADE_IN_DURATION);
+    });
     expect(screen.queryByText('テストメッセージ')).not.toBeNull();
Constants.manifestがdeprecatedになったことへの対応
Expo SDK 49の主な変更 - Expoがサポートするライブラリや機能 - Expo Constantsに記載した通り、Constants.manifestが非推奨になりました。
jest-mockでもmockする際にスプレッド構文を使用してアクセスすると警告ログが出力されるようになっています。
そのため、スプレッド構文ではなくProxyを使用して上書きするようにしています。
 import ExpoConstants from 'expo-constants';
 
-export const Constants: typeof ExpoConstants = {
-  ...ExpoConstants,
+import {wrapProperty} from '../utils/wrapProperty';
+
+export const Constants = wrapProperty(ExpoConstants, {
   expoConfig: {
     name: 'SantokuApp',
     slug: 'santokuApp',
@@ -16,6 +17,6 @@ export const Constants: typeof ExpoConstants = {
       mswEnabled: false,
     },
   },
-};
+});
function hasOwnProperty<T, K extends PropertyKey>(obj: T, propertyKey: K): obj is T & Record<K, unknown> {
  return Object.prototype.hasOwnProperty.call(obj, propertyKey);
}
/**
 * `{...original, [key]: value}`だとgetterにdeprecated警告ログが存在する場合警告ログが出てしまう問題対策
 */
export function wrapProperty<T extends object>(original: T, propertyObj: object): T {
  return new Proxy(original, {
    get(target, name, ...rest) {
      if (hasOwnProperty(propertyObj, name)) {
        return propertyObj[name];
      }
      return Reflect.get(target, name, ...rest);
    },
  });
}
アニメーション中のAnimatedStyleの僅かな変化への対応
アニメーション中のtranslateYの値が以前と僅かに異なっていました。
値が以前と同じことが必須ではなく差分も小さいため、理由の調査はせず、テストコードを更新しました。
@@ -53,7 +53,7 @@ describe('PickerContainer only with required props', () => {
     await act(() => {
       jest.advanceTimersByTime(DEFAULT_SLIDE_IN_DURATION / 2);
     });
-    expect(animatedView).toHaveAnimatedStyle({transform: [{translateY: 75}]});
+    expect(animatedView).toHaveAnimatedStyle({transform: [{translateY: 74.00333333333333}]});
     expect(sut).toMatchSnapshot('Animating (slide in)');
 
     // アニメーションが完了すると`transform`が設定値に到達していること
@@ -82,7 +82,7 @@ describe('PickerContainer only with required props', () => {
     await act(() => {
       jest.advanceTimersByTime(DEFAULT_SLIDE_OUT_DURATION / 2);
     });
-    expect(animatedView).toHaveAnimatedStyle({transform: [{translateY: 75}]});
+    expect(animatedView).toHaveAnimatedStyle({transform: [{translateY: 76.00333333333333}]});
     expect(sut).toMatchSnapshot('Animating (slide out)');
 
     // アニメーションが完了するとコンポーネントが消えること
slideOutDurationテストの時間指定が厳密すぎるとテストが失敗する問題の修正
今まではアニメーション終了1ms前のタイミングでアニメーション終了のcallbackが呼ばれないことを期待したテストをしていましたが、callbackが呼ばれるようになっていました。 1msは小さすぎ、誤差の影響と考えられるため10msに変更しました。
@@ -146,14 +146,14 @@ describe('PickerContainer with all props', () => {
     sut.update(<PickerContainer isVisible={false} afterSlideOut={afterSlideOut} slideOutDuration={100} />);
 
     await startAnimation();
-    // slideOutDurationで指定した時間の1msc前ではafterSlideOutは実行されない
+    // slideOutDurationで指定した時間の10msc前ではafterSlideOutは実行されない
     await act(() => {
-      jest.advanceTimersByTime(99);
+      jest.advanceTimersByTime(90);
     });
     expect(afterSlideOut).not.toHaveBeenCalled();
     // slideOutDurationで指定した時間経過後は、afterSlideOutが実行される
     await act(() => {
-      jest.advanceTimersByTime(1);
+      jest.advanceTimersByTime(10);
     });
     expect(afterSlideOut).toHaveBeenCalled();
   });
keyExtractorの呼び出しが増えたことによって失敗するようになったテストの修正
react-nativeのFlatListのkeyExtractorの呼び出しが増えていました。
2回目にindex=1が呼ばれるのは内部実装に依存していたため、toHaveBeenNthCalledWithの替わりにtoHaveBeenCalledWithを使用するようにしました。
     expect(flatListProps.contentContainerStyle).toEqual({paddingVertical: 120});
     expect(flatListProps.snapToInterval).toBe(60);
-    expect(keyExtractor).toHaveBeenNthCalledWith(
-      1,
+    // `value: '1'` だけ2回呼ばれるのでtoHaveBeenNthCalledWithを使用しない
+    expect(keyExtractor).toHaveBeenCalledWith(
       {value: '1', label: 'test1', key: 'key1', color: 'red', fontFamily: 'Roboto'},
       0,
     );
-    expect(keyExtractor).toHaveBeenNthCalledWith(
-      2,
+    expect(keyExtractor).toHaveBeenCalledWith(
       {value: '2', label: 'test2', color: 'yellow', fontFamily: 'SFProText'},
       1,
     );
警告ログへの対応
typescript-eslintを現在のTypeScriptバージョンに対応したバージョンへ更新
以下のライブラリがTypeScript~5.1.3をサポートしていなかったため、バージョンアップしました。
- @typescript-eslint/eslint-plugin
 - @typescript-eslint/parser
 
WARNING: You are currently running a version of TypeScript which is not officially supported by @typescript-eslint/typescript-estree.
You may find that it works just fine, or you may not.
SUPPORTED TYPESCRIPT VERSIONS: >=3.3.1 <5.1.0
YOUR TYPESCRIPT VERSION: 5.1.6
Please only submit bug reports when using the officially supported version.
npm update @typescript-eslint/eslint-plugin @typescript-eslint/parser
JSX namespaceが非推奨になったことへの対応
以下のcommitでJSXnamespaceがReactnamespaceの下に移動され、JSXは非推奨となりました。
これに対応するためJSX.ElementをReact.JSX.Elementに変更しました。
 type SelectPickerItemsTypes<ItemT> = {
   selectedValue?: React.Key | ItemT;
-  children?: JSX.Element | JSX.Element[];
+  children?: React.JSX.Element | React.JSX.Element[];
   items: Item<ItemT>[];
   itemHeight: number;
initialScrollIndexの範囲外警告への対応
react-native v0.72.0からFlatList(VirtualizedList)のinitialScrollIndexに-1(負の値)を指定すると警告ログがでるようになりました。
initialScrollIndex "-1" is not valid (list has 0 items)
これは、itemsが空かselectedValueが不正でitemsに含まれてないというレアケースでしか問題になりません。
しかし、テストケースにitems=[]が存在するため、以下のように-1のときはnullにする対応をしました。
@@ -120,7 +120,7 @@ <AnimatedFlatList
         renderItem={renderItem}
         decelerationRate={DECELERATION_RATE}
         getItemLayout={getItemLayout}
-        initialScrollIndex={selectedIndex}
+        initialScrollIndex={selectedIndex !== -1 ? selectedIndex : null}
         centerContent
         testID={flatListTestID}
       />
ビルドエラーの修正
app.notifee:coreを見つけられない問題の対応
npm run androidを実行すると以下のエラーが出ました。
Could not find any matches for app.notifee:core:+ as no versions of app.notifee:core are available.
ワークアラウンドとして、expo-build-propertiesプラグインのandroid.extraMavenRepos[]を追加する必要がありました。
@@ -94,6 +94,10 @@
 -keep public class com.horcrux.svg.** {*;}
 `,
             enableProguardInReleaseBuilds: true,
+            extraMavenRepos: [
+              // notifee Expo49対応: https://github.com/invertase/notifee/issues/808
+              '$rootDir/../../../node_modules/@notifee/react-native/android/libs',
+            ],
           },
           ios: {
参考Issueは以下です(2023/09時点で未解決)。
Config PluginswithAndroidRemoveUsesClearTextTrafficForReleaseを削除
今までは暗号化してないhttp通信を禁止するために、android:usesCleartextTrafficを削除するConfig Pluginsを作成し使用していました。
しかし、Expo SDK 49の主な変更 - Expoがサポートするライブラリや機能 - android:usesCleartextTrafficに記載した通り、Expo側で対応されました。 そのため、このConfig Pluginsは不要になったので削除しました。
Proguardルールの更新
各ルールが現在も必要か、コメントのリンク先の更新などが無いかを確認しました。
react-native-svg用ルールの削除
react-native-svg v13.7.0からライブラリ側で自動的にルールを含めるようになりました。
そのため、app.config.jsでexpo-build-propertiesプラグインのandroid.extraProguardRulesでルールを追加する必要はなくなったので削除しました。
 -keep class expo.modules.ExpoModulesPackageList { *; }
-
-# https://github.com/software-mansion/react-native-svg#problems-with-proguard
--keep public class com.horcrux.svg.** {*;}
 `,
             enableProguardInReleaseBuilds: true,
Expoの更新履歴確認と対応
ExpoのCHANGELOGを参照して、Expo SDKとExpoが管理するライブラリの更新内容を確認しました。
ここまでの対応以外に追加で必要なものはありませんでした。
React Nativeの更新履歴確認と対応
React NativeのCHANGELOGを参照して、React Nativeの更新内容を確認しました。
ここまでの対応以外に追加で必要なものはありませんでした。
expo-template-blank-typescriptの更新履歴確認と対応
expo-template-blank-typescriptの更新履歴を確認しました。
npm install expo@^49.0.0、npx expo install --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など)
 
.gitignoreの更新
expo-template-bare-minimumの以下のコミットで、.gitignoreが更新されていました。
大きな変更は、重複の削除でした。
つまり、Android, Xcode関連ファイルは android/, ios/ の .gitignore に記載されているため削除されました。
このアプリにもこの変更を取り込みました。
-# Xcode
-#
-build/
-*.pbxuser
-!default.pbxuser
-*.mode1v3
-!default.mode1v3
-*.mode2v3
-!default.mode2v3
-*.perspectivev3
-!default.perspectivev3
-xcuserdata
-*.xccheckout
-*.moved-aside
-DerivedData
-*.hmap
-*.ipa
-*.xcuserstate
-project.xcworkspace
-
-# Android/IntelliJ
-#
-build/
-.idea
-.gradle
-local.properties
-*.iml
-*.hprof
このアプリでは、prebuild時のファイルコピー処理に不具合があり、prebuild/ディレクトリの一部ファイルがignoreされなくなりました。
不具合はandroid/,ios/ディレクトリの.gitignoreがprebuild/ディレクトリにコピーされないもので、以下のPull Requestで対処しました。
手動管理しているライセンスの更新
このアプリでは、使用しているライブラリのライセンス一覧を出力するスクリプトを用意しています。詳細は、こちらを参照してください。
managed-license.jsの更新
ライセンス情報が不足しており補完したい、あるいは、開発時のみ使用するため除外したいライブラリとバージョンをmanaged-license.jsで管理しています。
ライセンス情報が不足しているライブラリなどは、以下のコマンドを実行することで確認できます。
node .script/check-licenses.js
実行した結果、いくつかのライブラリのライセンス情報を更新する必要があったので以下を実施しました。
- 使用ライブラリの名前更新
 - 使用ライブラリのバージョン更新
 - 使用ライブラリのライセンスファイルURL更新
 - 新規ライブラリ情報の追加
 - 使用しなくなったライブラリ情報の削除
 
新規ライセンスへの対応
このアプリでは、アプリに使用しても問題ないライセンスを管理してチェックできるようにしています。
今回のExpo SDKアップグレードでは、Mozilla Public License 2.0(MPL-2.0)のライブラリが増えました。
MPL-2.0は準コピーレフト型ライセンスであり、該当ライブラリ以外のソースコードの公開義務が発生しないため、使用可としました。
そのライセンス名(MPL-2.0)をcheck-licenses.jsのlicenseWhitelistに追加しています。