Skip to content

Commit e078706

Browse files
authored
feat: v9 multi-window support (#184)
1 parent 91c4f0a commit e078706

File tree

1 file changed

+244
-0
lines changed

1 file changed

+244
-0
lines changed

content/guide/multi-window.md

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
---
2+
title: Multiple Windows
3+
description: Develop with multiple windows on supported devices.
4+
contributors:
5+
- NathanWalker
6+
---
7+
8+
NativeScript 9 adds first-class support for iOS multi-window (multi-scene) applications by adopting the UIScene lifecycle when enabled. This guide explains how to enable scenes, how NativeScript integrates with them, and how to work with scene-specific APIs and events.
9+
10+
:::tip Why this matters
11+
Apple is moving all iOS apps to the UIScene lifecycle. Enabling scenes now makes your app future‑proof and unlocks multiple windows on iPadOS and visionOS.
12+
:::
13+
14+
## Supported platforms
15+
16+
- iPad running iPadOS 13 or later (multi-window capable)
17+
- visionOS (Vision Pro)
18+
- iPhone: runs with UIScene lifecycle; multiple windows are not exposed to users, but adopting UIScene is recommended
19+
20+
## Prerequisites
21+
22+
- NativeScript 9+
23+
- iOS 13+ runtime
24+
- Xcode/iOS tooling capable of building with UIScene (Xcode 11+)
25+
26+
## Enable scene lifecycle (Info.plist)
27+
28+
NativeScript will automatically switch to UIScene lifecycle when a scene manifest is present in your iOS app `Info.plist`. Add the following keys:
29+
30+
```xml
31+
<key>UIApplicationSceneManifest</key>
32+
<dict>
33+
<key>UIApplicationPreferredDefaultSceneSessionRole</key>
34+
<string>UIWindowSceneSessionRoleApplication</string>
35+
<key>UIApplicationSupportsMultipleScenes</key>
36+
<true/>
37+
<key>UISceneConfigurations</key>
38+
<dict>
39+
<key>UIWindowSceneSessionRoleApplication</key>
40+
<array>
41+
<dict>
42+
<key>UISceneConfigurationName</key>
43+
<string>Default Configuration</string>
44+
<key>UISceneDelegateClassName</key>
45+
<string>SceneDelegate</string>
46+
</dict>
47+
</array>
48+
</dict>
49+
</dict>
50+
```
51+
52+
When this configuration is detected, NativeScript adopts UIScene; on devices that don’t support scenes, your app continues to behave as a single-window app.
53+
54+
:::note iPhone and UIScene
55+
Even on iPhone, adding the manifest switches your app to UIScene lifecycle. Xcode may show warnings like “UIScene lifecycle will soon be required” — using the manifest addresses this.
56+
:::
57+
58+
## How it works in NativeScript
59+
60+
When the scene manifest is present:
61+
62+
- A `SceneDelegate` (exposed to iOS as `SceneDelegate`) implements `UIWindowSceneDelegate` to integrate scenes with NativeScript’s application runtime.
63+
- A UIWindow is created per `UIWindowScene` and mapped internally, preserving compatibility with traditional app lifecycle APIs.
64+
- NativeScript fires scene-specific events and forwards core application lifecycle events from a primary scene to maintain compatibility with existing code.
65+
66+
### Scene lifecycle events
67+
68+
Use the `SceneEvents` constants to subscribe to scene lifecycle changes:
69+
70+
```ts
71+
export const SceneEvents = {
72+
sceneWillConnect: 'sceneWillConnect',
73+
sceneDidActivate: 'sceneDidActivate',
74+
sceneWillResignActive: 'sceneWillResignActive',
75+
sceneWillEnterForeground: 'sceneWillEnterForeground',
76+
sceneDidEnterBackground: 'sceneDidEnterBackground',
77+
sceneDidDisconnect: 'sceneDidDisconnect',
78+
sceneContentSetup: 'sceneContentSetup',
79+
};
80+
```
81+
82+
Event payloads include scene and window references:
83+
84+
```ts
85+
/** iOS event data for UIScene lifecycle (iOS 13+). */
86+
export interface SceneEventData extends ApplicationEventData {
87+
/** The UIWindowScene instance associated with this event. */
88+
scene?: UIWindowScene;
89+
/** The UIWindow for this scene (if applicable). */
90+
window?: UIWindow;
91+
/** Scene connection options (for sceneWillConnect). */
92+
connectionOptions?: UISceneConnectionOptions;
93+
/** Additional user info from the notification. */
94+
userInfo?: NSDictionary<any, any>;
95+
}
96+
```
97+
98+
### iOSApplication scene APIs
99+
100+
When UIScene is active, `Application.ios` exposes helpers for inspecting and controlling scenes and windows:
101+
102+
- `supportsScenes(): boolean` — iOS supports UIScene (iOS 13+)
103+
- `supportsMultipleScenes(): boolean` — app can present multiple scenes/windows (iPadOS; typically false on iPhone and some simulators)
104+
- `getAllWindows(): UIWindow[]` — all app windows across scenes
105+
- `getAllScenes(): UIScene[]` — all attached scenes
106+
- `getWindowScenes(): UIWindowScene[]` — filtered to window scenes
107+
- `getPrimaryWindow(): UIWindow | undefined` — the primary window (for compatibility)
108+
- `getPrimaryScene(): UIWindowScene | undefined` — the primary scene
109+
- `isUsingSceneLifecycle(): boolean` — whether UIScene lifecycle is active
110+
- `setWindowRootView(window: UIWindow, view: View): void` — set NativeScript root view for a given scene’s window
111+
112+
## Usage
113+
114+
### Listen to scene events
115+
116+
```ts
117+
import { Application, SceneEvents } from '@nativescript/core';
118+
119+
Application.on(SceneEvents.sceneWillConnect, (args) => {
120+
console.log('Scene connecting:', args.scene);
121+
console.log('Window:', args.window);
122+
console.log('Connection options:', args.connectionOptions);
123+
});
124+
125+
Application.on(SceneEvents.sceneDidActivate, (args) => {
126+
console.log('Scene active:', args.scene);
127+
});
128+
129+
Application.on(SceneEvents.sceneWillResignActive, (args) => {
130+
console.log('Scene will resign active:', args.scene);
131+
});
132+
133+
Application.on(SceneEvents.sceneWillEnterForeground, (args) => {
134+
console.log('Scene will enter foreground:', args.scene);
135+
});
136+
137+
Application.on(SceneEvents.sceneDidEnterBackground, (args) => {
138+
console.log('Scene entered background:', args.scene);
139+
});
140+
141+
Application.on(SceneEvents.sceneDidDisconnect, (args) => {
142+
console.log('Scene disconnected:', args.scene);
143+
});
144+
145+
Application.on(SceneEvents.sceneContentSetup, (args) => {
146+
// Create and attach NativeScript View content for the new scene here
147+
// See "Provide scene-specific UI" section below
148+
setupSceneContent(args);
149+
});
150+
```
151+
152+
### Inspect and manage windows
153+
154+
```ts
155+
import { Application } from '@nativescript/core';
156+
157+
if (Application.ios.supportsScenes()) {
158+
const windows = Application.ios.getAllWindows();
159+
const scenes = Application.ios.getWindowScenes();
160+
const primaryWindow = Application.ios.getPrimaryWindow();
161+
162+
console.log(`App has ${windows.length} windows`);
163+
console.log(`App has ${scenes.length} scenes`);
164+
console.log('Primary window:', primaryWindow);
165+
166+
if (Application.ios.isUsingSceneLifecycle()) {
167+
console.log('Using UIScene lifecycle');
168+
}
169+
} else {
170+
console.log('Single-window app lifecycle in effect');
171+
}
172+
```
173+
174+
### Provide scene-specific UI
175+
176+
```ts
177+
import { Application, Page, Utils } from '@nativescript/core';
178+
179+
function createPageForScene(scene: UIWindowScene, window: UIWindow): Page {
180+
// Construct any NativeScript view hierarchy here
181+
const page = new Page();
182+
// ... add content
183+
return page;
184+
}
185+
186+
export function setupSceneContent(args: SceneEventData) {
187+
// Optionally distinguish scenes by an id when opening a new window
188+
// (e.g., via NSUserActivity userInfo)
189+
let nsViewId: string | undefined;
190+
if (args.connectionOptions?.userActivities?.count > 0) {
191+
const activity = args.connectionOptions.userActivities.allObjects.objectAtIndex(0) as NSUserActivity;
192+
nsViewId = Utils.dataDeserialize(activity.userInfo).id;
193+
}
194+
195+
let page: Page;
196+
switch (nsViewId) {
197+
case 'newSceneBasic':
198+
page = createPageForScene(args.scene, args.window);
199+
break;
200+
case 'newSceneAlt':
201+
page = createPageForScene(args.scene, args.window); // replace with alt page
202+
break;
203+
default:
204+
page = createPageForScene(args.scene, args.window);
205+
}
206+
207+
Application.ios.setWindowRootView(args.window, page);
208+
}
209+
```
210+
211+
## Custom SceneDelegate (advanced)
212+
213+
NativeScript ships a default `SceneDelegate` that integrates UIScene with the runtime and event system. If your application needs custom scene delegate behavior, you can provide your own implementation named `SceneDelegate` in your app and wire additional logic. Ensure that you continue to create a `UIWindow` per `UIWindowScene` and set the NativeScript root view to keep app behavior consistent. Most apps should prefer the default delegate.
214+
215+
## Compatibility and behavior
216+
217+
- Backwards compatibility: on devices or builds without a scene manifest, the traditional single-window lifecycle is used and existing apps continue to work unchanged.
218+
- Primary scene: for compatibility, NativeScript forwards core app lifecycle events (e.g., didBecomeActive) from the primary scene.
219+
- Multiple scenes: `supportsMultipleScenes()` is typically only true on physical iPadOS devices; it may return false on iPhone and some simulators.
220+
221+
## Migration guidance
222+
223+
Existing apps do not need to change code to adopt UIScene. To enable multi-window capabilities and Scene events:
224+
225+
1. Add the scene manifest to `Info.plist` (see above).
226+
2. Listen to `SceneEvents` to tailor behavior per window.
227+
1. If you open additional windows, set their root views with `Application.ios.setWindowRootView` during `sceneContentSetup`.
228+
229+
## Troubleshooting
230+
231+
- “UIScene lifecycle will soon be required” in Xcode: add the scene manifest to `Info.plist` to adopt UIScene.
232+
- No multiple windows on iPhone: expected; iPhone uses UIScene lifecycle but doesn’t expose multi-window UX to users.
233+
- `supportsMultipleScenes()` returns false on simulator: test on a physical iPad where multi-window is supported.
234+
235+
## Summary
236+
237+
With UIScene enabled, NativeScript gives you:
238+
239+
- Scene-aware events for window lifecycle handling
240+
- APIs to inspect scenes and windows and set scene-specific root views
241+
- Backwards-compatible behavior for apps that haven’t yet adopted scenes
242+
243+
Use the examples above as a starting point to build multi-window workflows on iPadOS and visionOS while keeping your app ready for the future UIScene requirement on iOS.
244+

0 commit comments

Comments
 (0)