From f52911dedf1450e3c879d494646b2053d98c2391 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Mon, 2 Nov 2026 08:53:38 +1100 Subject: [PATCH 1/3] iOS / tvOS: adopt the UIScene lifecycle (apps crash at launch on iOS / tvOS 27) Fixes #8639. ofxiOSSceneDelegate (ofAppiOSWindow.mm) attaches the window the app delegate already creates in applicationDidFinishLaunching: to the connecting UIWindowScene, and forwards the scene's active / resign / background / foreground events and opened URLs to the app delegate's existing methods. The iOS and tvOS template Info.plists name it in UIApplicationSceneManifest. App delegates, including ones started with startAppWithDelegate, are unchanged. --- addons/ofxiOS/src/app/ofAppiOSWindow.mm | 70 +++++++++++++++++++++++ scripts/templates/ios/ofxiOS-Info.plist | 17 ++++++ scripts/templates/tvOS/ofxtvOS-Info.plist | 17 ++++++ 3 files changed, 104 insertions(+) diff --git a/addons/ofxiOS/src/app/ofAppiOSWindow.mm b/addons/ofxiOS/src/app/ofAppiOSWindow.mm index 53a3856e6d4..3f739807975 100644 --- a/addons/ofxiOS/src/app/ofAppiOSWindow.mm +++ b/addons/ofxiOS/src/app/ofAppiOSWindow.mm @@ -46,6 +46,76 @@ #include "ofxiOSGLKView.h" #include "ofxiOSEAGLView.h" +//----------------------------------------------------------------------------------- scene delegate. +// iOS / tvOS 27 require the UIScene lifecycle. The app's Info.plist names this class in +// UIApplicationSceneManifest; it shows the window the app delegate created in +// applicationDidFinishLaunching: in the scene, and passes the scene's lifecycle events +// and URLs to the app delegate, which UIKit no longer sends them to. +@interface ofxiOSSceneDelegate : UIResponder +@property (strong, nonatomic) UIWindow * window; +@end + +@implementation ofxiOSSceneDelegate + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { + if (![scene isKindOfClass:[UIWindowScene class]]) return; + id appDelegate = [UIApplication sharedApplication].delegate; + if ([appDelegate respondsToSelector:@selector(window)]) { + self.window = appDelegate.window; + } + if (self.window) { + self.window.windowScene = (UIWindowScene *)scene; + [self.window makeKeyAndVisible]; + } + [self scene:scene openURLContexts:connectionOptions.URLContexts]; +} + +- (void)sceneDidBecomeActive:(UIScene *)scene { + [self forward:@selector(applicationDidBecomeActive:)]; +} + +- (void)sceneWillResignActive:(UIScene *)scene { + [self forward:@selector(applicationWillResignActive:)]; +} + +- (void)sceneWillEnterForeground:(UIScene *)scene { + [self forward:@selector(applicationWillEnterForeground:)]; +} + +- (void)sceneDidEnterBackground:(UIScene *)scene { + [self forward:@selector(applicationDidEnterBackground:)]; +} + +- (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts { + id appDelegate = [UIApplication sharedApplication].delegate; + for (UIOpenURLContext * context in URLContexts) { + if ([appDelegate respondsToSelector:@selector(application:openURL:options:)]) { + [appDelegate application:[UIApplication sharedApplication] openURL:context.URL options:@{}]; + } +#if !TARGET_OS_TV + else if ([appDelegate respondsToSelector:@selector(application:handleOpenURL:)]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [appDelegate application:[UIApplication sharedApplication] handleOpenURL:context.URL]; +#pragma clang diagnostic pop + } +#endif + } +} + +// calls the app delegate's application...: method for a scene event +- (void)forward:(SEL)selector { + id appDelegate = [UIApplication sharedApplication].delegate; + if ([appDelegate respondsToSelector:selector]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [appDelegate performSelector:selector withObject:[UIApplication sharedApplication]]; +#pragma clang diagnostic pop + } +} + +@end + //----------------------------------------------------------------------------------- instance. static ofAppiOSWindow * _instance = NULL; ofAppiOSWindow * ofAppiOSWindow::getInstance() { diff --git a/scripts/templates/ios/ofxiOS-Info.plist b/scripts/templates/ios/ofxiOS-Info.plist index 0c6c10119a2..5575d673b22 100644 --- a/scripts/templates/ios/ofxiOS-Info.plist +++ b/scripts/templates/ios/ofxiOS-Info.plist @@ -45,5 +45,22 @@ UIViewControllerBasedStatusBarAppearance + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default + UISceneDelegateClassName + ofxiOSSceneDelegate + + + + diff --git a/scripts/templates/tvOS/ofxtvOS-Info.plist b/scripts/templates/tvOS/ofxtvOS-Info.plist index 4aa5d31f9ca..69b3a79e592 100644 --- a/scripts/templates/tvOS/ofxtvOS-Info.plist +++ b/scripts/templates/tvOS/ofxtvOS-Info.plist @@ -26,5 +26,22 @@ arm64 + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default + UISceneDelegateClassName + ofxiOSSceneDelegate + + + + From a2c5055c421861cd4ee4b04a8dba8a94c31d1314 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Mon, 2 Nov 2026 20:26:14 +1100 Subject: [PATCH 2/3] Refactor URL handling in ofAppiOSWindow.mm Removed deprecated URL handling for tvOS. --- addons/ofxiOS/src/app/ofAppiOSWindow.mm | 2 -- 1 file changed, 2 deletions(-) diff --git a/addons/ofxiOS/src/app/ofAppiOSWindow.mm b/addons/ofxiOS/src/app/ofAppiOSWindow.mm index 3f739807975..205a55130cf 100644 --- a/addons/ofxiOS/src/app/ofAppiOSWindow.mm +++ b/addons/ofxiOS/src/app/ofAppiOSWindow.mm @@ -92,14 +92,12 @@ - (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLC if ([appDelegate respondsToSelector:@selector(application:openURL:options:)]) { [appDelegate application:[UIApplication sharedApplication] openURL:context.URL options:@{}]; } -#if !TARGET_OS_TV else if ([appDelegate respondsToSelector:@selector(application:handleOpenURL:)]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [appDelegate application:[UIApplication sharedApplication] handleOpenURL:context.URL]; #pragma clang diagnostic pop } -#endif } } From 735b0a5286ca11bb4a159946b34d350e169ec3c9 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Mon, 2 Nov 2026 20:27:14 +1100 Subject: [PATCH 3/3] Update ofAppiOSWindow.mm --- addons/ofxiOS/src/app/ofAppiOSWindow.mm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/addons/ofxiOS/src/app/ofAppiOSWindow.mm b/addons/ofxiOS/src/app/ofAppiOSWindow.mm index 205a55130cf..698e7960863 100644 --- a/addons/ofxiOS/src/app/ofAppiOSWindow.mm +++ b/addons/ofxiOS/src/app/ofAppiOSWindow.mm @@ -46,11 +46,6 @@ #include "ofxiOSGLKView.h" #include "ofxiOSEAGLView.h" -//----------------------------------------------------------------------------------- scene delegate. -// iOS / tvOS 27 require the UIScene lifecycle. The app's Info.plist names this class in -// UIApplicationSceneManifest; it shows the window the app delegate created in -// applicationDidFinishLaunching: in the scene, and passes the scene's lifecycle events -// and URLs to the app delegate, which UIKit no longer sends them to. @interface ofxiOSSceneDelegate : UIResponder @property (strong, nonatomic) UIWindow * window; @end