const { withAppDelegate, withInfoPlist } = require('expo/config-plugins'); /** * iOS 27 aborts at launch unless the app adopts the UIScene life cycle. Expo * ships `ExpoAppSceneDelegate` for this, but the SDK 57 template still starts * React Native from the app delegate, so register the scene delegate and let it * own the window. */ function withSceneManifest(config) { return withInfoPlist(config, (cfg) => { cfg.modResults.UIApplicationSceneManifest = { UIApplicationSupportsMultipleScenes: false, UISceneConfigurations: { UIWindowSceneSessionRoleApplication: [ { UISceneConfigurationName: 'Default Configuration', UISceneDelegateClassName: 'EXExpoAppSceneDelegate', }, ], }, }; return cfg; }); } function withSceneAppDelegate(config) { return withAppDelegate(config, (cfg) => { if (cfg.modResults.language !== 'swift') { throw new Error('withSceneLifecycle only supports a Swift AppDelegate'); } let src = cfg.modResults.contents; if (!src.includes('ExpoReactNativeFactoryProvider')) { src = src.replace( /class AppDelegate: ExpoAppDelegate \{/, 'class AppDelegate: ExpoAppDelegate, ExpoReactNativeFactoryProvider {', ); } src = src.replace( /\n#if os\(iOS\) \|\| os\(tvOS\)\n\s*window = UIWindow\(frame: UIScreen\.main\.bounds\)\n\s*factory\.startReactNative\([\s\S]*?\)\n#endif\n/, '', ); if (!src.includes('ExpoReactNativeFactoryProvider') || src.includes('UIWindow(frame: UIScreen.main.bounds)')) { throw new Error('withSceneLifecycle could not patch AppDelegate.swift; the Expo template has changed'); } cfg.modResults.contents = src; return cfg; }); } module.exports = function withSceneLifecycle(config) { return withSceneAppDelegate(withSceneManifest(config)); };