I have an old (Objective-C) iOS app that I expanded to Mac Catalyst, and now I want to add multi-window support to the Mac version, without affecting the iOS version. In accordance with several tutorials (example), I've set up a SceneDelegate
like this...
@implementation SceneDelegate
#if TARGET_OS_MACCATALYST
@synthesize window;
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
if ([scene isKindOfClass:[UIWindowScene class]]) {
self.window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];
self.window.rootViewController = [[MainView alloc] init];
[self.window makeKeyAndVisible];
}
}
@end
#endif
...added this to AppDelegate...
#if TARGET_OS_MACCATALYST
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(nonnull UISceneSession *)connectingSceneSession options:(nonnull UISceneConnectionOptions *)options {
return [[UISceneConfiguration alloc] initWithName:@"Main" sessionRole:connectingSceneSession.role];
}
#endif
...and added this to Info.plist:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
</dict>
<key>UIApplicationSceneManifest-macos</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Main</string>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
I'm still doing a lot of setup in the AppDelegate
's application:didFinishLaunchingWithOptions:
method, but that method was never called, nor was application:configurationForConnectingSceneSession:options:
, and there were no errors in the console. So I moved the definition of the scene from the Info.plist file to the application:configurationForConnectingSceneSession:options:
method, like this...
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(nonnull UISceneSession *)connectingSceneSession options:(nonnull UISceneConnectionOptions *)options {
UISceneConfiguration *config = [[UISceneConfiguration alloc] initWithName:@"Main" sessionRole:UIWindowSceneSessionRoleApplication];
config.sceneClass = [UIWindowScene class];
config.delegateClass = [SceneDelegate class];
return config;
}
...leaving only this in Info.plist:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
</dict>
<key>UIApplicationSceneManifest-macos</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
</dict>
Now it works as expected.
Is defining the scene configurations in Info.plist supposed to completely bypass the AppDelegate
? Based on the documentation, it seems like maybe that's supposed to bypass application:configurationForConnectingSceneSession:options:
, but I haven't seen any indication that it should also bypass application:didFinishLaunchingWithOptions:
. Some tutorials explicitly say that method should still run.
I tried removing the application:configurationForConnectingSceneSession:options:
method in case defining the configurations in two places was confusing the SDK, but application:didFinishLaunchingWithOptions:
was still not called, as long as UISceneConfigurations
was in Info.plist.
I can tell whether application:didFinishLaunchingWithOptions:
is called or not by adding a breakpoint to its first line. The breakpoint gets hit unless I add the UISceneConfigurations
dictionary to the Info.plist, but making only that change causes the breakpoint to not get hit.