Skip to content

Conversation

NathanWalker
Copy link
Contributor

@NathanWalker NathanWalker commented Aug 25, 2025

What is the current behavior?

For several years, since iOS 13, a developer could manually wire up a SceneDelegate, modify plist properties and achieve multi scene support.

What is the new behavior?

With iOS 26 making multi-window support even nicer, this finally brings a powerful yet simple API to develop multi-window apps on iPadOS and beyond.

  • Add automated tests
  • Switch some logging to use Trace
  • Outline new docs page for Multi-window support

✅ Fully backwards compatible so maintains current behavior by default.
Scene lifecycle will only engage once UIApplicationSceneManifest is added to Info.plist, for example:

<key>UIApplicationSceneManifest</key>
<dict>
	<key>UIApplicationPreferredDefaultSceneSessionRole</key>
	<string>UIWindowSceneSessionRoleApplication</string>
	<key>UIApplicationSupportsMultipleScenes</key>
	<true/>
	<key>UISceneConfigurations</key>
	<dict>
		<key>UIWindowSceneSessionRoleApplication</key>
		<array>
			<dict>
				<key>UISceneConfigurationName</key>
				<string>Default Configuration</string>
				<key>UISceneDelegateClassName</key>
				<string>SceneDelegate</string>
			</dict>
		</array>
	</dict>
</dict>

When a configuration like this is detected, the app will auto switch to scene lifecycle.

Multi-Window Scene Support for NativeScript iOS

Overview

This implementation provides full support for iOS 13+ scene-based applications while maintaining backwards compatibility with traditional single-window apps.

🔧 Core Changes

1. New SceneDelegate Class

A new @NativeClass SceneDelegate has been created that implements UIWindowSceneDelegate.

It is only activated when UIApplicationSceneManifest configuration is present in Info.plist.

  • Purpose: Handles scene lifecycle methods for multi-window support
  • Key Methods:
    • sceneWillConnectToSessionOptions - Creates UIWindow and sets up scene
    • sceneDidBecomeActive - Handles scene activation
    • sceneWillResignActive - Handles scene deactivation
    • sceneWillEnterForeground - Scene foreground transition
    • sceneDidEnterBackground - Scene background transition
    • sceneDidDisconnect - Scene cleanup

2. Enhanced iOSApplication

The iOSApplication class has been significantly enhanced with scene management capabilities:

New Methods

  • supportsScenes(): Checks if device supports scenes
  • supportsMultipleScenes(): Checks if the application supports multiple scenes (only available on physical iPadOS)
  • getAllWindows(): Returns all app windows
  • getAllScenes(): Returns all scenes
  • getWindowScenes(): Returns window scenes specifically
  • getPrimaryWindow(): Returns the primary window
  • getPrimaryScene(): Returns primary scene
  • isUsingSceneLifecycle(): Checks if using scene-based lifecycle
  • setWindowRootView(window: UIWindow, view: View): Sets the root view for a specific window.

3. Scene Event Support

New Interface: SceneEventData

/**
 * iOS Event data containing information for scene lifecycle events (iOS 13+).
 */
export interface SceneEventData extends ApplicationEventData {
	/**
	 * The UIWindowScene instance associated with this event.
	 */
	scene?: UIWindowScene;

	/**
	 * The UIWindow associated with this scene (if applicable).
	 */
	window?: UIWindow;

	/**
	 * Scene connection options (for sceneWillConnect event).
	 */
	connectionOptions?: UISceneConnectionOptions;

	/**
	 * Additional user info from the notification.
	 */
	userInfo?: NSDictionary<any, any>;
}

Scene Event Constants

export const SceneEvents = {
  sceneWillConnect: 'sceneWillConnect',
  sceneDidActivate: 'sceneDidActivate',
  sceneWillResignActive: 'sceneWillResignActive',
  sceneWillEnterForeground: 'sceneWillEnterForeground',
  sceneDidEnterBackground: 'sceneDidEnterBackground',
  sceneDidDisconnect: 'sceneDidDisconnect',
  sceneContentSetup: 'sceneContentSetup',
};

4. Scene Lifecycle Notification Observers

The application automatically registers for scene lifecycle notifications on iOS 13+ if the app has detected a scene manifest:

  • UISceneWillConnectNotification
  • UISceneDidActivateNotification
  • UISceneWillEnterForegroundNotification
  • UISceneDidEnterBackgroundNotification
  • UISceneDidDisconnectNotification

🚀 Usage Examples

Basic Scene Event Listening

import { Application, SceneEvents } from '@nativescript/core';

// Listen to scene events
Application.on(SceneEvents.sceneWillConnect, (args) => {
  console.log('New scene connecting:', args.scene);
  console.log('Window:', args.window);
  console.log('Connection options:', args.connectionOptions);
});

Application.on(SceneEvents.sceneDidActivate, (args) => {
  console.log('Scene became active:', args.scene);
});

Application.on(SceneEvents.sceneWillResignActive, (args) => {
  console.log('Scene will resign active:', args.scene);
});

Application.on(SceneEvents.sceneWillEnterForeground, (args) => {
  console.log('Scene entered foreground:', args.scene);
});

Application.on(SceneEvents.sceneDidEnterBackground, (args) => {
  console.log('Scene entered background:', args.scene);
});

Application.on(SceneEvents.sceneDidDisconnect, (args) => {
  console.log('Scene disconnected:', args.scene);
});

Application.on(SceneEvents.sceneContentSetup, (args: SceneEventData) => {
  // Developers can create NativeScript View content for the scene here.
  console.log('Setup scene content here!', args.scene);
  this.setupSceneContent(args);
});

Multi-Window Management

// Check if multi-window support is available
if (Application.ios.supportsScenes()) {
  console.log('Multi-window support available');
  
  // Get all windows and scenes
  const windows = Application.ios.getAllWindows();
  const scenes = Application.ios.getWindowScenes();
  const primaryWindow = Application.ios.getPrimaryWindow();
  
  console.log(`App has ${windows.length} windows`);
  console.log(`App has ${scenes.length} scenes`);
  console.log('Primary window:', primaryWindow);
  
  // Check if using scene lifecycle
  if (Application.ios.isUsingSceneLifecycle()) {
    console.log('App is using scene-based lifecycle');
  }
} else {
  console.log('Traditional single-window app');
}

Scene-Specific UI Management

function setupSceneContent(args: SceneEventData) {

  // You can 'id' scenes when using openWindow({ id: 'myScene' })
  // to conditionally show different views in different windows.
  let nsViewId: string;
  if (args.connectionOptions?.userActivities?.count > 0) {
    const activity = args.connectionOptions.userActivities.allObjects.objectAtIndex(0) as NSUserActivity;
    nsViewId = Utils.dataDeserialize(activity.userInfo).id;
  }

  /**
   * You can implement any NativeScript view using any flavor for new window scenes.
   */
  let page: Page;
  switch (nsViewId) {
    case 'newSceneBasic':
      page = this._createPageForScene(args.scene, args.window);
      break;
    case 'newSceneAlt':
      page = this._createAltPageForScene(args.scene, args.window);
      break;
    // Note: can implement any number of other scene views
  }

  Application.ios.setWindowRootView(args.window, page);
}

📋 Key Features

1. Multi-Window Management

  • Window Tracking: Automatically tracks all windows and their associated scenes
  • Scene Mapping: Maintains mapping between scenes and windows
  • Primary Scene: Designates and manages a primary scene for backwards compatibility

2. Scene Lifecycle Events

  • Full Coverage: Supports all scene lifecycle events with proper TypeScript typing
  • Event Data: Rich event data including scene, window, and connection options
  • Notification Integration: Uses iOS notification system for reliable event delivery

3. Backwards Compatibility

  • Legacy Support: Existing single-window apps continue to work without changes
  • Primary Scene Fallback: Traditional app events are forwarded from the primary scene
  • Graceful Degradation: Falls back gracefully on iOS versions prior to 13

4. Type Safety

  • TypeScript Interfaces: Full TypeScript support with proper interfaces
  • Event Constants: Strongly-typed event name constants
  • Method Signatures: Proper typing for all new methods and properties

5. Custom Scene Delegates

  • Extensibility: Support for custom scene delegate implementations
  • Override Capability: Ability to override default scene behavior
  • Flexible Integration: Easy integration with existing app architecture

🔍 Technical Implementation Details

Scene Detection and Setup

  • iOS Version Check: Automatically detects iOS 13+ scene support using SDK_VERSION >= 13
  • Multi-Scene Check: Verifies UIApplication.sharedApplication.supportsMultipleScenes
  • Notification Observers: Sets up scene lifecycle notification observers in constructor

Window Management

  • Scene-Window Mapping: Uses Map<UIScene, UIWindow> for efficient scene-to-window mapping
  • Primary Scene Logic: Maintains backwards compatibility by designating first scene as primary
  • Window Creation: Automatically creates UIWindow instances for each UIWindowScene

Event Forwarding

  • Traditional Events: App lifecycle events (didBecomeActive, didEnterBackground) are forwarded from primary scene
  • Scene Events: New scene-specific events are fired for all scenes
  • Dual Support: Both traditional and scene events are available simultaneously

Memory Management

  • Automatic Cleanup: Proper cleanup when scenes disconnect
  • Weak References: Uses appropriate memory management patterns
  • Observer Removal: Cleans up notification observers on app exit

Error Handling

  • Graceful Fallbacks: Handles cases where scene support is not available
  • Type Checking: Validates scene types before processing
  • Console Warnings: Provides helpful warnings for unsupported configurations

🔄 Migration Path?

For Existing Apps

No changes required - existing single-window apps will continue to work exactly as before.

For New Multi-Window Apps

  1. Enable multi-window support in your app's Info.plist
  2. Listen to scene events using the new SceneEvents constants
  3. Use the new scene management methods to handle multiple windows
  4. Optionally implement custom scene delegates for advanced scenarios

@NathanWalker NathanWalker changed the base branch from main to refactor/circular-deps August 25, 2025 20:48
Copy link

nx-cloud bot commented Aug 25, 2025

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable them in workspace settings.


View your CI Pipeline Execution ↗ for commit 0c67208

Command Status Duration Result
nx test apps-automated -c=android ❌ Failed 23m 8s View ↗
nx run-many --target=test --configuration=ci --... ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2025-08-25 21:34:39 UTC

@NathanWalker NathanWalker added this to the 9.0 milestone Aug 25, 2025
@NathanWalker NathanWalker added the docs needed Additional documentation on this issue/PR is needed label Aug 25, 2025
* Additional user info from the notification.
*/
userInfo?: NSDictionary<any, any>;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can likely embed ios|android keys within this interface for specific platform details and provide userInfo as a data property for both already deserialized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs needed Additional documentation on this issue/PR is needed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant