NativeScript / NativeScript/angular

[iOS] RouterExtensions.back() fails silently after app lock/suspend mid-navigation — component stuck, native back button unresponsive on resume

未關閉
#164 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

主要語言
TypeScript
星號
55
分支
13
平均合併
16 分鐘
30 天內合併 PR
1

描述

[iOS] RouterExtensions.back() fails silently after app lock/suspend mid-navigation — component stuck, native back button unresponsive on resume

Environment

  • CLI: 9.0.5
  • Cross-platform modules (@nativescript/core): 8.9.9
  • Android Runtime: 8.9.2
  • iOS Runtime: 8.9.4
  • Plugin(s): @nativescript/angular
  • NativeScript-Angular: 17.0.0
  • Angular: 17.0.8

Describe the bug

This is a runtime error on iOS only.

When RouterExtensions.back() is called (e.g. on form submit after an async API operation) and the user locks the phone or backgrounds the app within the same moment, the navigation silently fails. On resume:

  • The component that triggered the back navigation is still on screen
  • The native ActionBar back button (top-left iOS back chevron) is also completely unresponsive
  • No exception is thrown, no crash, no error log
  • Calling router.back() again on resume causes the call to repeat but navigation never completes
  • Frame.topmost().canGoBack() returns true but Frame.topmost().goBack() has no visible effect

The app is permanently stuck on that page until force-quit and relaunch.

Console output observed during a single "submit + lock phone" cycle (3 resume attempts):

back Called
Keyboard hidden
super.back Called
back Called
Keyboard hidden
super.back Called
back Called
Keyboard hidden
super.back Called

super.back() fires even when the app is in the background. NavigationEnd never fires. Angular router url still points to the source page on resume. Frame.topmost().goBack() on resume is silently ignored and the native back button stops responding entirely.


To Reproduce

Setup:

A custom RouterExtensions subclass wrapping back() in a setTimeout for iOS keyboard dismiss (a common and necessary iOS pattern — navigating without first dismissing the keyboard causes visual glitches):

@Injectable()
export class BaseRouterExtension extends RouterExtensions {
  IOS_KEYBOARD_DISMISS_DELAY = 150;

override back(options?: BackNavigationOptions) {
this.zone.run(() => {
hideKeyboard();
if (isIOS) {
setTimeout(() => {
super.back(options); // fires even when app is suspended
}, this.IOS_KEYBOARD_DISMISS_DELAY);
} else {
super.back(options);
}
});
}
}

Navigation triggered by a signal/effect on API success:

effect(() => {
  if (this.successOperation()) {
    this.navigateBack();
  }
});

async navigateBack() {
await this.onStopTextToSpeech();
this.router.back();
}

Steps:

  1. Navigate from Page A → Page B (a form/edit screen)
  2. On Page B, submit the form — this triggers an async API call, on success router.back() is called
  3. Lock the phone (press power button) within ~150ms of the back() call
  4. Unlock the phone and resume the app
  5. Observe: still on Page B — the programmatic back and the native ActionBar back button both do nothing

Most reliable reproduction (near 100% hit rate):

The setTimeout(150ms) creates a guaranteed window. Lock the phone immediately after tapping Submit before the toast or transition appears.


Expected behavior

Either:

  • RouterExtensions.back() / Frame.topmost().goBack() called on resume should complete the interrupted navigation, OR
  • The native UINavigationController state and NativeScript frame/Angular router state should be re-synced automatically on resume so that navigation can be retried from JS

Sample project

Unable to provide a Playground link as the issue requires a physical device lock action to reproduce (Simulator behaves differently for lock/sleep events). A minimal reproduction would be:

  1. Page A navigates to Page B
  2. Page B calls RouterExtensions.back() inside a setTimeout(150)
  3. Lock the physical device during that 150ms window
  4. Resume — Page B is stuck and native back button is unresponsive

Additional context

What was attempted as workarounds — none fully resolved the issue:

Approach Result
Check Application.inBackground in setTimeout Unreliable — flag not always set before setTimeout fires depending on lock timing
Own _isAppInBackground flag via suspend/resume events Race condition — suspend event fires after navigateBack() call in some sequences
Snapshot router.url before back(), compare on resume URL comparison is accurate, but Frame.topmost().goBack() on resume still silently fails
Frame.topmost().goBack() directly in resume handler No visual effect — native back button is also unresponsive at this point
RouterExtensions.navigateByUrl(previousUrl) on resume Pushes a new page onto the stack instead of popping — incorrect behaviour
page.on('navigatedFrom') to detect native completion Does not fire when navigation is interrupted by lock

The native UINavigationController appears to enter a broken/stuck state after a lock-interrupted navigation transition that cannot be recovered from the JavaScript layer with currently available APIs.

Questions for the team:

  1. After a lock-interrupted UIViewController pop, is there a supported way to re-trigger or complete the navigation from JS?
  2. Is the native UINavigationController instance exposed anywhere so we can call popViewControllerAnimated: directly as a fallback?
  3. Is there a navigation transition completion hook (equivalent to UINavigationControllerDelegate.navigationController:didShowViewController:animated:) accessible from NativeScript?

Related issues:

  • https://github.com/NativeScript/nativescript-angular/issues/1596 — Back navigation broken after suspend/resume in TabView + nested frame scenario — closed/fixed in 2018 via PR https://github.com/NativeScript/NativeScript/pull/6528. The current issue reproduces on a simple single-frame stack with no TabView and is not addressed by that fix.

貢獻指南

開啟貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

研究方向

使用所描述的150ms重現窗口,在實體裝置上從 RouterExtensions.back()、Frame.topmost().goBack() 以及 iOS 的暫停/恢復生命週期開始。追蹤中斷的 UINavigationController 與 NativeScript frame 狀態,然後確認可以重試導覽,或確認原生、frame 與 Angular router 狀態會在恢復後重新同步。

由索引模型根據 Issue 內容生成。

評估

技術堆疊
angular, ios, typescript
領域
mobile-dev
Issue 類型
缺陷
難度
5/5
預估耗時
一週以上
活躍度
冷清
描述清晰度
基本清楚
新手友好度
35/100

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。