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.

✅ 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
@nx-cloud
Copy link

nx-cloud bot commented Aug 25, 2025

View your CI Pipeline Execution ↗ for commit cbb075d

Command Status Duration Result
nx test apps-automated -c=ios ✅ Succeeded 2m 20s View ↗
nx run-many --target=test --configuration=ci --... ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2025-11-04 01:03:43 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.

Base automatically changed from refactor/circular-deps to main September 19, 2025 00:03
@NathanWalker NathanWalker marked this pull request as ready for review October 2, 2025 19:10
@NathanWalker NathanWalker requested a review from Copilot October 29, 2025 19:43
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds iOS multi-window and scene lifecycle support to NativeScript, enabling apps to manage multiple windows on iPadOS 13+ and handle scene-based lifecycle events. This is a significant enhancement for iPad apps that want to support modern iPadOS multitasking features.

  • Implements UIWindowScene delegate and scene lifecycle management
  • Adds API for opening and managing multiple windows with scene activation
  • Provides demo application showing multi-scene functionality

Reviewed Changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/assets/App_Resources/iOS/Info.plist Added UIApplicationSceneManifest configuration to enable scene support and fixed indentation
packages/webpack5/src/configuration/base.ts Fixed code formatting (quote style and spacing)
packages/core/vitest.setup.ts Added NSBundle and UIWindowSceneDelegate mocks for testing scene functionality
packages/core/ui/styling/css-stroke.ts Added Length import (appears unused)
packages/core/package.json Bumped version to 9.0.0-alpha.13
packages/core/application/application.ios.ts Implemented scene delegate, scene lifecycle management, and multi-window APIs
packages/core/application/application.d.ts Added TypeScript definitions for scene management APIs
packages/core/application/application-interfaces.ts Added SceneEventData interface for scene lifecycle events
packages/core/application/application-common.ts Added SceneEvents constants for scene lifecycle event names
apps/toolbox/src/pages/multiple-scenes.xml Added demo UI for testing multi-scene functionality
apps/toolbox/src/pages/multiple-scenes.ts Implemented demo logic for creating and managing scenes
apps/toolbox/src/main.ts Added SceneEvents import (appears unused)
apps/toolbox/src/main-page.xml Added navigation button to multiple-scenes demo
apps/toolbox/src/main-page.ts Added commented-out scene event setup code for testing
apps/toolbox/src/app.css Added CSS styles for multiple scenes demo UI

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@NathanWalker NathanWalker requested review from triniwiz and removed request for edusperoni November 3, 2025 20:02
@NathanWalker NathanWalker merged commit 96733c2 into main Nov 4, 2025
6 of 7 checks passed
@NathanWalker NathanWalker deleted the feat/multi-window-support branch November 4, 2025 01:01
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.

3 participants