@nativescript/core 9.1.0-next.3 → 9.1.0-next.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/accessibility/accessibility-common.d.ts +8 -0
- package/accessibility/accessibility-common.js +28 -0
- package/accessibility/accessibility-common.js.map +1 -1
- package/application/application-common.d.ts +187 -3
- package/application/application-common.js +397 -54
- package/application/application-common.js.map +1 -1
- package/application/application-interfaces.d.ts +59 -1
- package/application/application.android.d.ts +21 -0
- package/application/application.android.js +189 -7
- package/application/application.android.js.map +1 -1
- package/application/application.d.ts +121 -66
- package/application/application.ios.d.ts +88 -26
- package/application/application.ios.js +474 -351
- package/application/application.ios.js.map +1 -1
- package/application/helpers-common.d.ts +5 -0
- package/application/helpers-common.js +23 -1
- package/application/helpers-common.js.map +1 -1
- package/application/scene-delegate-bridge.d.ts +55 -0
- package/application/scene-delegate-bridge.js +122 -0
- package/application/scene-delegate-bridge.js.map +1 -0
- package/config/config.interface.d.ts +38 -2
- package/css/system-classes.d.ts +9 -0
- package/css/system-classes.js +23 -0
- package/css/system-classes.js.map +1 -1
- package/data/observable/index.d.ts +5 -0
- package/data/observable/index.js +9 -0
- package/data/observable/index.js.map +1 -1
- package/global-types.d.ts +39 -33
- package/globals/global-utils.d.ts +6 -1
- package/globals/global-utils.js +10 -5
- package/globals/global-utils.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/native-window/index.d.ts +426 -0
- package/native-window/index.js +4 -0
- package/native-window/index.js.map +1 -0
- package/native-window/native-window-common.d.ts +153 -0
- package/native-window/native-window-common.js +340 -0
- package/native-window/native-window-common.js.map +1 -0
- package/native-window/native-window-interfaces.d.ts +186 -0
- package/native-window/native-window-interfaces.js +90 -0
- package/native-window/native-window-interfaces.js.map +1 -0
- package/native-window/native-window.android.d.ts +59 -0
- package/native-window/native-window.android.js +175 -0
- package/native-window/native-window.android.js.map +1 -0
- package/native-window/native-window.ios.d.ts +57 -0
- package/native-window/native-window.ios.js +208 -0
- package/native-window/native-window.ios.js.map +1 -0
- package/native-window/window-base.d.ts +103 -0
- package/native-window/window-base.js +78 -0
- package/native-window/window-base.js.map +1 -0
- package/package.json +1 -1
- package/ui/core/view/index.android.js +3 -1
- package/ui/core/view/index.android.js.map +1 -1
- package/ui/core/view/index.d.ts +25 -0
- package/ui/core/view/view-common.d.ts +19 -0
- package/ui/core/view/view-common.js +34 -0
- package/ui/core/view/view-common.js.map +1 -1
- package/ui/core/view-base/index.js +6 -0
- package/ui/core/view-base/index.js.map +1 -1
- package/ui/frame/frame-common.d.ts +14 -2
- package/ui/frame/frame-common.js +17 -5
- package/ui/frame/frame-common.js.map +1 -1
- package/ui/frame/frame-constants.d.ts +9 -0
- package/ui/frame/frame-constants.js +10 -0
- package/ui/frame/frame-constants.js.map +1 -0
- package/ui/frame/frame-helper-for-android.d.ts +2 -2
- package/ui/frame/frame-helper-for-android.js +2 -2
- package/ui/frame/frame-helper-for-android.js.map +1 -1
- package/ui/frame/frame-interfaces.d.ts +1 -1
- package/ui/frame/frame-stack.d.ts +17 -1
- package/ui/frame/frame-stack.js +32 -5
- package/ui/frame/frame-stack.js.map +1 -1
- package/ui/frame/index.android.d.ts +1 -1
- package/ui/frame/index.android.js +111 -38
- package/ui/frame/index.android.js.map +1 -1
- package/ui/frame/index.d.ts +15 -5
- package/utils/native-helper.d.ts +2 -2
|
@@ -3,10 +3,14 @@ import { isEmbedded } from '../ui/embedding';
|
|
|
3
3
|
import { IOSHelper } from '../ui/core/view/view-helper';
|
|
4
4
|
import { getWindow } from '../utils/native-helper';
|
|
5
5
|
import { SDK_VERSION } from '../utils/constants';
|
|
6
|
-
import { ios as iosUtils, dataSerialize } from '../utils/native-helper';
|
|
7
|
-
import { ApplicationCommon
|
|
6
|
+
import { ios as iosUtils, dataSerialize, dataDeserialize } from '../utils/native-helper';
|
|
7
|
+
import { ApplicationCommon } from './application-common';
|
|
8
|
+
import { deliverShortcutItem, forwardContinueUserActivity, forwardOpenURLContexts, oneShotCompletion } from './scene-delegate-bridge';
|
|
8
9
|
import { Observable } from '../data/observable';
|
|
9
10
|
import { Trace } from '../trace';
|
|
11
|
+
import { IOSNativeWindow } from '../native-window/native-window.ios';
|
|
12
|
+
import { NativeWindow } from '../native-window/native-window-common';
|
|
13
|
+
import { NativeWindowEvents } from '../native-window/native-window-interfaces';
|
|
10
14
|
import { AccessibilityServiceEnabledPropName, CommonA11YServiceEnabledObservable, SharedA11YObservable, a11yServiceClasses, a11yServiceDisabledClass, a11yServiceEnabledClass, fontScaleCategoryClasses, fontScaleExtraLargeCategoryClass, fontScaleExtraSmallCategoryClass, fontScaleMediumCategoryClass, getCurrentA11YServiceClass, getCurrentFontScaleCategory, getCurrentFontScaleClass, getFontScaleCssClasses, setCurrentA11YServiceClass, setCurrentFontScaleCategory, setCurrentFontScaleClass, setFontScaleCssClasses, FontScaleCategory, getClosestValidFontScale, VALID_FONT_SCALES, setFontScale, getFontScale, setInitFontScale, getFontScaleCategory, setInitAccessibilityCssHelper, notifyAccessibilityFocusState, AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait, isA11yEnabled, setA11yEnabled, enforceArray, } from '../accessibility/accessibility-common';
|
|
11
15
|
import { CoreTypes } from '../core-types';
|
|
12
16
|
import { getiOSWindow, setA11yUpdatePropertiesCallback, setApplicationPropertiesCallback, setAppMainEntry, setiOSWindow, setRootView, setToggleApplicationEventListenersCallback } from './helpers-common';
|
|
@@ -39,6 +43,7 @@ var CADisplayLinkTarget = (function (_super) {
|
|
|
39
43
|
return target;
|
|
40
44
|
};
|
|
41
45
|
CADisplayLinkTarget.prototype.onDisplayed = function (link) {
|
|
46
|
+
var _a;
|
|
42
47
|
link.invalidate();
|
|
43
48
|
var owner = this._owner.deref();
|
|
44
49
|
if (!owner) {
|
|
@@ -50,6 +55,7 @@ var CADisplayLinkTarget = (function (_super) {
|
|
|
50
55
|
object: owner,
|
|
51
56
|
ios: UIApplication.sharedApplication,
|
|
52
57
|
});
|
|
58
|
+
(_a = owner.primaryWindow) === null || _a === void 0 ? void 0 : _a._notifyEvent(NativeWindowEvents.displayed);
|
|
53
59
|
owner.displayedLinkTarget = null;
|
|
54
60
|
owner.displayedLink = null;
|
|
55
61
|
};
|
|
@@ -113,8 +119,28 @@ if (supportsScenes()) {
|
|
|
113
119
|
* Detected by the Info.plist existence 'UIApplicationSceneManifest'.
|
|
114
120
|
* If this method is implemented when there is no manifest defined,
|
|
115
121
|
* the app will boot to a white screen.
|
|
122
|
+
*
|
|
123
|
+
* Since we configure the delegate dynamically here, UISceneConfigurations
|
|
124
|
+
* does NOT need to be present in Info.plist — only UIApplicationSceneManifest is required.
|
|
125
|
+
*
|
|
126
|
+
* NativeScript only handles UIWindowSceneSessionRoleApplication by default.
|
|
127
|
+
* Other scene types (CarPlay, external displays, etc.) are ignored unless
|
|
128
|
+
* the user provides an `onSceneConfiguration` callback.
|
|
116
129
|
*/
|
|
117
130
|
Responder.prototype.applicationConfigurationForConnectingSceneSessionOptions = function (application, connectingSceneSession, options) {
|
|
131
|
+
// Let the user intercept scene configuration for any/all scenes
|
|
132
|
+
const userHandler = Application.ios._onSceneConfiguration;
|
|
133
|
+
if (userHandler) {
|
|
134
|
+
const userConfig = userHandler(application, connectingSceneSession, options);
|
|
135
|
+
if (userConfig) {
|
|
136
|
+
return userConfig;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Only handle the standard window scene role — skip CarPlay, external displays, etc.
|
|
140
|
+
if (connectingSceneSession.role !== UIWindowSceneSessionRoleApplication) {
|
|
141
|
+
// Return a bare configuration so iOS doesn't crash, but NativeScript won't manage it
|
|
142
|
+
return UISceneConfiguration.configurationWithNameSessionRole('Unmanaged', connectingSceneSession.role);
|
|
143
|
+
}
|
|
118
144
|
const config = UISceneConfiguration.configurationWithNameSessionRole('Default Configuration', connectingSceneSession.role);
|
|
119
145
|
config.sceneClass = UIWindowScene;
|
|
120
146
|
config.delegateClass = SceneDelegate;
|
|
@@ -122,10 +148,20 @@ if (supportsScenes()) {
|
|
|
122
148
|
};
|
|
123
149
|
// scene session destruction handling
|
|
124
150
|
Responder.prototype.applicationDidDiscardSceneSessions = function (application, sceneSessions) {
|
|
125
|
-
|
|
126
|
-
// console.log('Scene sessions discarded:', sceneSessions.count);
|
|
151
|
+
Application.ios._onSceneSessionsDiscarded(sceneSessions);
|
|
127
152
|
};
|
|
128
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Reads the payload `openWindow()` put on the activating NSUserActivity.
|
|
156
|
+
*/
|
|
157
|
+
function getSceneConnectionData(connectionOptions) {
|
|
158
|
+
const activities = connectionOptions?.userActivities;
|
|
159
|
+
if (!activities || activities.count === 0) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
const activity = activities.allObjects.objectAtIndex(0);
|
|
163
|
+
return activity?.userInfo ? dataDeserialize(activity.userInfo) : undefined;
|
|
164
|
+
}
|
|
129
165
|
var SceneDelegate = (function (_super) {
|
|
130
166
|
__extends(SceneDelegate, _super);
|
|
131
167
|
function SceneDelegate() {
|
|
@@ -148,48 +184,234 @@ var SceneDelegate = (function (_super) {
|
|
|
148
184
|
if (!(scene instanceof UIWindowScene)) {
|
|
149
185
|
return;
|
|
150
186
|
}
|
|
151
|
-
var
|
|
152
|
-
|
|
153
|
-
this.
|
|
154
|
-
|
|
155
|
-
|
|
187
|
+
var windowScene = scene;
|
|
188
|
+
var isFirstScene = Application.ios._getWindows().length === 0;
|
|
189
|
+
this._scene = windowScene;
|
|
190
|
+
this._window = UIWindow.alloc().initWithWindowScene(windowScene);
|
|
191
|
+
if (!__VISIONOS__) {
|
|
192
|
+
this._window.backgroundColor = SDK_VERSION <= 12 || !UIColor.systemBackgroundColor ? UIColor.whiteColor : UIColor.systemBackgroundColor;
|
|
193
|
+
}
|
|
194
|
+
var nativeWindowId = IOSNativeWindow.getSceneId(windowScene);
|
|
195
|
+
var knownWindow = nativeWindowId ? Application.ios.getWindowById(nativeWindowId) : undefined;
|
|
196
|
+
var nativeWindow;
|
|
197
|
+
if ((knownWindow === null || knownWindow === void 0 ? void 0 : knownWindow.state) === "detached") {
|
|
198
|
+
nativeWindow = knownWindow;
|
|
199
|
+
nativeWindow._reattach(windowScene, this._window);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
var isPrimary_1 = isFirstScene || !Application.ios.primaryWindow;
|
|
203
|
+
nativeWindow = new IOSNativeWindow(windowScene, this._window, nativeWindowId, isPrimary_1);
|
|
204
|
+
Application.ios._registerWindow(nativeWindow);
|
|
205
|
+
}
|
|
206
|
+
var isPrimary = nativeWindow.isPrimary;
|
|
207
|
+
nativeWindow._notifyEvent(NativeWindowEvents.attached);
|
|
208
|
+
if (isPrimary) {
|
|
209
|
+
setiOSWindow(this._window);
|
|
210
|
+
}
|
|
211
|
+
nativeWindow.notify({
|
|
212
|
+
eventName: NativeWindowEvents.sceneWillConnect,
|
|
213
|
+
object: nativeWindow,
|
|
214
|
+
window: nativeWindow,
|
|
215
|
+
scene: windowScene,
|
|
216
|
+
uiWindow: this._window,
|
|
217
|
+
connectionOptions: connectionOptions,
|
|
218
|
+
});
|
|
156
219
|
Application.ios.notify({
|
|
157
|
-
eventName:
|
|
220
|
+
eventName: NativeWindowEvents.sceneWillConnect,
|
|
158
221
|
object: Application.ios,
|
|
159
|
-
|
|
160
|
-
|
|
222
|
+
window: nativeWindow,
|
|
223
|
+
scene: windowScene,
|
|
224
|
+
uiWindow: this._window,
|
|
161
225
|
connectionOptions: connectionOptions,
|
|
162
226
|
});
|
|
163
|
-
if (
|
|
227
|
+
if (isPrimary) {
|
|
164
228
|
this._window.makeKeyAndVisible();
|
|
165
229
|
}
|
|
166
|
-
|
|
167
|
-
Application.ios.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
connectionOptions: connectionOptions,
|
|
230
|
+
if (nativeWindow.role === "application") {
|
|
231
|
+
Application.ios._resolveWindowContent(nativeWindow, {
|
|
232
|
+
window: nativeWindow,
|
|
233
|
+
isPrimary: isPrimary,
|
|
234
|
+
data: getSceneConnectionData(connectionOptions),
|
|
235
|
+
ios: { connectionOptions: connectionOptions },
|
|
173
236
|
});
|
|
174
237
|
}
|
|
175
|
-
if (isFirstScene) {
|
|
176
|
-
Application.ios._notifySceneAppStarted();
|
|
177
|
-
}
|
|
178
238
|
};
|
|
179
239
|
SceneDelegate.prototype.sceneDidBecomeActive = function (scene) {
|
|
240
|
+
var windowScene = scene;
|
|
241
|
+
var nativeWindow = Application.ios._getWindowForScene(windowScene);
|
|
242
|
+
if (nativeWindow) {
|
|
243
|
+
nativeWindow._notifyEvent(NativeWindowEvents.activate);
|
|
244
|
+
nativeWindow.notify({
|
|
245
|
+
eventName: NativeWindowEvents.sceneDidActivate,
|
|
246
|
+
object: nativeWindow,
|
|
247
|
+
window: nativeWindow,
|
|
248
|
+
scene: windowScene,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
Application.ios.notify({
|
|
252
|
+
eventName: NativeWindowEvents.sceneDidActivate,
|
|
253
|
+
object: Application.ios,
|
|
254
|
+
window: nativeWindow,
|
|
255
|
+
scene: windowScene,
|
|
256
|
+
});
|
|
257
|
+
var rootView = nativeWindow === null || nativeWindow === void 0 ? void 0 : nativeWindow.rootView;
|
|
258
|
+
if (rootView && !rootView.isLoaded) {
|
|
259
|
+
rootView.callLoaded();
|
|
260
|
+
}
|
|
180
261
|
};
|
|
181
262
|
SceneDelegate.prototype.sceneWillResignActive = function (scene) {
|
|
263
|
+
var windowScene = scene;
|
|
264
|
+
var nativeWindow = Application.ios._getWindowForScene(windowScene);
|
|
265
|
+
if (nativeWindow) {
|
|
266
|
+
nativeWindow._notifyEvent(NativeWindowEvents.deactivate);
|
|
267
|
+
nativeWindow.notify({
|
|
268
|
+
eventName: NativeWindowEvents.sceneWillResignActive,
|
|
269
|
+
object: nativeWindow,
|
|
270
|
+
window: nativeWindow,
|
|
271
|
+
scene: windowScene,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
182
274
|
Application.ios.notify({
|
|
183
|
-
eventName:
|
|
275
|
+
eventName: NativeWindowEvents.sceneWillResignActive,
|
|
184
276
|
object: Application.ios,
|
|
185
|
-
|
|
277
|
+
window: nativeWindow,
|
|
278
|
+
scene: windowScene,
|
|
186
279
|
});
|
|
187
280
|
};
|
|
188
281
|
SceneDelegate.prototype.sceneWillEnterForeground = function (scene) {
|
|
282
|
+
var windowScene = scene;
|
|
283
|
+
var nativeWindow = Application.ios._getWindowForScene(windowScene);
|
|
284
|
+
if (nativeWindow) {
|
|
285
|
+
nativeWindow._notifyEvent(NativeWindowEvents.foreground);
|
|
286
|
+
nativeWindow.notify({
|
|
287
|
+
eventName: NativeWindowEvents.sceneWillEnterForeground,
|
|
288
|
+
object: nativeWindow,
|
|
289
|
+
window: nativeWindow,
|
|
290
|
+
scene: windowScene,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
Application.ios.notify({
|
|
294
|
+
eventName: NativeWindowEvents.sceneWillEnterForeground,
|
|
295
|
+
object: Application.ios,
|
|
296
|
+
window: nativeWindow,
|
|
297
|
+
scene: windowScene,
|
|
298
|
+
});
|
|
189
299
|
};
|
|
190
300
|
SceneDelegate.prototype.sceneDidEnterBackground = function (scene) {
|
|
301
|
+
var windowScene = scene;
|
|
302
|
+
var nativeWindow = Application.ios._getWindowForScene(windowScene);
|
|
303
|
+
if (nativeWindow) {
|
|
304
|
+
nativeWindow._notifyEvent(NativeWindowEvents.background);
|
|
305
|
+
nativeWindow.notify({
|
|
306
|
+
eventName: NativeWindowEvents.sceneDidEnterBackground,
|
|
307
|
+
object: nativeWindow,
|
|
308
|
+
window: nativeWindow,
|
|
309
|
+
scene: windowScene,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
Application.ios.notify({
|
|
313
|
+
eventName: NativeWindowEvents.sceneDidEnterBackground,
|
|
314
|
+
object: Application.ios,
|
|
315
|
+
window: nativeWindow,
|
|
316
|
+
scene: windowScene,
|
|
317
|
+
});
|
|
318
|
+
var rootView = nativeWindow === null || nativeWindow === void 0 ? void 0 : nativeWindow.rootView;
|
|
319
|
+
if (rootView && rootView.isLoaded) {
|
|
320
|
+
rootView.callUnloaded();
|
|
321
|
+
}
|
|
191
322
|
};
|
|
192
323
|
SceneDelegate.prototype.sceneDidDisconnect = function (scene) {
|
|
324
|
+
var windowScene = scene;
|
|
325
|
+
var nativeWindow = Application.ios._getWindowForScene(windowScene);
|
|
326
|
+
if (nativeWindow) {
|
|
327
|
+
var isClosing = nativeWindow._closeRequested || !nativeWindow._hasSessionIdentity;
|
|
328
|
+
if (isClosing) {
|
|
329
|
+
nativeWindow._notifyEvent(NativeWindowEvents.close);
|
|
330
|
+
}
|
|
331
|
+
nativeWindow.notify({
|
|
332
|
+
eventName: NativeWindowEvents.sceneDidDisconnect,
|
|
333
|
+
object: nativeWindow,
|
|
334
|
+
window: nativeWindow,
|
|
335
|
+
scene: windowScene,
|
|
336
|
+
});
|
|
337
|
+
if (isClosing) {
|
|
338
|
+
Application.ios._unregisterWindow(nativeWindow);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
nativeWindow._detach();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
Application.ios.notify({
|
|
345
|
+
eventName: NativeWindowEvents.sceneDidDisconnect,
|
|
346
|
+
object: Application.ios,
|
|
347
|
+
window: nativeWindow,
|
|
348
|
+
scene: windowScene,
|
|
349
|
+
});
|
|
350
|
+
};
|
|
351
|
+
SceneDelegate.prototype.sceneOpenURLContexts = function (scene, URLContexts) {
|
|
352
|
+
var windowScene = scene;
|
|
353
|
+
var nativeWindow = Application.ios._getWindowForScene(windowScene);
|
|
354
|
+
if (nativeWindow) {
|
|
355
|
+
nativeWindow.notify({
|
|
356
|
+
eventName: NativeWindowEvents.sceneOpenURLContexts,
|
|
357
|
+
object: nativeWindow,
|
|
358
|
+
window: nativeWindow,
|
|
359
|
+
scene: windowScene,
|
|
360
|
+
urlContexts: URLContexts,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
Application.ios.notify({
|
|
364
|
+
eventName: NativeWindowEvents.sceneOpenURLContexts,
|
|
365
|
+
object: Application.ios,
|
|
366
|
+
window: nativeWindow,
|
|
367
|
+
scene: windowScene,
|
|
368
|
+
urlContexts: URLContexts,
|
|
369
|
+
});
|
|
370
|
+
forwardOpenURLContexts(Application.ios.delegate, UIApplication.sharedApplication, URLContexts);
|
|
371
|
+
};
|
|
372
|
+
SceneDelegate.prototype.sceneContinueUserActivity = function (scene, userActivity) {
|
|
373
|
+
var windowScene = scene;
|
|
374
|
+
var nativeWindow = Application.ios._getWindowForScene(windowScene);
|
|
375
|
+
if (nativeWindow) {
|
|
376
|
+
nativeWindow.notify({
|
|
377
|
+
eventName: NativeWindowEvents.sceneContinueUserActivity,
|
|
378
|
+
object: nativeWindow,
|
|
379
|
+
window: nativeWindow,
|
|
380
|
+
scene: windowScene,
|
|
381
|
+
userActivity: userActivity,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
Application.ios.notify({
|
|
385
|
+
eventName: NativeWindowEvents.sceneContinueUserActivity,
|
|
386
|
+
object: Application.ios,
|
|
387
|
+
window: nativeWindow,
|
|
388
|
+
scene: windowScene,
|
|
389
|
+
userActivity: userActivity,
|
|
390
|
+
});
|
|
391
|
+
forwardContinueUserActivity(Application.ios.delegate, UIApplication.sharedApplication, userActivity);
|
|
392
|
+
};
|
|
393
|
+
SceneDelegate.prototype.windowScenePerformActionForShortcutItemCompletionHandler = function (windowScene, shortcutItem, completionHandler) {
|
|
394
|
+
var nativeWindow = Application.ios._getWindowForScene(windowScene);
|
|
395
|
+
var deliver = oneShotCompletion(completionHandler);
|
|
396
|
+
if (nativeWindow) {
|
|
397
|
+
nativeWindow.notify({
|
|
398
|
+
eventName: NativeWindowEvents.scenePerformActionForShortcutItem,
|
|
399
|
+
object: nativeWindow,
|
|
400
|
+
window: nativeWindow,
|
|
401
|
+
scene: windowScene,
|
|
402
|
+
shortcutItem: shortcutItem,
|
|
403
|
+
completionHandler: deliver,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
Application.ios.notify({
|
|
407
|
+
eventName: NativeWindowEvents.scenePerformActionForShortcutItem,
|
|
408
|
+
object: Application.ios,
|
|
409
|
+
window: nativeWindow,
|
|
410
|
+
scene: windowScene,
|
|
411
|
+
shortcutItem: shortcutItem,
|
|
412
|
+
completionHandler: deliver,
|
|
413
|
+
});
|
|
414
|
+
deliverShortcutItem(Application.ios.delegate, UIApplication.sharedApplication, shortcutItem, deliver);
|
|
193
415
|
};
|
|
194
416
|
SceneDelegate.ObjCProtocols = [UIWindowSceneDelegate];
|
|
195
417
|
return SceneDelegate;
|
|
@@ -203,31 +425,19 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
203
425
|
constructor() {
|
|
204
426
|
super();
|
|
205
427
|
this._delegateHandlers = new Map();
|
|
206
|
-
this.launchEventCalled = false;
|
|
207
|
-
this._windowSceneMap = new Map();
|
|
208
|
-
this._primaryScene = null;
|
|
209
|
-
this._openedScenesById = new Map();
|
|
210
428
|
this._notificationObservers = [];
|
|
211
429
|
this._softRebootSceneDelegates = new Map();
|
|
212
430
|
this.displayedOnce = false;
|
|
431
|
+
/**
|
|
432
|
+
* @deprecated Has no effect. Application initialization is signalled by the 'ready' event, which is never deferred.
|
|
433
|
+
*/
|
|
213
434
|
this.shouldDelayLaunchEvent = false;
|
|
214
435
|
this.addNotificationObserver(UIApplicationDidFinishLaunchingNotification, this.didFinishLaunchingWithOptions.bind(this));
|
|
436
|
+
this.addNotificationObserver(UIApplicationDidBecomeActiveNotification, this.didBecomeActive.bind(this));
|
|
437
|
+
this.addNotificationObserver(UIApplicationDidEnterBackgroundNotification, this.didEnterBackground.bind(this));
|
|
215
438
|
this.addNotificationObserver(UIApplicationWillTerminateNotification, this.willTerminate.bind(this));
|
|
216
439
|
this.addNotificationObserver(UIApplicationDidReceiveMemoryWarningNotification, this.didReceiveMemoryWarning.bind(this));
|
|
217
440
|
this.addNotificationObserver(UIApplicationDidChangeStatusBarOrientationNotification, this.didChangeStatusBarOrientation.bind(this));
|
|
218
|
-
// Add scene lifecycle notification observers only if scenes are supported
|
|
219
|
-
if (this.supportsScenes()) {
|
|
220
|
-
this.addNotificationObserver('UISceneWillConnectNotification', this.sceneWillConnect.bind(this));
|
|
221
|
-
this.addNotificationObserver('UISceneDidActivateNotification', this.sceneDidActivate.bind(this));
|
|
222
|
-
this.addNotificationObserver('UISceneWillEnterForegroundNotification', this.sceneWillEnterForeground.bind(this));
|
|
223
|
-
this.addNotificationObserver('UISceneDidEnterBackgroundNotification', this.sceneDidEnterBackground.bind(this));
|
|
224
|
-
this.addNotificationObserver('UISceneDidDisconnectNotification', this.sceneDidDisconnect.bind(this));
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
// For scene-based apps, the below are not needed as they are handled by the scene notifications
|
|
228
|
-
this.addNotificationObserver(UIApplicationDidBecomeActiveNotification, this.didBecomeActive.bind(this));
|
|
229
|
-
this.addNotificationObserver(UIApplicationDidEnterBackgroundNotification, this.didEnterBackground.bind(this));
|
|
230
|
-
}
|
|
231
441
|
}
|
|
232
442
|
getRootView() {
|
|
233
443
|
return this._rootView;
|
|
@@ -290,6 +500,7 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
290
500
|
UIApplicationMain(0, null, null, this.delegate ? NSStringFromClass(this.delegate) : NSStringFromClass(Responder));
|
|
291
501
|
}
|
|
292
502
|
runAsEmbeddedApp() {
|
|
503
|
+
this.notifyReady();
|
|
293
504
|
this._reattachNativeDelegatesAfterSoftReboot();
|
|
294
505
|
// TODO: this rootView should be held alive until rootController dismissViewController is called.
|
|
295
506
|
const rootView = this.createRootView(this._rootView, true);
|
|
@@ -322,8 +533,17 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
322
533
|
}
|
|
323
534
|
if (targetScene) {
|
|
324
535
|
window = UIWindow.alloc().initWithWindowScene(targetScene);
|
|
325
|
-
|
|
326
|
-
|
|
536
|
+
if (!__VISIONOS__) {
|
|
537
|
+
window.backgroundColor = SDK_VERSION <= 12 || !UIColor.systemBackgroundColor ? UIColor.whiteColor : UIColor.systemBackgroundColor;
|
|
538
|
+
}
|
|
539
|
+
// The registry lives in JS and was lost with the previous isolate, so
|
|
540
|
+
// the still-connected scene needs a fresh NativeWindow to be reachable.
|
|
541
|
+
const isPrimary = !this.primaryWindow;
|
|
542
|
+
const nativeWindow = new IOSNativeWindow(targetScene, window, IOSNativeWindow.getSceneId(targetScene), isPrimary);
|
|
543
|
+
this._registerWindow(nativeWindow);
|
|
544
|
+
if (isPrimary) {
|
|
545
|
+
setiOSWindow(window);
|
|
546
|
+
}
|
|
327
547
|
// If the scene's delegate was recreated after a soft reboot, point it
|
|
328
548
|
// at the new window so `scene.delegate.window` queries resolve.
|
|
329
549
|
const freshSceneDelegate = this._softRebootSceneDelegates.get(targetScene);
|
|
@@ -343,31 +563,16 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
343
563
|
if (embedderDelegate && !rootController) {
|
|
344
564
|
return;
|
|
345
565
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const layoutDirection = controller.traitCollection.layoutDirection;
|
|
355
|
-
const newLayoutDirection = this.getLayoutDirectionValue(layoutDirection);
|
|
356
|
-
this.setLayoutDirection(newLayoutDirection);
|
|
357
|
-
});
|
|
358
|
-
if (embedderDelegate) {
|
|
359
|
-
// Embed into host app.
|
|
360
|
-
// present over the host's existing root view controller.
|
|
361
|
-
this.setViewControllerView(rootView);
|
|
362
|
-
embedderDelegate.presentNativeScriptApp(controller);
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
// No embedder delegate = NativeScript owns the UIApplication.
|
|
366
|
-
// Attach the root to the window.
|
|
367
|
-
this.setViewControllerView(rootView);
|
|
368
|
-
this.setWindowRootView(window, rootView);
|
|
566
|
+
let hostWindow = this.primaryWindow;
|
|
567
|
+
if (!hostWindow) {
|
|
568
|
+
// Only an embedder delegate makes this window a guest: without one NativeScript
|
|
569
|
+
// owns the UIApplication and the window has to attach its own content.
|
|
570
|
+
const role = embedderDelegate ? 'embedded' : 'application';
|
|
571
|
+
hostWindow = new IOSNativeWindow(window.windowScene ?? undefined, window, 'embedded-main', true, role);
|
|
572
|
+
this._registerWindow(hostWindow);
|
|
573
|
+
hostWindow._notifyEvent(NativeWindowEvents.attached);
|
|
369
574
|
}
|
|
370
|
-
|
|
575
|
+
hostWindow.setContent(rootView);
|
|
371
576
|
this.notifyAppStarted();
|
|
372
577
|
}
|
|
373
578
|
/**
|
|
@@ -609,7 +814,6 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
609
814
|
}
|
|
610
815
|
}
|
|
611
816
|
notifyAppStarted(notification) {
|
|
612
|
-
this.launchEventCalled = true;
|
|
613
817
|
const root = this.notifyLaunch({
|
|
614
818
|
ios: notification?.userInfo?.objectForKey('UIApplicationLaunchOptionsLocalNotificationKey') ?? null,
|
|
615
819
|
});
|
|
@@ -622,10 +826,6 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
622
826
|
setiOSWindow(this.window);
|
|
623
827
|
}
|
|
624
828
|
}
|
|
625
|
-
// Public method for scene-based app startup
|
|
626
|
-
_notifySceneAppStarted() {
|
|
627
|
-
this.notifyAppStarted();
|
|
628
|
-
}
|
|
629
829
|
_onLivesync(context) {
|
|
630
830
|
// Handle application root module
|
|
631
831
|
const isAppRootModuleChanged = context && context.path && context.path.includes(this.getMainEntry().moduleName) && context.type !== 'style';
|
|
@@ -640,14 +840,23 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
640
840
|
}
|
|
641
841
|
}
|
|
642
842
|
setWindowContent(view) {
|
|
843
|
+
const rootView = this.createRootView(view);
|
|
844
|
+
const primaryWindow = this.primaryWindow;
|
|
845
|
+
if (primaryWindow) {
|
|
846
|
+
primaryWindow.setContent(rootView);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
this.setWindowContentFallback(rootView);
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Attaches content to the raw `UIWindow`. Every launch path registers a primary
|
|
853
|
+
* NativeWindow, so this only runs when no window is left to own the content.
|
|
854
|
+
*/
|
|
855
|
+
setWindowContentFallback(rootView) {
|
|
643
856
|
if (this._rootView) {
|
|
644
|
-
// if we already have a root view, we reset it.
|
|
645
857
|
this._rootView._onRootViewReset();
|
|
646
858
|
}
|
|
647
|
-
const rootView = this.createRootView(view);
|
|
648
859
|
const controller = this.getViewController(rootView);
|
|
649
|
-
this._rootView = rootView;
|
|
650
|
-
setRootView(rootView);
|
|
651
860
|
// setup view as styleScopeHost
|
|
652
861
|
rootView._setupAsRootView({});
|
|
653
862
|
this.setViewControllerView(rootView);
|
|
@@ -657,17 +866,7 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
657
866
|
if (!haveController) {
|
|
658
867
|
win.makeKeyAndVisible();
|
|
659
868
|
}
|
|
660
|
-
this.
|
|
661
|
-
rootView.on(IOSHelper.traitCollectionColorAppearanceChangedEvent, () => {
|
|
662
|
-
const userInterfaceStyle = controller.traitCollection.userInterfaceStyle;
|
|
663
|
-
const newSystemAppearance = this.getSystemAppearanceValue(userInterfaceStyle);
|
|
664
|
-
this.setSystemAppearance(newSystemAppearance);
|
|
665
|
-
});
|
|
666
|
-
rootView.on(IOSHelper.traitCollectionLayoutDirectionChangedEvent, () => {
|
|
667
|
-
const layoutDirection = controller.traitCollection.layoutDirection;
|
|
668
|
-
const newLayoutDirection = this.getLayoutDirectionValue(layoutDirection);
|
|
669
|
-
this.setLayoutDirection(newLayoutDirection);
|
|
670
|
-
});
|
|
869
|
+
this.adoptRootView(rootView);
|
|
671
870
|
}
|
|
672
871
|
// Observers
|
|
673
872
|
didFinishLaunchingWithOptions(notification) {
|
|
@@ -685,6 +884,8 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
685
884
|
}
|
|
686
885
|
}
|
|
687
886
|
}
|
|
887
|
+
// Must precede every window registration below and every scene connect that follows.
|
|
888
|
+
this.notifyReady();
|
|
688
889
|
this.setMaxRefreshRate();
|
|
689
890
|
// Only set up window if NOT using scene-based lifecycle
|
|
690
891
|
if (!this.supportsScenes()) {
|
|
@@ -698,9 +899,26 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
698
899
|
if (!__VISIONOS__) {
|
|
699
900
|
this.window.backgroundColor = SDK_VERSION <= 12 || !UIColor.systemBackgroundColor ? UIColor.whiteColor : UIColor.systemBackgroundColor;
|
|
700
901
|
}
|
|
701
|
-
this.
|
|
702
|
-
|
|
703
|
-
this.
|
|
902
|
+
if (!this.primaryWindow) {
|
|
903
|
+
const nativeWindow = new IOSNativeWindow(undefined, this.window, 'main', true, 'application');
|
|
904
|
+
this._registerWindow(nativeWindow);
|
|
905
|
+
nativeWindow._notifyEvent(NativeWindowEvents.attached);
|
|
906
|
+
}
|
|
907
|
+
const primaryWindow = this.primaryWindow;
|
|
908
|
+
const resolveContent = () => this._resolveWindowContent(primaryWindow, {
|
|
909
|
+
window: primaryWindow,
|
|
910
|
+
isPrimary: true,
|
|
911
|
+
}, {
|
|
912
|
+
launchData: {
|
|
913
|
+
ios: notification?.userInfo?.objectForKey('UIApplicationLaunchOptionsLocalNotificationKey') ?? null,
|
|
914
|
+
},
|
|
915
|
+
});
|
|
916
|
+
if (UIApplication.sharedApplication.applicationState === 2 /* UIApplicationState.Background */) {
|
|
917
|
+
// A background launch has no UI to build yet, so content waits for the first activation.
|
|
918
|
+
this._pendingWindowContentResolve = resolveContent;
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
resolveContent();
|
|
704
922
|
}
|
|
705
923
|
}
|
|
706
924
|
else {
|
|
@@ -708,17 +926,22 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
708
926
|
}
|
|
709
927
|
}
|
|
710
928
|
didBecomeActive(notification) {
|
|
711
|
-
|
|
712
|
-
|
|
929
|
+
const pendingWindowContentResolve = this._pendingWindowContentResolve;
|
|
930
|
+
if (pendingWindowContentResolve) {
|
|
931
|
+
this._pendingWindowContentResolve = null;
|
|
932
|
+
pendingWindowContentResolve();
|
|
713
933
|
}
|
|
714
934
|
const additionalData = {
|
|
715
935
|
ios: UIApplication.sharedApplication,
|
|
716
936
|
};
|
|
717
937
|
this.setInBackground(false, additionalData);
|
|
718
938
|
this.setSuspended(false, additionalData);
|
|
719
|
-
|
|
720
|
-
if (
|
|
721
|
-
rootView.
|
|
939
|
+
// In scene mode the root view belongs to a window, so the scene delegate loads it.
|
|
940
|
+
if (!this.supportsScenes()) {
|
|
941
|
+
const rootView = this._rootView;
|
|
942
|
+
if (rootView && !rootView.isLoaded) {
|
|
943
|
+
rootView.callLoaded();
|
|
944
|
+
}
|
|
722
945
|
}
|
|
723
946
|
}
|
|
724
947
|
didEnterBackground(notification) {
|
|
@@ -727,9 +950,11 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
727
950
|
};
|
|
728
951
|
this.setInBackground(true, additionalData);
|
|
729
952
|
this.setSuspended(true, additionalData);
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
rootView.
|
|
953
|
+
if (!this.supportsScenes()) {
|
|
954
|
+
const rootView = this._rootView;
|
|
955
|
+
if (rootView && rootView.isLoaded) {
|
|
956
|
+
rootView.callUnloaded();
|
|
957
|
+
}
|
|
733
958
|
}
|
|
734
959
|
}
|
|
735
960
|
willTerminate(notification) {
|
|
@@ -751,157 +976,111 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
751
976
|
});
|
|
752
977
|
}
|
|
753
978
|
didChangeStatusBarOrientation(notification) {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
this.
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
// Store as primary scene if it's the first one
|
|
765
|
-
if (!this._primaryScene) {
|
|
766
|
-
this._primaryScene = scene;
|
|
979
|
+
// The notification is app-wide, but scenes rotate independently, so every attached
|
|
980
|
+
// window is refreshed from its own scene.
|
|
981
|
+
for (const nativeWindow of this._windows) {
|
|
982
|
+
if (nativeWindow.state !== 'attached') {
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
const orientation = nativeWindow.ios?.scene?.interfaceOrientation ?? UIApplication.sharedApplication.statusBarOrientation;
|
|
986
|
+
nativeWindow._setOrientation(this.getOrientationValue(orientation));
|
|
767
987
|
}
|
|
768
|
-
this.notify({
|
|
769
|
-
eventName: SceneEvents.sceneWillConnect,
|
|
770
|
-
object: this,
|
|
771
|
-
scene: scene,
|
|
772
|
-
userInfo: notification.userInfo,
|
|
773
|
-
});
|
|
774
988
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
this.
|
|
789
|
-
this.setSuspended(false, additionalData);
|
|
790
|
-
if (this._rootView && !this._rootView.isLoaded) {
|
|
791
|
-
this._rootView.callLoaded();
|
|
792
|
-
}
|
|
989
|
+
// --- App-level root view mirror ---
|
|
990
|
+
/**
|
|
991
|
+
* Keeps the app-level root view state (`getRootView()`, the global root view and the
|
|
992
|
+
* `initRootView` event) following whatever the primary window shows.
|
|
993
|
+
*/
|
|
994
|
+
mirrorPrimaryWindow(nativeWindow) {
|
|
995
|
+
if (this._mirroredWindow === nativeWindow) {
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
this._mirroredWindow?.off(NativeWindowEvents.contentLoaded, this.onPrimaryWindowContentLoaded, this);
|
|
999
|
+
this._mirroredWindow = nativeWindow;
|
|
1000
|
+
nativeWindow.on(NativeWindowEvents.contentLoaded, this.onPrimaryWindowContentLoaded, this);
|
|
1001
|
+
if (nativeWindow.rootView && nativeWindow.rootView !== this._rootView) {
|
|
1002
|
+
this.adoptRootView(nativeWindow.rootView);
|
|
793
1003
|
}
|
|
794
1004
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
this.notify({
|
|
798
|
-
eventName: SceneEvents.sceneWillEnterForeground,
|
|
799
|
-
object: this,
|
|
800
|
-
scene: scene,
|
|
801
|
-
});
|
|
1005
|
+
onPrimaryWindowContentLoaded(data) {
|
|
1006
|
+
this.adoptRootView(data.window.rootView);
|
|
802
1007
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
eventName: SceneEvents.sceneDidEnterBackground,
|
|
807
|
-
object: this,
|
|
808
|
-
scene: scene,
|
|
809
|
-
});
|
|
810
|
-
// If this is the primary scene, trigger traditional app lifecycle
|
|
811
|
-
if (scene === this._primaryScene) {
|
|
812
|
-
const additionalData = {
|
|
813
|
-
ios: UIApplication.sharedApplication,
|
|
814
|
-
scene: scene,
|
|
815
|
-
};
|
|
816
|
-
this.setInBackground(true, additionalData);
|
|
817
|
-
this.setSuspended(true, additionalData);
|
|
818
|
-
if (this._rootView && this._rootView.isLoaded) {
|
|
819
|
-
this._rootView.callUnloaded();
|
|
820
|
-
}
|
|
1008
|
+
adoptRootView(rootView) {
|
|
1009
|
+
if (!rootView) {
|
|
1010
|
+
return;
|
|
821
1011
|
}
|
|
1012
|
+
this._rootView = rootView;
|
|
1013
|
+
setRootView(rootView);
|
|
1014
|
+
this.initRootView(rootView, this._mirroredWindow);
|
|
822
1015
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
if (
|
|
831
|
-
|
|
832
|
-
const request = UISceneSessionActivationRequest.requestWithSession(this._primaryScene.session);
|
|
833
|
-
UIApplication.sharedApplication.activateSceneSessionForRequestErrorHandler(request, (err) => {
|
|
834
|
-
if (err) {
|
|
835
|
-
console.log('Failed to activate primary scene:', err.localizedDescription);
|
|
836
|
-
}
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
else {
|
|
840
|
-
UIApplication.sharedApplication.requestSceneSessionActivationUserActivityOptionsErrorHandler(this._primaryScene.session, null, null, (err) => {
|
|
841
|
-
if (err) {
|
|
842
|
-
console.log('Failed to activate primary scene (legacy):', err.localizedDescription);
|
|
843
|
-
}
|
|
844
|
-
});
|
|
845
|
-
}
|
|
1016
|
+
// --- NativeWindow registry ---
|
|
1017
|
+
/**
|
|
1018
|
+
* @internal - iOS reports discarded sessions for windows this JS context may never
|
|
1019
|
+
* have seen (they can arrive on a later launch), so unknown ids are ignored.
|
|
1020
|
+
*/
|
|
1021
|
+
_onSceneSessionsDiscarded(sessions) {
|
|
1022
|
+
const all = sessions?.allObjects;
|
|
1023
|
+
if (!all) {
|
|
1024
|
+
return;
|
|
846
1025
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
}
|
|
853
|
-
// Scene management helper methods
|
|
854
|
-
_setWindowForScene(window, scene) {
|
|
855
|
-
this._windowSceneMap.set(scene, window);
|
|
856
|
-
}
|
|
857
|
-
_removeWindowForScene(scene) {
|
|
858
|
-
this._windowSceneMap.delete(scene);
|
|
859
|
-
// also untrack opened scene id
|
|
860
|
-
try {
|
|
861
|
-
const s = scene;
|
|
862
|
-
if (s && s.session) {
|
|
863
|
-
const id = this._getSceneId(s);
|
|
864
|
-
this._openedScenesById.delete(id);
|
|
1026
|
+
for (let i = 0; i < all.count; i++) {
|
|
1027
|
+
const persistentIdentifier = all.objectAtIndex(i)?.persistentIdentifier;
|
|
1028
|
+
const nativeWindow = persistentIdentifier ? this.getWindowById(`${persistentIdentifier}`) : undefined;
|
|
1029
|
+
if (!nativeWindow) {
|
|
1030
|
+
continue;
|
|
865
1031
|
}
|
|
1032
|
+
nativeWindow._notifyEvent(NativeWindowEvents.close);
|
|
1033
|
+
this._unregisterWindow(nativeWindow);
|
|
866
1034
|
}
|
|
867
|
-
catch { }
|
|
868
1035
|
}
|
|
1036
|
+
/**
|
|
1037
|
+
* @internal - Get a NativeWindow by its scene.
|
|
1038
|
+
*/
|
|
869
1039
|
_getWindowForScene(scene) {
|
|
870
|
-
return this.
|
|
1040
|
+
return this._windows.find((nw) => nw.ios?.scene === scene);
|
|
871
1041
|
}
|
|
872
|
-
|
|
873
|
-
if (
|
|
874
|
-
|
|
875
|
-
}
|
|
876
|
-
// track opened scene
|
|
877
|
-
try {
|
|
878
|
-
const id = this._getSceneId(scene);
|
|
879
|
-
this._openedScenesById.set(id, scene);
|
|
1042
|
+
_onWindowRegistered(nativeWindow) {
|
|
1043
|
+
if (nativeWindow.isPrimary) {
|
|
1044
|
+
this.mirrorPrimaryWindow(nativeWindow);
|
|
880
1045
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
// If this is the primary scene, set up the main application content
|
|
887
|
-
if (scene === this._primaryScene || !this._primaryScene) {
|
|
888
|
-
this._primaryScene = scene;
|
|
889
|
-
if (!getiOSWindow()) {
|
|
890
|
-
setiOSWindow(window);
|
|
891
|
-
}
|
|
892
|
-
// During initial scene startup we must wait for launch to be notified first.
|
|
893
|
-
// Some frameworks provide root content from launch handlers.
|
|
894
|
-
// Guard: skip setWindowContent when no main entry is configured yet.
|
|
895
|
-
// During Vite HMR dev boot, the placeholder calls Application.run() with
|
|
896
|
-
// no entry; the real entry is set later when the HTTP-loaded main module
|
|
897
|
-
// calls Application.run({ moduleName: ... }). Without this guard the
|
|
898
|
-
// scene handler would throw "Main entry is missing" and leave the window
|
|
899
|
-
// in a broken state (root view reset but no replacement created).
|
|
900
|
-
if (this.hasLaunched() && this.getMainEntry()) {
|
|
901
|
-
this.setWindowContent();
|
|
902
|
-
}
|
|
1046
|
+
}
|
|
1047
|
+
_onPrimaryWindowPromoted(nativeWindow) {
|
|
1048
|
+
const promotedWindow = nativeWindow.ios?.uiWindow;
|
|
1049
|
+
if (promotedWindow) {
|
|
1050
|
+
setiOSWindow(promotedWindow);
|
|
903
1051
|
}
|
|
1052
|
+
this.mirrorPrimaryWindow(nativeWindow);
|
|
904
1053
|
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Register a callback to intercept scene configuration.
|
|
1056
|
+
*
|
|
1057
|
+
* Called for every new scene session. Return a `UISceneConfiguration` to handle
|
|
1058
|
+
* the scene yourself (e.g. CarPlay, external display), or return `null`/`undefined`
|
|
1059
|
+
* to let NativeScript handle it with the default SceneDelegate.
|
|
1060
|
+
*
|
|
1061
|
+
* NativeScript only auto-manages `UIWindowSceneSessionRoleApplication` scenes.
|
|
1062
|
+
* All other scene roles are ignored unless you provide a configuration here.
|
|
1063
|
+
*
|
|
1064
|
+
* @example
|
|
1065
|
+
* ```ts
|
|
1066
|
+
* Application.ios.onSceneConfiguration = (app, session, options) => {
|
|
1067
|
+
* if (session.role === CPTemplateApplicationSceneSessionRoleApplication) {
|
|
1068
|
+
* const config = UISceneConfiguration.configurationWithNameSessionRole('CarPlay', session.role);
|
|
1069
|
+
* config.delegateClass = MyCarPlaySceneDelegate;
|
|
1070
|
+
* return config;
|
|
1071
|
+
* }
|
|
1072
|
+
* // Return null to let NativeScript handle the default window scene
|
|
1073
|
+
* return null;
|
|
1074
|
+
* };
|
|
1075
|
+
* ```
|
|
1076
|
+
*/
|
|
1077
|
+
set onSceneConfiguration(handler) {
|
|
1078
|
+
this._onSceneConfiguration = handler;
|
|
1079
|
+
}
|
|
1080
|
+
get onSceneConfiguration() {
|
|
1081
|
+
return this._onSceneConfiguration;
|
|
1082
|
+
}
|
|
1083
|
+
// Scene management helper methods (kept for backward compat)
|
|
905
1084
|
get sceneDelegate() {
|
|
906
1085
|
if (!this._sceneDelegate) {
|
|
907
1086
|
this._sceneDelegate = SceneDelegate.new();
|
|
@@ -915,10 +1094,12 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
915
1094
|
* Multi-window support
|
|
916
1095
|
*/
|
|
917
1096
|
/**
|
|
918
|
-
* Opens a new window
|
|
919
|
-
*
|
|
1097
|
+
* Opens a new window (scene).
|
|
1098
|
+
*
|
|
1099
|
+
* @param options Options for the new window. `options.data` is serialized into the
|
|
1100
|
+
* activating scene's `NSUserActivity.userInfo`.
|
|
920
1101
|
*/
|
|
921
|
-
openWindow(
|
|
1102
|
+
openWindow(options) {
|
|
922
1103
|
if (!supportsMultipleScenes()) {
|
|
923
1104
|
console.log('Cannot create a new scene - not supported on this device.');
|
|
924
1105
|
return;
|
|
@@ -927,34 +1108,18 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
927
1108
|
const app = UIApplication.sharedApplication;
|
|
928
1109
|
// iOS 17+
|
|
929
1110
|
if (SDK_VERSION >= 17) {
|
|
930
|
-
// Create a new scene activation request with proper role
|
|
931
1111
|
let request;
|
|
932
1112
|
try {
|
|
933
|
-
// Use the correct factory method to create request with role
|
|
934
|
-
// Based on the type definitions, this is the proper way
|
|
935
1113
|
request = UISceneSessionActivationRequest.requestWithRole(UIWindowSceneSessionRoleApplication);
|
|
936
|
-
// Note: may be useful to allow user defined activity type through optional string typed data in future
|
|
937
1114
|
const activity = NSUserActivity.alloc().initWithActivityType(`${NSBundle.mainBundle.bundleIdentifier}.scene`);
|
|
938
|
-
activity.userInfo = dataSerialize(data);
|
|
1115
|
+
activity.userInfo = dataSerialize(options?.data ?? {});
|
|
939
1116
|
request.userActivity = activity;
|
|
940
|
-
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
options
|
|
946
|
-
/**
|
|
947
|
-
* Note: This does not work in testing but worth exploring further sometime
|
|
948
|
-
* regarding the size/dimensions of opened secondary windows.
|
|
949
|
-
* The initial size is ultimately determined by the system
|
|
950
|
-
* based on available space and user context.
|
|
951
|
-
*/
|
|
952
|
-
// Get the size restrictions from the window scene
|
|
953
|
-
// const sizeRestrictions = (options.requestingScene as UIWindowScene).sizeRestrictions;
|
|
954
|
-
// // Set your minimum and maximum dimensions
|
|
955
|
-
// sizeRestrictions.minimumSize = CGSizeMake(320, 400);
|
|
956
|
-
// sizeRestrictions.maximumSize = CGSizeMake(600, 800);
|
|
957
|
-
request.options = options;
|
|
1117
|
+
const activationOptions = UISceneActivationRequestOptions.new();
|
|
1118
|
+
const primary = this.primaryWindow;
|
|
1119
|
+
if (primary?.ios?.scene) {
|
|
1120
|
+
activationOptions.requestingScene = primary.ios.scene;
|
|
1121
|
+
}
|
|
1122
|
+
request.options = activationOptions;
|
|
958
1123
|
}
|
|
959
1124
|
catch (roleError) {
|
|
960
1125
|
console.log('Error creating request:', roleError);
|
|
@@ -963,32 +1128,25 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
963
1128
|
app.activateSceneSessionForRequestErrorHandler(request, (error) => {
|
|
964
1129
|
if (error) {
|
|
965
1130
|
console.log('Error creating new scene (iOS 17+):', error);
|
|
966
|
-
// Log additional debugging info
|
|
967
1131
|
if (error.userInfo) {
|
|
968
1132
|
console.error(`Error userInfo: ${error.userInfo.description}`);
|
|
969
1133
|
}
|
|
970
|
-
// Handle specific error types
|
|
971
1134
|
if (error.localizedDescription.includes('role') && error.localizedDescription.includes('nil')) {
|
|
972
|
-
this.createSceneWithLegacyAPI(data);
|
|
1135
|
+
this.createSceneWithLegacyAPI(options?.data);
|
|
973
1136
|
}
|
|
974
1137
|
else if (error.domain === 'FBSWorkspaceErrorDomain' && error.code === 2) {
|
|
975
|
-
this.createSceneWithLegacyAPI(data);
|
|
1138
|
+
this.createSceneWithLegacyAPI(options?.data);
|
|
976
1139
|
}
|
|
977
1140
|
}
|
|
978
1141
|
});
|
|
979
1142
|
}
|
|
980
|
-
// iOS 13-16 - Use the legacy requestSceneSessionActivationUserActivityOptionsErrorHandler method
|
|
981
1143
|
else if (SDK_VERSION >= 13 && SDK_VERSION < 17) {
|
|
982
|
-
app.requestSceneSessionActivationUserActivityOptionsErrorHandler(null,
|
|
983
|
-
null, // userActivity
|
|
984
|
-
null, // options
|
|
985
|
-
(error) => {
|
|
1144
|
+
app.requestSceneSessionActivationUserActivityOptionsErrorHandler(null, null, null, (error) => {
|
|
986
1145
|
if (error) {
|
|
987
1146
|
console.log('Error creating new scene (legacy):', error);
|
|
988
1147
|
}
|
|
989
1148
|
});
|
|
990
1149
|
}
|
|
991
|
-
// Fallback for older iOS versions or unsupported configurations
|
|
992
1150
|
else {
|
|
993
1151
|
console.log('Neither new nor legacy scene activation methods are available');
|
|
994
1152
|
}
|
|
@@ -999,71 +1157,66 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
999
1157
|
}
|
|
1000
1158
|
/**
|
|
1001
1159
|
* Closes a secondary window/scene.
|
|
1002
|
-
*
|
|
1003
|
-
* - Application.ios.closeWindow() // best-effort close of a non-primary scene
|
|
1004
|
-
* - Application.ios.closeWindow(button) // from a tap handler within the scene
|
|
1005
|
-
* - Application.ios.closeWindow(window)
|
|
1006
|
-
* - Application.ios.closeWindow(scene)
|
|
1007
|
-
* - Application.ios.closeWindow('scene-id')
|
|
1160
|
+
* Accepts a NativeWindow, View, UIWindow, UIWindowScene, or string id.
|
|
1008
1161
|
*/
|
|
1009
1162
|
closeWindow(target) {
|
|
1010
1163
|
if (!__APPLE__) {
|
|
1011
1164
|
return;
|
|
1012
1165
|
}
|
|
1013
1166
|
try {
|
|
1014
|
-
|
|
1015
|
-
if (
|
|
1016
|
-
|
|
1017
|
-
return;
|
|
1167
|
+
let nativeWindow;
|
|
1168
|
+
if (target instanceof NativeWindow) {
|
|
1169
|
+
nativeWindow = target;
|
|
1018
1170
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1171
|
+
else {
|
|
1172
|
+
const scene = this._resolveScene(target);
|
|
1173
|
+
if (scene) {
|
|
1174
|
+
nativeWindow = this._getWindowForScene(scene);
|
|
1175
|
+
}
|
|
1023
1176
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
console.log('closeWindow: Scene has no session to destroy');
|
|
1177
|
+
if (!nativeWindow) {
|
|
1178
|
+
console.log('closeWindow: No window resolved for target');
|
|
1027
1179
|
return;
|
|
1028
1180
|
}
|
|
1029
|
-
|
|
1030
|
-
if (app.requestSceneSessionDestructionOptionsErrorHandler) {
|
|
1031
|
-
app.requestSceneSessionDestructionOptionsErrorHandler(session, null, (error) => {
|
|
1032
|
-
if (error) {
|
|
1033
|
-
console.log('closeWindow: destruction error', error);
|
|
1034
|
-
}
|
|
1035
|
-
else {
|
|
1036
|
-
// clean up tracked id
|
|
1037
|
-
const id = this._getSceneId(scene);
|
|
1038
|
-
this._openedScenesById.delete(id);
|
|
1039
|
-
}
|
|
1040
|
-
});
|
|
1041
|
-
}
|
|
1042
|
-
else {
|
|
1043
|
-
console.info('closeWindow: Scene destruction API not available on this iOS version');
|
|
1044
|
-
}
|
|
1181
|
+
nativeWindow.close();
|
|
1045
1182
|
}
|
|
1046
1183
|
catch (err) {
|
|
1047
1184
|
console.log('closeWindow: Unexpected error', err);
|
|
1048
1185
|
}
|
|
1049
1186
|
}
|
|
1187
|
+
/**
|
|
1188
|
+
* @deprecated Use `getWindows()` instead.
|
|
1189
|
+
*/
|
|
1050
1190
|
getAllWindows() {
|
|
1051
|
-
return
|
|
1191
|
+
return this._windows.map((nw) => nw.ios?.uiWindow).filter(Boolean);
|
|
1052
1192
|
}
|
|
1193
|
+
/**
|
|
1194
|
+
* @deprecated Use `getWindows()` instead.
|
|
1195
|
+
*/
|
|
1053
1196
|
getAllScenes() {
|
|
1054
|
-
return
|
|
1197
|
+
return this._windows.map((nw) => nw.ios?.scene).filter(Boolean);
|
|
1055
1198
|
}
|
|
1199
|
+
/**
|
|
1200
|
+
* @deprecated Use `getWindows()` instead.
|
|
1201
|
+
*/
|
|
1056
1202
|
getWindowScenes() {
|
|
1057
1203
|
return this.getAllScenes().filter((scene) => scene instanceof UIWindowScene);
|
|
1058
1204
|
}
|
|
1205
|
+
/**
|
|
1206
|
+
* @deprecated Use `primaryWindow?.ios?.uiWindow` instead.
|
|
1207
|
+
*/
|
|
1059
1208
|
getPrimaryWindow() {
|
|
1060
|
-
|
|
1061
|
-
|
|
1209
|
+
const primary = this.primaryWindow;
|
|
1210
|
+
if (primary?.ios?.uiWindow) {
|
|
1211
|
+
return primary.ios.uiWindow;
|
|
1062
1212
|
}
|
|
1063
1213
|
return getiOSWindow();
|
|
1064
1214
|
}
|
|
1215
|
+
/**
|
|
1216
|
+
* @deprecated Use `primaryWindow?.ios?.scene` instead.
|
|
1217
|
+
*/
|
|
1065
1218
|
getPrimaryScene() {
|
|
1066
|
-
return this.
|
|
1219
|
+
return this.primaryWindow?.ios?.scene || null;
|
|
1067
1220
|
}
|
|
1068
1221
|
// Scene lifecycle management
|
|
1069
1222
|
supportsScenes() {
|
|
@@ -1073,7 +1226,7 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
1073
1226
|
return supportsMultipleScenes();
|
|
1074
1227
|
}
|
|
1075
1228
|
isUsingSceneLifecycle() {
|
|
1076
|
-
return this.supportsScenes() && this.
|
|
1229
|
+
return this.supportsScenes() && this._windows.length > 0;
|
|
1077
1230
|
}
|
|
1078
1231
|
// Call this to set up scene-based configuration
|
|
1079
1232
|
configureForScenes() {
|
|
@@ -1081,34 +1234,6 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
1081
1234
|
console.warn('Scene-based lifecycle is only supported on iOS 13+ iPad or visionOS with multi-scene enabled apps.');
|
|
1082
1235
|
return;
|
|
1083
1236
|
}
|
|
1084
|
-
// Additional scene configuration can be added here
|
|
1085
|
-
// For now, the notification observers are already set up in the constructor
|
|
1086
|
-
}
|
|
1087
|
-
// Stable scene id for lookups
|
|
1088
|
-
_getSceneId(scene) {
|
|
1089
|
-
try {
|
|
1090
|
-
if (!scene) {
|
|
1091
|
-
return 'Unknown';
|
|
1092
|
-
}
|
|
1093
|
-
// Prefer session persistentIdentifier when available (stable across lifetime)
|
|
1094
|
-
const session = scene.session;
|
|
1095
|
-
const persistentId = session && session.persistentIdentifier;
|
|
1096
|
-
if (persistentId) {
|
|
1097
|
-
return `${persistentId}`;
|
|
1098
|
-
}
|
|
1099
|
-
// Fallbacks
|
|
1100
|
-
if (scene.hash != null) {
|
|
1101
|
-
return `${scene.hash}`;
|
|
1102
|
-
}
|
|
1103
|
-
const desc = scene.description;
|
|
1104
|
-
if (desc) {
|
|
1105
|
-
return `${desc}`;
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
catch (err) {
|
|
1109
|
-
// ignore
|
|
1110
|
-
}
|
|
1111
|
-
return 'Unknown';
|
|
1112
1237
|
}
|
|
1113
1238
|
// Resolve a UIWindowScene from various input types
|
|
1114
1239
|
_resolveScene(target) {
|
|
@@ -1116,12 +1241,10 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
1116
1241
|
return null;
|
|
1117
1242
|
}
|
|
1118
1243
|
if (!target) {
|
|
1119
|
-
// Try to pick a non-primary
|
|
1120
|
-
const
|
|
1121
|
-
|
|
1122
|
-
return nonPrimary[0] || scenes[0] || null;
|
|
1244
|
+
// Try to pick a non-primary window's scene
|
|
1245
|
+
const nonPrimary = this._windows.filter((nw) => !nw.isPrimary);
|
|
1246
|
+
return nonPrimary[0]?.ios?.scene || this.primaryWindow?.ios?.scene || null;
|
|
1123
1247
|
}
|
|
1124
|
-
// If a View was passed, derive its window.scene
|
|
1125
1248
|
if (target && typeof target === 'object') {
|
|
1126
1249
|
// UIWindowScene
|
|
1127
1250
|
if (target.session && target.activationState !== undefined) {
|
|
@@ -1140,15 +1263,15 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
1140
1263
|
}
|
|
1141
1264
|
// String id lookup
|
|
1142
1265
|
if (typeof target === 'string') {
|
|
1143
|
-
|
|
1144
|
-
|
|
1266
|
+
const found = this.getWindowById(target);
|
|
1267
|
+
if (found) {
|
|
1268
|
+
return found.ios?.scene || null;
|
|
1145
1269
|
}
|
|
1146
|
-
// Try matching
|
|
1147
|
-
const
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
return s;
|
|
1270
|
+
// Try matching among known scenes
|
|
1271
|
+
for (const nw of this._windows) {
|
|
1272
|
+
const scene = nw.ios?.scene;
|
|
1273
|
+
if (scene && IOSNativeWindow.getSceneId(scene) === target) {
|
|
1274
|
+
return scene;
|
|
1152
1275
|
}
|
|
1153
1276
|
}
|
|
1154
1277
|
}
|
|
@@ -1161,7 +1284,7 @@ export class iOSApplication extends ApplicationCommon {
|
|
|
1161
1284
|
}
|
|
1162
1285
|
// Create user activity for the new scene
|
|
1163
1286
|
const userActivity = NSUserActivity.alloc().initWithActivityType(`${NSBundle.mainBundle.bundleIdentifier}.scene`);
|
|
1164
|
-
userActivity.userInfo = dataSerialize(data);
|
|
1287
|
+
userActivity.userInfo = dataSerialize(data ?? {});
|
|
1165
1288
|
// Use the legacy API
|
|
1166
1289
|
const options = UISceneActivationRequestOptions.new();
|
|
1167
1290
|
options.requestingScene = windowScene;
|