@nativescript/core 9.0.0-alpha.13 → 9.0.0-alpha.13-next-11-05-2025-19118999729

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.
Files changed (179) hide show
  1. package/application/application-common.d.ts +10 -4
  2. package/application/application-common.js +10 -0
  3. package/application/application-common.js.map +1 -1
  4. package/application/application-interfaces.d.ts +21 -0
  5. package/application/application.android.d.ts +9 -2
  6. package/application/application.android.js +83 -6
  7. package/application/application.android.js.map +1 -1
  8. package/application/application.d.ts +74 -1
  9. package/application/application.ios.d.ts +65 -1
  10. package/application/application.ios.js +612 -13
  11. package/application/application.ios.js.map +1 -1
  12. package/application/helpers.android.d.ts +0 -9
  13. package/application/helpers.android.js +0 -54
  14. package/application/helpers.android.js.map +1 -1
  15. package/application/helpers.d.ts +0 -10
  16. package/application/helpers.ios.d.ts +0 -19
  17. package/application/helpers.ios.js +0 -38
  18. package/application/helpers.ios.js.map +1 -1
  19. package/connectivity/index.android.js +3 -3
  20. package/connectivity/index.android.js.map +1 -1
  21. package/core-types/index.d.ts +69 -63
  22. package/core-types/index.js +1 -0
  23. package/core-types/index.js.map +1 -1
  24. package/globals/index.js +1 -0
  25. package/globals/index.js.map +1 -1
  26. package/image-asset/image-asset-common.js +31 -3
  27. package/image-asset/image-asset-common.js.map +1 -1
  28. package/image-source/index.android.js +4 -3
  29. package/image-source/index.android.js.map +1 -1
  30. package/image-source/index.d.ts +2 -2
  31. package/index.d.ts +1 -0
  32. package/index.js +0 -1
  33. package/index.js.map +1 -1
  34. package/inspector_modules.js +168 -55
  35. package/inspector_modules.js.map +1 -1
  36. package/package.json +1 -1
  37. package/platforms/android/widgets-release.aar +0 -0
  38. package/references.d.ts +1 -1
  39. package/timer/index.android.js +15 -7
  40. package/timer/index.android.js.map +1 -1
  41. package/ui/animation/index.d.ts +14 -1
  42. package/ui/button/index.android.js +2 -0
  43. package/ui/button/index.android.js.map +1 -1
  44. package/ui/button/index.d.ts +1 -1
  45. package/ui/button/index.ios.js.map +1 -1
  46. package/ui/core/properties/index.js +64 -52
  47. package/ui/core/properties/index.js.map +1 -1
  48. package/ui/core/view/index.android.d.ts +17 -2
  49. package/ui/core/view/index.android.js +335 -16
  50. package/ui/core/view/index.android.js.map +1 -1
  51. package/ui/core/view/index.d.ts +29 -1
  52. package/ui/core/view/index.ios.d.ts +10 -1
  53. package/ui/core/view/index.ios.js +109 -40
  54. package/ui/core/view/index.ios.js.map +1 -1
  55. package/ui/core/view/view-common.d.ts +52 -5
  56. package/ui/core/view/view-common.js +53 -1
  57. package/ui/core/view/view-common.js.map +1 -1
  58. package/ui/core/view/view-helper/index.ios.d.ts +1 -0
  59. package/ui/core/view/view-helper/index.ios.js +20 -0
  60. package/ui/core/view/view-helper/index.ios.js.map +1 -1
  61. package/ui/core/view/view-interfaces.d.ts +22 -0
  62. package/ui/core/view-base/index.d.ts +4 -0
  63. package/ui/core/view-base/index.js.map +1 -1
  64. package/ui/dialogs/index.ios.js +4 -2
  65. package/ui/dialogs/index.ios.js.map +1 -1
  66. package/ui/editable-text-base/editable-text-base-common.js +1 -1
  67. package/ui/editable-text-base/editable-text-base-common.js.map +1 -1
  68. package/ui/editable-text-base/index.android.d.ts +1 -1
  69. package/ui/editable-text-base/index.android.js +3 -0
  70. package/ui/editable-text-base/index.android.js.map +1 -1
  71. package/ui/editable-text-base/index.ios.d.ts +1 -1
  72. package/ui/editable-text-base/index.ios.js +3 -0
  73. package/ui/editable-text-base/index.ios.js.map +1 -1
  74. package/ui/frame/activity.android.js +3 -0
  75. package/ui/frame/activity.android.js.map +1 -1
  76. package/ui/frame/index.android.d.ts +2 -0
  77. package/ui/frame/index.android.js +55 -0
  78. package/ui/frame/index.android.js.map +1 -1
  79. package/ui/frame/index.d.ts +1 -1
  80. package/ui/frame/index.ios.d.ts +1 -0
  81. package/ui/frame/index.ios.js +8 -0
  82. package/ui/frame/index.ios.js.map +1 -1
  83. package/ui/index.d.ts +9 -4
  84. package/ui/index.js +3 -1
  85. package/ui/index.js.map +1 -1
  86. package/ui/layouts/index.d.ts +5 -1
  87. package/ui/layouts/index.js +2 -0
  88. package/ui/layouts/index.js.map +1 -1
  89. package/ui/layouts/liquid-glass/index.android.d.ts +5 -0
  90. package/ui/layouts/liquid-glass/index.android.js +8 -0
  91. package/ui/layouts/liquid-glass/index.android.js.map +1 -0
  92. package/ui/layouts/liquid-glass/index.d.ts +10 -0
  93. package/ui/layouts/liquid-glass/index.ios.d.ts +10 -0
  94. package/ui/layouts/liquid-glass/index.ios.js +59 -0
  95. package/ui/layouts/liquid-glass/index.ios.js.map +1 -0
  96. package/ui/layouts/liquid-glass/liquid-glass-common.d.ts +3 -0
  97. package/ui/layouts/liquid-glass/liquid-glass-common.js +4 -0
  98. package/ui/layouts/liquid-glass/liquid-glass-common.js.map +1 -0
  99. package/ui/layouts/liquid-glass-container/index.android.d.ts +3 -0
  100. package/ui/layouts/liquid-glass-container/index.android.js +4 -0
  101. package/ui/layouts/liquid-glass-container/index.android.js.map +1 -0
  102. package/ui/layouts/liquid-glass-container/index.d.ts +3 -0
  103. package/ui/layouts/liquid-glass-container/index.ios.d.ts +14 -0
  104. package/ui/layouts/liquid-glass-container/index.ios.js +121 -0
  105. package/ui/layouts/liquid-glass-container/index.ios.js.map +1 -0
  106. package/ui/layouts/liquid-glass-container/liquid-glass-container-common.d.ts +4 -0
  107. package/ui/layouts/liquid-glass-container/liquid-glass-container-common.js +5 -0
  108. package/ui/layouts/liquid-glass-container/liquid-glass-container-common.js.map +1 -0
  109. package/ui/list-view/index.android.d.ts +26 -1
  110. package/ui/list-view/index.android.js +701 -23
  111. package/ui/list-view/index.android.js.map +1 -1
  112. package/ui/list-view/index.d.ts +122 -0
  113. package/ui/list-view/index.ios.d.ts +34 -2
  114. package/ui/list-view/index.ios.js +560 -9
  115. package/ui/list-view/index.ios.js.map +1 -1
  116. package/ui/list-view/list-view-common.d.ts +22 -1
  117. package/ui/list-view/list-view-common.js +85 -0
  118. package/ui/list-view/list-view-common.js.map +1 -1
  119. package/ui/page/index.android.d.ts +1 -10
  120. package/ui/page/index.android.js +1 -45
  121. package/ui/page/index.android.js.map +1 -1
  122. package/ui/page/index.d.ts +0 -8
  123. package/ui/page/index.ios.d.ts +1 -3
  124. package/ui/page/index.ios.js +7 -17
  125. package/ui/page/index.ios.js.map +1 -1
  126. package/ui/page/page-common.d.ts +0 -6
  127. package/ui/page/page-common.js +0 -14
  128. package/ui/page/page-common.js.map +1 -1
  129. package/ui/search-bar/index.android.d.ts +2 -1
  130. package/ui/search-bar/index.android.js +20 -1
  131. package/ui/search-bar/index.android.js.map +1 -1
  132. package/ui/search-bar/index.d.ts +7 -0
  133. package/ui/search-bar/index.ios.d.ts +2 -1
  134. package/ui/search-bar/index.ios.js +11 -1
  135. package/ui/search-bar/index.ios.js.map +1 -1
  136. package/ui/search-bar/search-bar-common.d.ts +2 -0
  137. package/ui/search-bar/search-bar-common.js +7 -0
  138. package/ui/search-bar/search-bar-common.js.map +1 -1
  139. package/ui/slider/index.d.ts +1 -1
  140. package/ui/styling/background-common.d.ts +4 -4
  141. package/ui/styling/background-common.js +8 -8
  142. package/ui/styling/background-common.js.map +1 -1
  143. package/ui/styling/background.d.ts +0 -3
  144. package/ui/styling/background.ios.d.ts +2 -1
  145. package/ui/styling/background.ios.js +47 -38
  146. package/ui/styling/background.ios.js.map +1 -1
  147. package/ui/styling/css-stroke.js.map +1 -1
  148. package/ui/styling/css-utils.d.ts +1 -0
  149. package/ui/styling/css-utils.js +15 -4
  150. package/ui/styling/css-utils.js.map +1 -1
  151. package/ui/styling/style/index.d.ts +2 -1
  152. package/ui/styling/style/index.js.map +1 -1
  153. package/ui/styling/style-properties.js +23 -11
  154. package/ui/styling/style-properties.js.map +1 -1
  155. package/ui/tab-view/index.android.js +22 -8
  156. package/ui/tab-view/index.android.js.map +1 -1
  157. package/ui/tab-view/index.ios.d.ts +1 -1
  158. package/ui/tab-view/index.ios.js +28 -9
  159. package/ui/tab-view/index.ios.js.map +1 -1
  160. package/ui/tab-view/tab-view-common.d.ts +2 -0
  161. package/ui/tab-view/tab-view-common.js +5 -0
  162. package/ui/tab-view/tab-view-common.js.map +1 -1
  163. package/ui/text-base/index.android.js +3 -0
  164. package/ui/text-base/index.android.js.map +1 -1
  165. package/ui/text-field/index.android.js +3 -0
  166. package/ui/text-field/index.android.js.map +1 -1
  167. package/ui/transition/page-transition.android.js +15 -1
  168. package/ui/transition/page-transition.android.js.map +1 -1
  169. package/utils/common.d.ts +3 -1
  170. package/utils/common.js +9 -3
  171. package/utils/common.js.map +1 -1
  172. package/utils/index.d.ts +6 -0
  173. package/utils/native-helper-for-android.d.ts +22 -0
  174. package/utils/native-helper-for-android.js +103 -0
  175. package/utils/native-helper-for-android.js.map +1 -1
  176. package/utils/native-helper.android.d.ts +9 -1
  177. package/utils/native-helper.android.js +5 -1
  178. package/utils/native-helper.android.js.map +1 -1
  179. package/utils/native-helper.d.ts +30 -0
@@ -3,13 +3,30 @@ 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 } from '../utils/native-helper';
7
- import { ApplicationCommon, initializeSdkVersionClass } from './application-common';
6
+ import { ios as iosUtils, dataSerialize } from '../utils/native-helper';
7
+ import { ApplicationCommon, initializeSdkVersionClass, SceneEvents } from './application-common';
8
8
  import { Observable } from '../data/observable';
9
9
  import { Trace } from '../trace';
10
10
  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
- import { iosAddNotificationObserver, iosRemoveNotificationObserver } from './helpers';
12
11
  import { getiOSWindow, setA11yUpdatePropertiesCallback, setApplicationPropertiesCallback, setAppMainEntry, setiOSWindow, setRootView, setToggleApplicationEventListenersCallback } from './helpers-common';
12
+ var NotificationObserver = /** @class */ (function (_super) {
13
+ __extends(NotificationObserver, _super);
14
+ function NotificationObserver() {
15
+ return _super !== null && _super.apply(this, arguments) || this;
16
+ }
17
+ NotificationObserver.initWithCallback = function (onReceiveCallback) {
18
+ var observer = _super.new.call(this);
19
+ observer._onReceiveCallback = onReceiveCallback;
20
+ return observer;
21
+ };
22
+ NotificationObserver.prototype.onReceive = function (notification) {
23
+ this._onReceiveCallback(notification);
24
+ };
25
+ NotificationObserver.ObjCExposedMethods = {
26
+ onReceive: { returns: interop.types.void, params: [NSNotification] },
27
+ };
28
+ return NotificationObserver;
29
+ }(NSObject));
13
30
  var CADisplayLinkTarget = /** @class */ (function (_super) {
14
31
  __extends(CADisplayLinkTarget, _super);
15
32
  function CADisplayLinkTarget() {
@@ -40,6 +57,28 @@ var CADisplayLinkTarget = /** @class */ (function (_super) {
40
57
  };
41
58
  return CADisplayLinkTarget;
42
59
  }(NSObject));
60
+ /**
61
+ * Detect if the app supports scenes.
62
+ * When an app configures UIApplicationSceneManifest in Info.plist
63
+ * it will use scene lifecycle management.
64
+ */
65
+ let sceneManifest;
66
+ function supportsScenes() {
67
+ if (SDK_VERSION < 13) {
68
+ return false;
69
+ }
70
+ if (typeof sceneManifest === 'undefined') {
71
+ // Check if scene manifest exists in Info.plist
72
+ sceneManifest = NSBundle.mainBundle.objectForInfoDictionaryKey('UIApplicationSceneManifest');
73
+ }
74
+ return !!sceneManifest;
75
+ }
76
+ function supportsMultipleScenes() {
77
+ if (SDK_VERSION < 13) {
78
+ return false;
79
+ }
80
+ return UIApplication.sharedApplication?.supportsMultipleScenes;
81
+ }
43
82
  var Responder = /** @class */ (function (_super) {
44
83
  __extends(Responder, _super);
45
84
  function Responder() {
@@ -58,6 +97,109 @@ var Responder = /** @class */ (function (_super) {
58
97
  Responder.ObjCProtocols = [UIApplicationDelegate];
59
98
  return Responder;
60
99
  }(UIResponder));
100
+ if (supportsScenes()) {
101
+ /**
102
+ * This method is called when a new scene session is being created.
103
+ * Important: When this method is implemented, the app assumes scene-based lifecycle management.
104
+ * Detected by the Info.plist existence 'UIApplicationSceneManifest'.
105
+ * If this method is implemented when there is no manifest defined,
106
+ * the app will boot to a white screen.
107
+ */
108
+ Responder.prototype.applicationConfigurationForConnectingSceneSessionOptions = function (application, connectingSceneSession, options) {
109
+ const config = UISceneConfiguration.configurationWithNameSessionRole('Default Configuration', connectingSceneSession.role);
110
+ config.sceneClass = UIWindowScene;
111
+ config.delegateClass = SceneDelegate;
112
+ return config;
113
+ };
114
+ // scene session destruction handling
115
+ Responder.prototype.applicationDidDiscardSceneSessions = function (application, sceneSessions) {
116
+ // Note: we could emit an event here if needed
117
+ // console.log('Scene sessions discarded:', sceneSessions.count);
118
+ };
119
+ }
120
+ var SceneDelegate = /** @class */ (function (_super) {
121
+ __extends(SceneDelegate, _super);
122
+ function SceneDelegate() {
123
+ return _super !== null && _super.apply(this, arguments) || this;
124
+ }
125
+ Object.defineProperty(SceneDelegate.prototype, "window", {
126
+ get: function () {
127
+ return this._window;
128
+ },
129
+ set: function (value) {
130
+ this._window = value;
131
+ },
132
+ enumerable: true,
133
+ configurable: true
134
+ });
135
+ SceneDelegate.prototype.sceneWillConnectToSessionOptions = function (scene, session, connectionOptions) {
136
+ if (Trace.isEnabled()) {
137
+ Trace.write("SceneDelegate.sceneWillConnectToSessionOptions called with role: ".concat(session.role), Trace.categories.NativeLifecycle);
138
+ }
139
+ if (!(scene instanceof UIWindowScene)) {
140
+ // Scene is not a UIWindowScene, ignoring
141
+ return;
142
+ }
143
+ this._scene = scene;
144
+ // Create window for this scene
145
+ this._window = UIWindow.alloc().initWithWindowScene(scene);
146
+ // Store the window scene for this window
147
+ Application.ios._setWindowForScene(this._window, scene);
148
+ // Set up the window content
149
+ Application.ios._setupWindowForScene(this._window, scene);
150
+ // Notify that scene will connect
151
+ Application.ios.notify({
152
+ eventName: SceneEvents.sceneWillConnect,
153
+ object: Application.ios,
154
+ scene: scene,
155
+ window: this._window,
156
+ connectionOptions: connectionOptions,
157
+ });
158
+ if (scene === Application.ios.getPrimaryScene()) {
159
+ // primary scene, activate right away
160
+ this._window.makeKeyAndVisible();
161
+ }
162
+ else {
163
+ // For secondary scenes, emit an event to allow developers to set up custom content for the window
164
+ Application.ios.notify({
165
+ eventName: SceneEvents.sceneContentSetup,
166
+ object: Application.ios,
167
+ scene: scene,
168
+ window: this._window,
169
+ connectionOptions: connectionOptions,
170
+ });
171
+ }
172
+ // If this is the first scene, trigger app startup
173
+ if (!Application.ios.getPrimaryScene()) {
174
+ Application.ios._notifySceneAppStarted();
175
+ }
176
+ };
177
+ SceneDelegate.prototype.sceneDidBecomeActive = function (scene) {
178
+ // This will be handled by the notification observer in iOSApplication
179
+ // The notification system will automatically trigger sceneDidActivate
180
+ };
181
+ SceneDelegate.prototype.sceneWillResignActive = function (scene) {
182
+ // Notify that scene will resign active
183
+ Application.ios.notify({
184
+ eventName: SceneEvents.sceneWillResignActive,
185
+ object: Application.ios,
186
+ scene: scene,
187
+ });
188
+ };
189
+ SceneDelegate.prototype.sceneWillEnterForeground = function (scene) {
190
+ // This will be handled by the notification observer in iOSApplication
191
+ };
192
+ SceneDelegate.prototype.sceneDidEnterBackground = function (scene) {
193
+ // This will be handled by the notification observer in iOSApplication
194
+ };
195
+ SceneDelegate.prototype.sceneDidDisconnect = function (scene) {
196
+ // This will be handled by the notification observer in iOSApplication
197
+ };
198
+ SceneDelegate.ObjCProtocols = [UIWindowSceneDelegate];
199
+ return SceneDelegate;
200
+ }(UIResponder));
201
+ // ensure available globally
202
+ global.SceneDelegate = SceneDelegate;
61
203
  export class iOSApplication extends ApplicationCommon {
62
204
  /**
63
205
  * @internal - should not be constructed by the user.
@@ -65,6 +207,10 @@ export class iOSApplication extends ApplicationCommon {
65
207
  constructor() {
66
208
  super();
67
209
  this._delegateHandlers = new Map();
210
+ this._windowSceneMap = new Map();
211
+ this._primaryScene = null;
212
+ this._openedScenesById = new Map();
213
+ this._notificationObservers = [];
68
214
  this.displayedOnce = false;
69
215
  this.addNotificationObserver(UIApplicationDidFinishLaunchingNotification, this.didFinishLaunchingWithOptions.bind(this));
70
216
  this.addNotificationObserver(UIApplicationDidBecomeActiveNotification, this.didBecomeActive.bind(this));
@@ -72,6 +218,14 @@ export class iOSApplication extends ApplicationCommon {
72
218
  this.addNotificationObserver(UIApplicationWillTerminateNotification, this.willTerminate.bind(this));
73
219
  this.addNotificationObserver(UIApplicationDidReceiveMemoryWarningNotification, this.didReceiveMemoryWarning.bind(this));
74
220
  this.addNotificationObserver(UIApplicationDidChangeStatusBarOrientationNotification, this.didChangeStatusBarOrientation.bind(this));
221
+ // Add scene lifecycle notification observers only if scenes are supported
222
+ if (this.supportsScenes()) {
223
+ this.addNotificationObserver('UISceneWillConnectNotification', this.sceneWillConnect.bind(this));
224
+ this.addNotificationObserver('UISceneDidActivateNotification', this.sceneDidActivate.bind(this));
225
+ this.addNotificationObserver('UISceneWillEnterForegroundNotification', this.sceneWillEnterForeground.bind(this));
226
+ this.addNotificationObserver('UISceneDidEnterBackgroundNotification', this.sceneDidEnterBackground.bind(this));
227
+ this.addNotificationObserver('UISceneDidDisconnectNotification', this.sceneDidDisconnect.bind(this));
228
+ }
75
229
  }
76
230
  getRootView() {
77
231
  return this._rootView;
@@ -246,10 +400,17 @@ export class iOSApplication extends ApplicationCommon {
246
400
  return this.nativeApp;
247
401
  }
248
402
  addNotificationObserver(notificationName, onReceiveCallback) {
249
- return iosAddNotificationObserver(notificationName, onReceiveCallback);
403
+ const observer = NotificationObserver.initWithCallback(onReceiveCallback);
404
+ NSNotificationCenter.defaultCenter.addObserverSelectorNameObject(observer, 'onReceive', notificationName, null);
405
+ this._notificationObservers.push(observer);
406
+ return observer;
250
407
  }
251
408
  removeNotificationObserver(observer /* NotificationObserver */, notificationName) {
252
- iosRemoveNotificationObserver(observer, notificationName);
409
+ const index = this._notificationObservers.indexOf(observer);
410
+ if (index >= 0) {
411
+ this._notificationObservers.splice(index, 1);
412
+ NSNotificationCenter.defaultCenter.removeObserverNameObject(observer, notificationName, null);
413
+ }
253
414
  }
254
415
  getSystemAppearance() {
255
416
  // userInterfaceStyle is available on UITraitCollection since iOS 12.
@@ -303,6 +464,10 @@ export class iOSApplication extends ApplicationCommon {
303
464
  setiOSWindow(this.window);
304
465
  }
305
466
  }
467
+ // Public method for scene-based app startup
468
+ _notifySceneAppStarted() {
469
+ this.notifyAppStarted();
470
+ }
306
471
  _onLivesync(context) {
307
472
  // Handle application root module
308
473
  const isAppRootModuleChanged = context && context.path && context.path.includes(this.getMainEntry().moduleName) && context.type !== 'style';
@@ -358,16 +523,23 @@ export class iOSApplication extends ApplicationCommon {
358
523
  }
359
524
  }
360
525
  this.setMaxRefreshRate();
361
- // ensures window is assigned to proper window scene
362
- setiOSWindow(this.window);
363
- if (!getiOSWindow()) {
364
- // if still no window, create one
365
- setiOSWindow(UIWindow.alloc().initWithFrame(UIScreen.mainScreen.bounds));
526
+ // Only set up window if NOT using scene-based lifecycle
527
+ if (!this.supportsScenes()) {
528
+ // Traditional single-window app setup
529
+ // ensures window is assigned to proper window scene
530
+ setiOSWindow(this.window);
531
+ if (!getiOSWindow()) {
532
+ // if still no window, create one
533
+ setiOSWindow(UIWindow.alloc().initWithFrame(UIScreen.mainScreen.bounds));
534
+ }
535
+ if (!__VISIONOS__) {
536
+ this.window.backgroundColor = SDK_VERSION <= 12 || !UIColor.systemBackgroundColor ? UIColor.whiteColor : UIColor.systemBackgroundColor;
537
+ }
538
+ this.notifyAppStarted(notification);
366
539
  }
367
- if (!__VISIONOS__) {
368
- this.window.backgroundColor = SDK_VERSION <= 12 || !UIColor.systemBackgroundColor ? UIColor.whiteColor : UIColor.systemBackgroundColor;
540
+ else {
541
+ // Scene-based app - window creation will happen in scene delegate
369
542
  }
370
- this.notifyAppStarted(notification);
371
543
  }
372
544
  didBecomeActive(notification) {
373
545
  const additionalData = {
@@ -414,6 +586,431 @@ export class iOSApplication extends ApplicationCommon {
414
586
  const newOrientation = this.getOrientationValue(statusBarOrientation);
415
587
  this.setOrientation(newOrientation);
416
588
  }
589
+ // Scene lifecycle notification handlers
590
+ sceneWillConnect(notification) {
591
+ const scene = notification.object;
592
+ if (!scene || !(scene instanceof UIWindowScene)) {
593
+ return;
594
+ }
595
+ // Store as primary scene if it's the first one
596
+ if (!this._primaryScene) {
597
+ this._primaryScene = scene;
598
+ }
599
+ this.notify({
600
+ eventName: SceneEvents.sceneWillConnect,
601
+ object: this,
602
+ scene: scene,
603
+ userInfo: notification.userInfo,
604
+ });
605
+ }
606
+ sceneDidActivate(notification) {
607
+ const scene = notification.object;
608
+ this.notify({
609
+ eventName: SceneEvents.sceneDidActivate,
610
+ object: this,
611
+ scene: scene,
612
+ });
613
+ // If this is the primary scene, trigger traditional app lifecycle
614
+ if (scene === this._primaryScene) {
615
+ const additionalData = {
616
+ ios: UIApplication.sharedApplication,
617
+ scene: scene,
618
+ };
619
+ this.setInBackground(false, additionalData);
620
+ this.setSuspended(false, additionalData);
621
+ if (this._rootView && !this._rootView.isLoaded) {
622
+ this._rootView.callLoaded();
623
+ }
624
+ }
625
+ }
626
+ sceneWillEnterForeground(notification) {
627
+ const scene = notification.object;
628
+ this.notify({
629
+ eventName: SceneEvents.sceneWillEnterForeground,
630
+ object: this,
631
+ scene: scene,
632
+ });
633
+ }
634
+ sceneDidEnterBackground(notification) {
635
+ const scene = notification.object;
636
+ this.notify({
637
+ eventName: SceneEvents.sceneDidEnterBackground,
638
+ object: this,
639
+ scene: scene,
640
+ });
641
+ // If this is the primary scene, trigger traditional app lifecycle
642
+ if (scene === this._primaryScene) {
643
+ const additionalData = {
644
+ ios: UIApplication.sharedApplication,
645
+ scene: scene,
646
+ };
647
+ this.setInBackground(true, additionalData);
648
+ this.setSuspended(true, additionalData);
649
+ if (this._rootView && this._rootView.isLoaded) {
650
+ this._rootView.callUnloaded();
651
+ }
652
+ }
653
+ }
654
+ sceneDidDisconnect(notification) {
655
+ const scene = notification.object;
656
+ this._removeWindowForScene(scene);
657
+ // If primary scene disconnected, clear it
658
+ if (scene === this._primaryScene) {
659
+ this._primaryScene = null;
660
+ }
661
+ if (this._primaryScene) {
662
+ if (SDK_VERSION >= 17) {
663
+ const request = UISceneSessionActivationRequest.requestWithSession(this._primaryScene.session);
664
+ UIApplication.sharedApplication.activateSceneSessionForRequestErrorHandler(request, (err) => {
665
+ if (err) {
666
+ console.log('Failed to activate primary scene:', err.localizedDescription);
667
+ }
668
+ });
669
+ }
670
+ else {
671
+ UIApplication.sharedApplication.requestSceneSessionActivationUserActivityOptionsErrorHandler(this._primaryScene.session, null, null, (err) => {
672
+ if (err) {
673
+ console.log('Failed to activate primary scene (legacy):', err.localizedDescription);
674
+ }
675
+ });
676
+ }
677
+ }
678
+ this.notify({
679
+ eventName: SceneEvents.sceneDidDisconnect,
680
+ object: this,
681
+ scene: scene,
682
+ });
683
+ }
684
+ // Scene management helper methods
685
+ _setWindowForScene(window, scene) {
686
+ this._windowSceneMap.set(scene, window);
687
+ }
688
+ _removeWindowForScene(scene) {
689
+ this._windowSceneMap.delete(scene);
690
+ // also untrack opened scene id
691
+ try {
692
+ const s = scene;
693
+ if (s && s.session) {
694
+ const id = this._getSceneId(s);
695
+ this._openedScenesById.delete(id);
696
+ }
697
+ }
698
+ catch { }
699
+ }
700
+ _getWindowForScene(scene) {
701
+ return this._windowSceneMap.get(scene);
702
+ }
703
+ _setupWindowForScene(window, scene) {
704
+ if (!window) {
705
+ return;
706
+ }
707
+ // track opened scene
708
+ try {
709
+ const id = this._getSceneId(scene);
710
+ this._openedScenesById.set(id, scene);
711
+ }
712
+ catch { }
713
+ // Set up window background
714
+ if (!__VISIONOS__) {
715
+ window.backgroundColor = SDK_VERSION <= 12 || !UIColor.systemBackgroundColor ? UIColor.whiteColor : UIColor.systemBackgroundColor;
716
+ }
717
+ // If this is the primary scene, set up the main application content
718
+ if (scene === this._primaryScene || !this._primaryScene) {
719
+ this._primaryScene = scene;
720
+ if (!getiOSWindow()) {
721
+ setiOSWindow(window);
722
+ }
723
+ // Set up the window content for the primary scene
724
+ this.setWindowContent();
725
+ }
726
+ }
727
+ get sceneDelegate() {
728
+ if (!this._sceneDelegate) {
729
+ this._sceneDelegate = SceneDelegate.new();
730
+ }
731
+ return this._sceneDelegate;
732
+ }
733
+ set sceneDelegate(value) {
734
+ this._sceneDelegate = value;
735
+ }
736
+ /**
737
+ * Multi-window support
738
+ */
739
+ /**
740
+ * Opens a new window with the specified data.
741
+ * @param data The data to pass to the new window.
742
+ */
743
+ openWindow(data) {
744
+ if (!supportsMultipleScenes()) {
745
+ console.log('Cannot create a new scene - not supported on this device.');
746
+ return;
747
+ }
748
+ try {
749
+ const app = UIApplication.sharedApplication;
750
+ // iOS 17+
751
+ if (SDK_VERSION >= 17) {
752
+ // Create a new scene activation request with proper role
753
+ let request;
754
+ try {
755
+ // Use the correct factory method to create request with role
756
+ // Based on the type definitions, this is the proper way
757
+ request = UISceneSessionActivationRequest.requestWithRole(UIWindowSceneSessionRoleApplication);
758
+ // Note: may be useful to allow user defined activity type through optional string typed data in future
759
+ const activity = NSUserActivity.alloc().initWithActivityType(`${NSBundle.mainBundle.bundleIdentifier}.scene`);
760
+ activity.userInfo = dataSerialize(data);
761
+ request.userActivity = activity;
762
+ // Set proper options with requesting scene
763
+ const options = UISceneActivationRequestOptions.new();
764
+ // Note: explore secondary windows spawning other windows
765
+ // and if this context needs to change in those cases
766
+ const mainWindow = Application.ios.getPrimaryWindow();
767
+ options.requestingScene = mainWindow?.windowScene;
768
+ /**
769
+ * Note: This does not work in testing but worth exploring further sometime
770
+ * regarding the size/dimensions of opened secondary windows.
771
+ * The initial size is ultimately determined by the system
772
+ * based on available space and user context.
773
+ */
774
+ // Get the size restrictions from the window scene
775
+ // const sizeRestrictions = (options.requestingScene as UIWindowScene).sizeRestrictions;
776
+ // // Set your minimum and maximum dimensions
777
+ // sizeRestrictions.minimumSize = CGSizeMake(320, 400);
778
+ // sizeRestrictions.maximumSize = CGSizeMake(600, 800);
779
+ request.options = options;
780
+ }
781
+ catch (roleError) {
782
+ console.log('Error creating request:', roleError);
783
+ return;
784
+ }
785
+ app.activateSceneSessionForRequestErrorHandler(request, (error) => {
786
+ if (error) {
787
+ console.log('Error creating new scene (iOS 17+):', error);
788
+ // Log additional debugging info
789
+ if (error.userInfo) {
790
+ console.error(`Error userInfo: ${error.userInfo.description}`);
791
+ }
792
+ // Handle specific error types
793
+ if (error.localizedDescription.includes('role') && error.localizedDescription.includes('nil')) {
794
+ this.createSceneWithLegacyAPI(data);
795
+ }
796
+ else if (error.domain === 'FBSWorkspaceErrorDomain' && error.code === 2) {
797
+ this.createSceneWithLegacyAPI(data);
798
+ }
799
+ }
800
+ });
801
+ }
802
+ // iOS 13-16 - Use the legacy requestSceneSessionActivationUserActivityOptionsErrorHandler method
803
+ else if (SDK_VERSION >= 13 && SDK_VERSION < 17) {
804
+ app.requestSceneSessionActivationUserActivityOptionsErrorHandler(null, // session
805
+ null, // userActivity
806
+ null, // options
807
+ (error) => {
808
+ if (error) {
809
+ console.log('Error creating new scene (legacy):', error);
810
+ }
811
+ });
812
+ }
813
+ // Fallback for older iOS versions or unsupported configurations
814
+ else {
815
+ console.log('Neither new nor legacy scene activation methods are available');
816
+ }
817
+ }
818
+ catch (error) {
819
+ console.error('Error requesting new scene:', error);
820
+ }
821
+ }
822
+ /**
823
+ * Closes a secondary window/scene.
824
+ * Usage examples:
825
+ * - Application.ios.closeWindow() // best-effort close of a non-primary scene
826
+ * - Application.ios.closeWindow(button) // from a tap handler within the scene
827
+ * - Application.ios.closeWindow(window)
828
+ * - Application.ios.closeWindow(scene)
829
+ * - Application.ios.closeWindow('scene-id')
830
+ */
831
+ closeWindow(target) {
832
+ if (!__APPLE__) {
833
+ return;
834
+ }
835
+ try {
836
+ const scene = this._resolveScene(target);
837
+ if (!scene) {
838
+ console.log('closeWindow: No scene resolved for target');
839
+ return;
840
+ }
841
+ // Don't allow closing the primary scene
842
+ if (scene === this._primaryScene) {
843
+ console.log('closeWindow: Refusing to close the primary scene');
844
+ return;
845
+ }
846
+ const session = scene.session;
847
+ if (!session) {
848
+ console.log('closeWindow: Scene has no session to destroy');
849
+ return;
850
+ }
851
+ const app = UIApplication.sharedApplication;
852
+ if (app.requestSceneSessionDestructionOptionsErrorHandler) {
853
+ app.requestSceneSessionDestructionOptionsErrorHandler(session, null, (error) => {
854
+ if (error) {
855
+ console.log('closeWindow: destruction error', error);
856
+ }
857
+ else {
858
+ // clean up tracked id
859
+ const id = this._getSceneId(scene);
860
+ this._openedScenesById.delete(id);
861
+ }
862
+ });
863
+ }
864
+ else {
865
+ console.info('closeWindow: Scene destruction API not available on this iOS version');
866
+ }
867
+ }
868
+ catch (err) {
869
+ console.log('closeWindow: Unexpected error', err);
870
+ }
871
+ }
872
+ getAllWindows() {
873
+ return Array.from(this._windowSceneMap.values());
874
+ }
875
+ getAllScenes() {
876
+ return Array.from(this._windowSceneMap.keys());
877
+ }
878
+ getWindowScenes() {
879
+ return this.getAllScenes().filter((scene) => scene instanceof UIWindowScene);
880
+ }
881
+ getPrimaryWindow() {
882
+ if (this._primaryScene) {
883
+ return this._getWindowForScene(this._primaryScene) || getiOSWindow();
884
+ }
885
+ return getiOSWindow();
886
+ }
887
+ getPrimaryScene() {
888
+ return this._primaryScene;
889
+ }
890
+ // Scene lifecycle management
891
+ supportsScenes() {
892
+ return supportsScenes();
893
+ }
894
+ supportsMultipleScenes() {
895
+ return supportsMultipleScenes();
896
+ }
897
+ isUsingSceneLifecycle() {
898
+ return this.supportsScenes() && this._windowSceneMap.size > 0;
899
+ }
900
+ // Call this to set up scene-based configuration
901
+ configureForScenes() {
902
+ if (!this.supportsScenes()) {
903
+ console.warn('Scene-based lifecycle is only supported on iOS 13+ iPad or visionOS with multi-scene enabled apps.');
904
+ return;
905
+ }
906
+ // Additional scene configuration can be added here
907
+ // For now, the notification observers are already set up in the constructor
908
+ }
909
+ // Stable scene id for lookups
910
+ _getSceneId(scene) {
911
+ try {
912
+ if (!scene) {
913
+ return 'Unknown';
914
+ }
915
+ // Prefer session persistentIdentifier when available (stable across lifetime)
916
+ const session = scene.session;
917
+ const persistentId = session && session.persistentIdentifier;
918
+ if (persistentId) {
919
+ return `${persistentId}`;
920
+ }
921
+ // Fallbacks
922
+ if (scene.hash != null) {
923
+ return `${scene.hash}`;
924
+ }
925
+ const desc = scene.description;
926
+ if (desc) {
927
+ return `${desc}`;
928
+ }
929
+ }
930
+ catch (err) {
931
+ // ignore
932
+ }
933
+ return 'Unknown';
934
+ }
935
+ // Resolve a UIWindowScene from various input types
936
+ _resolveScene(target) {
937
+ if (!__APPLE__) {
938
+ return null;
939
+ }
940
+ if (!target) {
941
+ // Try to pick a non-primary foreground active scene, else last known scene
942
+ const scenes = this.getWindowScenes?.() || [];
943
+ const nonPrimary = scenes.filter((s) => s !== this._primaryScene);
944
+ return nonPrimary[0] || scenes[0] || null;
945
+ }
946
+ // If a View was passed, derive its window.scene
947
+ if (target && typeof target === 'object') {
948
+ // UIWindowScene
949
+ if (target.session && target.activationState !== undefined) {
950
+ return target;
951
+ }
952
+ // UIWindow
953
+ if (target.windowScene) {
954
+ return target.windowScene;
955
+ }
956
+ // NativeScript View
957
+ if (target?.nativeViewProtected) {
958
+ const uiView = target.nativeViewProtected;
959
+ const win = uiView?.window;
960
+ return win?.windowScene || null;
961
+ }
962
+ }
963
+ // String id lookup
964
+ if (typeof target === 'string') {
965
+ if (this._openedScenesById.has(target)) {
966
+ return this._openedScenesById.get(target);
967
+ }
968
+ // Try matching by persistentIdentifier or hash among known scenes
969
+ const scenes = this.getWindowScenes?.() || [];
970
+ for (const s of scenes) {
971
+ const sid = this._getSceneId(s);
972
+ if (sid === target) {
973
+ return s;
974
+ }
975
+ }
976
+ }
977
+ return null;
978
+ }
979
+ createSceneWithLegacyAPI(data) {
980
+ const windowScene = this.window?.windowScene;
981
+ if (!windowScene) {
982
+ return;
983
+ }
984
+ // Create user activity for the new scene
985
+ const userActivity = NSUserActivity.alloc().initWithActivityType(`${NSBundle.mainBundle.bundleIdentifier}.scene`);
986
+ userActivity.userInfo = dataSerialize(data);
987
+ // Use the legacy API
988
+ const options = UISceneActivationRequestOptions.new();
989
+ options.requestingScene = windowScene;
990
+ UIApplication.sharedApplication.requestSceneSessionActivationUserActivityOptionsErrorHandler(null, // session - null for new scene
991
+ userActivity, options, (error) => {
992
+ if (error) {
993
+ console.error(`Legacy scene API failed: ${error.localizedDescription}`);
994
+ }
995
+ });
996
+ }
997
+ /**
998
+ * Creates a simple view controller with a NativeScript view for a scene window.
999
+ * @param window The UIWindow to set content for
1000
+ * @param view The NativeScript View to set as root content
1001
+ */
1002
+ setWindowRootView(window, view) {
1003
+ if (!window || !view) {
1004
+ return;
1005
+ }
1006
+ if (view.ios) {
1007
+ window.rootViewController = view.viewController;
1008
+ window.makeKeyAndVisible();
1009
+ }
1010
+ else {
1011
+ console.warn('View does not have a native iOS implementation');
1012
+ }
1013
+ }
417
1014
  get ios() {
418
1015
  // ensures Application.ios is defined when running on iOS
419
1016
  return this;
@@ -843,6 +1440,8 @@ function applyFontScaleToRootViews() {
843
1440
  }
844
1441
  export function initAccessibilityCssHelper() {
845
1442
  ensureClasses();
1443
+ updateCurrentHelperClasses(applyRootCssClass);
1444
+ applyFontScaleToRootViews();
846
1445
  Application.on(Application.fontScaleChangedEvent, () => {
847
1446
  updateCurrentHelperClasses(applyRootCssClass);
848
1447
  applyFontScaleToRootViews();