Skip to content

Commit bcb87c9

Browse files
committed
fix(ios): re-attach root view after in-process runtime reload
When the runtime is reloaded in-process (NativeScriptRuntime.reloadApplication, used for OTA soft reboots) the app re-runs main in a fresh JS isolate while the process stays alive. iOSApplication.run() sees a live UIApplication and routes to runAsEmbeddedApp() - The previous UIWindow was deallocated along with the old isolate (it was retained only by the JS-side SceneDelegate._window), and sceneWillConnectToSession — which creates the window — does not re-fire on a reload. getWindow() then returned null and runAsEmbeddedApp() bailed early, leaving a black screen. - With no embedder delegate (NativeScript owns the UIApplication), the new root was presented modally over the old, now-dead root controller instead of replacing it — leaking the old controller on every reload and breaking when an overlay is mid-presentation. runAsEmbeddedApp() now: - recreates a window bound to the active UIWindowScene when none is found (initWithWindowScene + _setWindowForScene/_setupWindowForScene); - moves the rootViewController guard into the embedder-only branch, since a freshly recreated window has no controller yet; - replaces the window root via setWindowRootView() for the NativeScript-owned case instead of modal-presenting over the stale root.
1 parent 71d2203 commit bcb87c9

1 file changed

Lines changed: 39 additions & 6 deletions

File tree

packages/core/application/application.ios.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -331,17 +331,43 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
331331
this._rootView = rootView;
332332
setRootView(rootView);
333333
// Attach to the existing iOS app
334-
const window = getWindow() as UIWindow;
334+
let window = getWindow() as UIWindow;
335335

336336
if (!window) {
337-
return;
337+
// In-process soft reboot with OTAs.
338+
// Original UIWindow is deallocated when the old JS isolate is torn down.
339+
// Recreate a window bound to the active UIWindowScene so the
340+
// root has somewhere to attach.
341+
const app = UIApplication.sharedApplication;
342+
const all = app && app.connectedScenes ? app.connectedScenes.allObjects : null;
343+
let targetScene: UIWindowScene;
344+
if (all) {
345+
for (let i = 0; i < all.count; i++) {
346+
const s = all.objectAtIndex(i) as UIWindowScene;
347+
// Only UIWindowScene exposes `windows`; prefer a foreground-active one.
348+
if (s && typeof s.windows !== 'undefined') {
349+
targetScene = s;
350+
if (s.activationState === UISceneActivationState.ForegroundActive) {
351+
break;
352+
}
353+
}
354+
}
355+
}
356+
if (targetScene) {
357+
window = UIWindow.alloc().initWithWindowScene(targetScene);
358+
this._setWindowForScene(window, targetScene);
359+
this._setupWindowForScene?.(window, targetScene);
360+
}
338361
}
339362

340-
const rootController = window.rootViewController;
341-
if (!rootController) {
363+
if (!window) {
342364
return;
343365
}
344366

367+
// May be null on a freshly recreated window — expected; the replace-root
368+
// path below sets it. Only the embedder path needs an existing controller.
369+
const rootController = window.rootViewController;
370+
345371
const controller = this.getViewController(rootView);
346372
const embedderDelegate = NativeScriptEmbedder.sharedInstance().delegate;
347373

@@ -360,11 +386,18 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
360386
});
361387

362388
if (embedderDelegate) {
389+
// Embed into host app.
390+
// present over the host's existing root view controller.
391+
if (!rootController) {
392+
return;
393+
}
363394
this.setViewControllerView(rootView);
364395
embedderDelegate.presentNativeScriptApp(controller);
365396
} else {
366-
const visibleVC = iosUtils.getVisibleViewController(rootController);
367-
visibleVC.presentViewControllerAnimatedCompletion(controller, true, null);
397+
// No embedder delegate = NativeScript owns the UIApplication.
398+
// Attach the root to the window.
399+
this.setViewControllerView(rootView);
400+
this.setWindowRootView(window, rootView);
368401
}
369402

370403
this.initRootView(rootView);

0 commit comments

Comments
 (0)