FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat: reloadApplication support · NativeScript/NativeScript@8bd41bf · GitHub

Commit 8bd41bf

Browse files
committed
feat: reloadApplication support
Restart app js bundle without requiring app process restart.
1 parent b54537d commit 8bd41bf

4 files changed

Lines changed: 79 additions & 124 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { ApplicationCommon } from './application-common';
2+
import { getAppMainEntry, setAppMainEntry } from './helpers-common';
3+
4+
class TestApplication extends ApplicationCommon {
5+
getRootView() {
6+
return null as any;
7+
}
8+
}
9+
10+
describe('__onApplicationReload', () => {
11+
afterEach(() => {
12+
setAppMainEntry(undefined);
13+
delete global.__onApplicationReload;
14+
});
15+
16+
it('remounts the current main entry without constructing a new Application', () => {
17+
const app = new TestApplication();
18+
const entry = { moduleName: 'app-root' };
19+
setAppMainEntry(entry);
20+
21+
const remount = vi.spyOn(app, 'resetRootView');
22+
global.__onApplicationReload();
23+
24+
expect(remount).toHaveBeenCalledWith(entry);
25+
expect(getAppMainEntry()).toBe(entry);
26+
});
27+
28+
it('is a no-op when the app has no main entry yet', () => {
29+
const app = new TestApplication();
30+
const remount = vi.spyOn(app, 'resetRootView');
31+
32+
global.__onApplicationReload();
33+
34+
expect(remount).not.toHaveBeenCalled();
35+
});
36+
});

‎packages/core/application/application-common.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,15 @@ export class ApplicationCommon {
227227
const rootView = this.getRootView();
228228
this.livesync(rootView, context);
229229
};
230+
231+
// Isolate-preserving reload remounts the current entry without a new runtime.
232+
global.__onApplicationReload = () => {
233+
const entry = getAppMainEntry();
234+
if (!entry) {
235+
return;
236+
}
237+
this.resetRootView(entry);
238+
};
230239
}
231240

232241
/**

‎packages/core/application/application.ios.ts‎

Lines changed: 0 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,6 @@ function supportsMultipleScenes(): boolean {
127127
return UIApplication.sharedApplication?.supportsMultipleScenes;
128128
}
129129

130-
/**
131-
* Number of times the JS runtime has been soft-rebooted in this process via
132-
* NativeScriptRuntime.reloadApplication / restartWithConfig. 0 on first boot.
133-
* Provided as a global by the iOS runtime (v9+); older runtimes report 0.
134-
*/
135-
function getRuntimeReloadCount(): number {
136-
const runtime = (globalThis as any).NativeScriptRuntime;
137-
return runtime && typeof runtime.reloadCount === 'number' ? runtime.reloadCount : 0;
138-
}
139-
140130
@NativeClass
141131
class Responder extends UIResponder implements UIApplicationDelegate {
142132
get window(): UIWindow {
@@ -279,13 +269,6 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
279269

280270
private _notificationObservers: NotificationObserver[] = [];
281271

282-
// Strong references to delegates recreated after an in-process soft reboot
283-
// (NativeScriptRuntime.reloadApplication). UIApplication.delegate is an
284-
// `assign` property and UIScene keeps its own reference to the delegate we
285-
// replace, so without these the fresh instances would be deallocated.
286-
private _softRebootAppDelegate: UIApplicationDelegate;
287-
private _softRebootSceneDelegates = new Map<UIScene, UIWindowSceneDelegate>();
288-
289272
displayedOnce = false;
290273
displayedLinkTarget: CADisplayLinkTarget;
291274
displayedLink: CADisplayLink;
@@ -378,8 +361,6 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
378361
}
379362

380363
private runAsEmbeddedApp() {
381-
this._reattachNativeDelegatesAfterSoftReboot();
382-
383364
// TODO: this rootView should be held alive until rootController dismissViewController is called.
384365
const rootView = this.createRootView(this._rootView, true);
385366
if (!rootView) {
@@ -391,10 +372,6 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
391372
let window = getWindow() as UIWindow;
392373

393374
if (!window) {
394-
// In-process soft reboot with OTAs.
395-
// Original UIWindow is deallocated when the old JS isolate is torn down.
396-
// Recreate a window bound to the active UIWindowScene so the
397-
// root has somewhere to attach.
398375
const app = UIApplication.sharedApplication;
399376
const all = app && app.connectedScenes ? app.connectedScenes.allObjects : null;
400377
let targetScene: UIWindowScene;
@@ -414,13 +391,6 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
414391
window = UIWindow.alloc().initWithWindowScene(targetScene);
415392
this._setWindowForScene(window, targetScene);
416393
this._setupWindowForScene?.(window, targetScene);
417-
418-
// If the scene's delegate was recreated after a soft reboot, point it
419-
// at the new window so `scene.delegate.window` queries resolve.
420-
const freshSceneDelegate = this._softRebootSceneDelegates.get(targetScene);
421-
if (freshSceneDelegate) {
422-
freshSceneDelegate.window = window;
423-
}
424394
}
425395
}
426396

@@ -470,67 +440,6 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
470440
this.notifyAppStarted();
471441
}
472442

473-
/**
474-
* After an in-process soft reboot (NativeScriptRuntime.reloadApplication /
475-
* restartWithConfig), the Objective-C delegate classes created by the
476-
* previous JS isolate still exist and UIKit keeps dispatching to their
477-
* now-inert instances: their method callbacks bail out because the isolate
478-
* that implemented them is gone. Notification-center observers are
479-
* re-registered by the new isolate, but delegate-based dispatch (custom
480-
* UIApplicationDelegate methods like push-token/openURL callbacks, and the
481-
* UIScene delegates used by scene-lifecycle apps) stays pinned to the old
482-
* bundle. Recreate those delegates from this bundle's classes and re-point
483-
* UIKit at them.
484-
*/
485-
private _reattachNativeDelegatesAfterSoftReboot(): void {
486-
if (getRuntimeReloadCount() <= 0) {
487-
// First boot: UIApplicationMain (or the host app) set up delegates.
488-
return;
489-
}
490-
491-
if (isEmbedded()) {
492-
// The host app owns the UIApplication delegate; never touch it.
493-
return;
494-
}
495-
496-
const app = UIApplication.sharedApplication;
497-
if (!app) {
498-
return;
499-
}
500-
501-
// Fresh application delegate from the new bundle. Assigning `delegate`
502-
// does not retain (unlike the UIApplicationMain launch path), so keep a
503-
// strong reference ourselves.
504-
this.delegate ??= Responder as any;
505-
const freshDelegate = (<any>this.delegate).new() as UIApplicationDelegate;
506-
this._softRebootAppDelegate = freshDelegate;
507-
app.delegate = freshDelegate;
508-
509-
// Re-point already-connected scenes at fresh scene delegates so scene
510-
// lifecycle and user-implemented scene delegate methods (shortcuts,
511-
// openURLContexts, userActivity continuation, etc.) reach this isolate.
512-
// Newly connecting scenes are covered by the fresh application delegate's
513-
// applicationConfigurationForConnectingSceneSessionOptions, which returns
514-
// this bundle's SceneDelegate class.
515-
if (this.supportsScenes()) {
516-
this._softRebootSceneDelegates.clear();
517-
const scenes = app.connectedScenes?.allObjects;
518-
for (let i = 0; scenes && i < scenes.count; i++) {
519-
const scene = scenes.objectAtIndex(i);
520-
if (!(scene instanceof UIWindowScene)) {
521-
continue;
522-
}
523-
const freshSceneDelegate = SceneDelegate.new() as UIWindowSceneDelegate;
524-
scene.delegate = freshSceneDelegate;
525-
this._softRebootSceneDelegates.set(scene, freshSceneDelegate);
526-
}
527-
}
528-
529-
if (Trace.isEnabled()) {
530-
Trace.write(`Reattached application delegate${this._softRebootSceneDelegates.size ? ` and ${this._softRebootSceneDelegates.size} scene delegate(s)` : ''} after soft reboot (reloadCount: ${getRuntimeReloadCount()})`, Trace.categories.NativeLifecycle);
531-
}
532-
}
533-
534443
private getViewController(rootView: View): UIViewController {
535444
let viewController: UIViewController = rootView.viewController || rootView.ios;
536445

‎packages/core/global-types.d.ts‎

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ declare module globalThis {
111111
var __extends: any;
112112
var __onLiveSync: (context?: { type: string; path: string }) => void;
113113
var __onLiveSyncCore: (context?: { type: string; path: string }) => void;
114+
var __onApplicationReload: () => void;
114115
var __onUncaughtError: (error: NativeScriptError) => void;
115116
var __onDiscardedError: (error: NativeScriptError) => void;
116117
var __snapshot: boolean;
@@ -165,39 +166,39 @@ interface NodeModule {
165166
}
166167

167168
declare enum RequestContext {
168-
'audio',
169-
'beacon',
170-
'cspreport',
171-
'download',
172-
'embed',
173-
'eventsource',
174-
'favicon',
175-
'fetch',
176-
'font',
177-
'form',
178-
'frame',
179-
'hyperlink',
180-
'iframe',
181-
'image',
182-
'imageset',
183-
'import',
184-
'internal',
185-
'location',
186-
'manifest',
187-
'object',
188-
'ping',
189-
'plugin',
190-
'prefetch',
191-
'script',
192-
'serviceworker',
193-
'sharedworker',
194-
'subresource',
195-
'style',
196-
'track',
197-
'video',
198-
'worker',
199-
'xmlhttprequest',
200-
'xslt',
169+
audio,
170+
beacon,
171+
cspreport,
172+
download,
173+
embed,
174+
eventsource,
175+
favicon,
176+
fetch,
177+
font,
178+
form,
179+
frame,
180+
hyperlink,
181+
iframe,
182+
image,
183+
imageset,
184+
import,
185+
internal,
186+
location,
187+
manifest,
188+
object,
189+
ping,
190+
plugin,
191+
prefetch,
192+
script,
193+
serviceworker,
194+
sharedworker,
195+
subresource,
196+
style,
197+
track,
198+
video,
199+
worker,
200+
xmlhttprequest,
201+
xslt,
201202
}
202203

203204
// Extend the lib.dom.d.ts Body interface with `formData`

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL