Skip to content

[iOS] Fix Shell long-press back button not triggering navigation events#33380

Merged
PureWeen merged 5 commits intodotnet:inflight/candidatefrom
kubaflo:fixed-failing-test-inflight
Jan 9, 2026
Merged

[iOS] Fix Shell long-press back button not triggering navigation events#33380
PureWeen merged 5 commits intodotnet:inflight/candidatefrom
kubaflo:fixed-failing-test-inflight

Conversation

@kubaflo
Copy link
Contributor

@kubaflo kubaflo commented Jan 6, 2026

Description of Change

Fixes long-press back button navigation not triggering OnAppearing and other navigation events in Shell.

Problem: Test expected OnAppearing count: 2, got OnAppearing count: 1

Root cause: PR #29825 replaced SendPop() with manual stack synchronization (SyncStackDownTo()), which doesn't trigger navigation events.

Fix: Simplified DidPopItem to use stack-sync detection:

  • Stacks in sync → Shell already handled pop → return early
  • Stacks out of sync → user-initiated (long-press) → call SendPop()

Key insight: Tab tap updates Shell's stack BEFORE DidPopItem is called, but iOS long-press pops directly without notifying Shell first.

Regression chain:

PR What happened
#24003 Fixed #23892 with _popRequested flag
#29825 Removed flag, added SyncStackDownTo() - broke long-press
#32456 Added null checks, maintained broken state
#33380 This PR - simplified fix using stack-sync detection

Removed: SyncStackDownTo() method (44 lines)
What to avoid: Don't remove stack count comparison - distinguishes user vs programmatic navigation.

Issues Fixed

Fixes #33379

Related: #23892, #29798 (verified not regressed ✅)

@kubaflo kubaflo changed the title [iOS] Fix Issue23892 test - Restore _popRequested logic for long-pres… [PR agent] Issue23892.ShellBackButtonShouldWorkOnLongPress - test fix Jan 6, 2026
@kubaflo kubaflo mentioned this pull request Jan 6, 2026
@kubaflo kubaflo assigned kubaflo and unassigned kubaflo Jan 6, 2026
@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Jan 6, 2026
@PureWeen
Copy link
Member

PureWeen commented Jan 6, 2026

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@kubaflo
Copy link
Contributor Author

kubaflo commented Jan 6, 2026

✅ Agent Review: APPROVE

Automated review completed. This PR correctly fixes the test regression by restoring the _popRequested logic that distinguishes user-initiated from programmatic navigation.


📊 Phase Status

Phase Status Result
Pre-Flight ✅ COMPLETE Context gathered
🧪 Tests ✅ COMPLETE Tests exist and follow conventions
🚦 Gate PASSED Tests FAIL without fix, PASS with fix
🔍 Analysis ✅ COMPLETE Root cause identified
⚖️ Compare ✅ COMPLETE Optimal approach confirmed
🔬 Regression ✅ COMPLETE No regressions expected
📋 Report ✅ COMPLETE Recommendation: APPROVE

🔍 Root Cause Analysis

Timeline of Changes

1. PR #24003 (Feb 2025) - Original fix for #23892

  • Added _popRequested flag to distinguish navigation types
  • User-initiated navigation → calls SendPop() → fires navigation events
  • Result: Long-press navigation worked correctly ✅

2. PR #29825 (Jul 2025) - Fix for tab blank issue

  • Expanded DidPopItem with null checks and manual synchronization
  • Accidentally removed: _popRequested flag and all logic
  • Result: Long-press navigation stopped firing events ❌

3. PR #32456 (Jan 2026) - Fix for navigation hang

The Core Problem

iOS Shell has two navigation types that need different handling:

Type Example Needs SendPop()? Why?
User-initiated Long-press back, swipe ✅ YES Must trigger navigation events
Programmatic Navigation.PopAsync() ❌ NO Manual sync sufficient

PR #29825 broke this by removing the mechanism to distinguish between them.

This PR's Solution

Restores the _popRequested flag while keeping all null checks:

bool DidPopItem(...)
{
    // User-initiated → delegate to SendPop() (fires events)
    if (!_popRequested)
        return SendPop();

    // Programmatic → manual sync (preserves PR #29825 logic)
    [... null checks and manual synchronization ...]
}
🧪 Test Verification

✅ Gate Verification: PASSED

WITHOUT fix (current inflight/candidate with PR #32456):

Failed ShellBackButtonShouldWorkOnLongPress
Expected: "OnAppearing count: 2"
But was:  "OnAppearing count: 1"

✅ Issue successfully reproduced

WITH fix (PR #33380 applied):

Passed ShellBackButtonShouldWorkOnLongPress [3 s]
Total tests: 1, Passed: 1

✅ Fix successfully resolves the issue

Test Coverage

⚖️ Comparison & Assessment

Why This Approach is Optimal

Aspect Assessment
Minimal? ✅ Only adds back what was removed (+11, -1 lines)
Preserves fixes? ✅ All null checks from PR #32456 & #29825 intact
Complexity ✅ Low - simple flag-based routing
Risk ✅ Low - restores proven logic from PR #24003
Tests ✅ Pass - gate verification confirms

Alternative Approaches (Rejected)

  • Always call SendPop(): Would cause event re-entry for programmatic navigation
  • Always manual sync: Current broken behavior
  • iOS stack inspection: More complex, no clear indicator in iOS APIs
🔬 Regression Analysis

Edge Cases Verified

Long-press navigation (#23892) - Test confirms fixed
Rapid navigation (#32425) - Null checks preserved
Tab blank issue (#29798) - Manual sync logic preserved
Null ViewControllers - Pattern matching fix handles correctly

No Regressions Expected

Scenario Risk Why?
Programmatic navigation Low Logic unchanged from PR #29825
Null references Low All null checks preserved
Event re-entry Low Flag prevents this
Tab blank returns Low Manual sync preserved

📝 Summary

This PR:

  • ✅ Fixes the test regression correctly
  • ✅ Preserves all recent fixes (null checks, manual sync)
  • ✅ Uses minimal, surgical changes
  • ✅ Has excellent documentation in PR description
  • ✅ Passes all verification tests

Recommendation: APPROVE and merge


Review generated by GitHub Copilot CLI PR Agent
Session state: .github/agent-pr-session/pr-33380.md

@PureWeen
Copy link
Member

PureWeen commented Jan 6, 2026

/azp run maui-pr-uitests

@azure-pipelines
Copy link

Azure Pipelines could not run because the pipeline triggers exclude this branch/path.

PureWeen added a commit that referenced this pull request Jan 7, 2026
Copy link
Member

@PureWeen PureWeen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you review the suggested changes in the agent-pr-session

@PureWeen
Copy link
Member

PureWeen commented Jan 8, 2026

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

PureWeen added a commit to kubaflo/maui that referenced this pull request Jan 8, 2026
The PR has been updated to implement a simpler approach for stack sync detection, resulting in a net reduction of 49 lines of code. The simpler method removes unnecessary complexity while maintaining functionality and passing all tests.
@PureWeen
Copy link
Member

PureWeen commented Jan 8, 2026

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@PureWeen PureWeen force-pushed the inflight/candidate branch from 4d73f0a to 8eab1e5 Compare January 9, 2026 02:06
kubaflo and others added 5 commits January 8, 2026 20:08
…s navigation

The test was failing because PR dotnet#32456 removed the _popRequested flag and
logic that was originally added in PR dotnet#24003 to fix issue dotnet#23892 (long-press
back button navigation not updating Shell's current page).

Later, PR dotnet#29825 expanded the DidPopItem method with null checks and manual
synchronization, but removed the _popRequested flag handling.

This commit restores the _popRequested logic while keeping the null checks:
- For user-initiated navigation (_popRequested == false), call SendPop()
  which triggers GoToAsync and properly fires navigation events
- For programmatic navigation (_popRequested == true), use manual
  synchronization with null checks to prevent crashes

Also fixes ElementForViewController to properly handle null ViewControllers
by avoiding property pattern matching that requires non-null values.

Fixes dotnet#33379
Replace _popRequested flag approach with simpler stateless detection:
- Check if Shell stack and iOS stack are in sync
- In sync = programmatic navigation (Shell already updated)
- Out of sync = user-initiated navigation (iOS popped directly)

Benefits over previous approach:
- No state tracking needed (removes _popRequested field)
- Removes 17 lines of manual stack sync code
- Simpler logic that's easier to maintain

Tests passing:
- Issue23892 (long-press navigation)
- Issue29798 (tab blank scenario)
- Issue21119 (Shell navigation)

Fixes dotnet#33379
The PR has been updated to implement a simpler approach for stack sync detection, resulting in a net reduction of 49 lines of code. The simpler method removes unnecessary complexity while maintaining functionality and passing all tests.
@PureWeen PureWeen force-pushed the fixed-failing-test-inflight branch from 77aead4 to dfd1613 Compare January 9, 2026 02:08
@sheiksyedm
Copy link
Contributor

sheiksyedm commented Jan 9, 2026

@kubaflo I have some concern on this fix. The description says about restoring _popRequested flag, but that doesn't exist in code changes. You have reverted the manual sync logic with SyncStackDownTo() method from the PR #29825. Will PR #29825's fix be broken after this change?

@PureWeen
Copy link
Member

PureWeen commented Jan 9, 2026

@sheiksyedm Great question! I investigated this concern and can confirm the PR does NOT regress Issue #29798.

Verification

I ran the Issue29798.TabShouldNotBeBlackAfterTabNavigation UI test on iOS with the PR's changes:

Test:     Issue29798.TabShouldNotBeBlackAfterTabNavigation
Platform: iOS 18.5
Result:   ✅ PASSED

Why the Simplified Approach Works for Both Scenarios

The key insight is that the simplified stack-sync detection handles both issues correctly:

Scenario Stack State at DidPopItem Behavior
Tab tap pop (Issue #29798) Shell updates stack BEFORE DidPopItem → stacks ARE in sync Returns true early (no SendPop()) ✅
Long-press back (Issue #23892) iOS pops directly → Shell stack NOT updated → stacks out of sync Calls SendPop() to trigger navigation events ✅

The SyncStackDownTo() method was a manual workaround to synchronize stacks after the fact. The simplified approach achieves the same result more elegantly by detecting sync state upfront and only calling SendPop() when the stacks are actually out of sync.

About the PR Description

You're right that the description mentions "restoring _popRequested flag" but the current implementation uses a different (simpler) approach. The PR was updated to use stack-sync detection instead, which is a net -49 lines of code. The description could be updated to reflect this.

@PureWeen
Copy link
Member

PureWeen commented Jan 9, 2026

/azp run maui-pr-uitests

@azure-pipelines
Copy link

Azure Pipelines could not run because the pipeline triggers exclude this branch/path.

@PureWeen
Copy link
Member

PureWeen commented Jan 9, 2026

/azp run maui-pr-devicetests

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@PureWeen PureWeen changed the title [PR agent] Issue23892.ShellBackButtonShouldWorkOnLongPress - test fix [iOS] Fix Shell long-press back button not triggering navigation events Jan 9, 2026
@kubaflo
Copy link
Contributor Author

kubaflo commented Jan 9, 2026

@sheiksyedm Thanks! I didn't update the description after I've committed the agent's suggestions

rmarinho pushed a commit that referenced this pull request Jan 9, 2026
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Description

The `maui-pr-uitests` pipeline was not running for PRs targeting
`inflight/*` branches because they were not included in the trigger
configuration.

This was discovered in PR #33380 where `/azp run maui-pr-uitests`
returned:
> Azure Pipelines could not run because the pipeline triggers exclude
this branch/path.

## Changes

Added `inflight/*` to both `trigger:` and `pr:` branch includes in
`eng/pipelines/ci-uitests.yml`, matching what `ci-device-tests.yml`
already has.

## Related

- Unblocks: #33380
@PureWeen PureWeen merged commit 591ea07 into dotnet:inflight/candidate Jan 9, 2026
41 checks passed
PureWeen added a commit that referenced this pull request Jan 9, 2026
…ts (#33380)

### Description of Change

Fixes long-press back button navigation not triggering `OnAppearing` and
other navigation events in Shell.

**Problem:** Test expected `OnAppearing count: 2`, got `OnAppearing
count: 1`

**Root cause:** PR #29825 replaced `SendPop()` with manual stack
synchronization (`SyncStackDownTo()`), which doesn't trigger navigation
events.

**Fix:** Simplified `DidPopItem` to use stack-sync detection:
- Stacks in sync → Shell already handled pop → return early  
- Stacks out of sync → user-initiated (long-press) → call `SendPop()`

**Key insight:** Tab tap updates Shell's stack BEFORE `DidPopItem` is
called, but iOS long-press pops directly without notifying Shell first.

**Regression chain:**
| PR | What happened |
|-----|---------------|
| #24003 | Fixed #23892 with `_popRequested` flag |
| #29825 | Removed flag, added `SyncStackDownTo()` - **broke
long-press** |
| #32456 | Added null checks, maintained broken state |
| #33380 | **This PR** - simplified fix using stack-sync detection |

**Removed:** `SyncStackDownTo()` method (44 lines)  
**What to avoid:** Don't remove stack count comparison - distinguishes
user vs programmatic navigation.

### Issues Fixed

Fixes #33379

**Related:** #23892, #29798 (verified not regressed ✅)

---------

Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
PureWeen added a commit that referenced this pull request Jan 13, 2026
…ts (#33380)

### Description of Change

Fixes long-press back button navigation not triggering `OnAppearing` and
other navigation events in Shell.

**Problem:** Test expected `OnAppearing count: 2`, got `OnAppearing
count: 1`

**Root cause:** PR #29825 replaced `SendPop()` with manual stack
synchronization (`SyncStackDownTo()`), which doesn't trigger navigation
events.

**Fix:** Simplified `DidPopItem` to use stack-sync detection:
- Stacks in sync → Shell already handled pop → return early  
- Stacks out of sync → user-initiated (long-press) → call `SendPop()`

**Key insight:** Tab tap updates Shell's stack BEFORE `DidPopItem` is
called, but iOS long-press pops directly without notifying Shell first.

**Regression chain:**
| PR | What happened |
|-----|---------------|
| #24003 | Fixed #23892 with `_popRequested` flag |
| #29825 | Removed flag, added `SyncStackDownTo()` - **broke
long-press** |
| #32456 | Added null checks, maintained broken state |
| #33380 | **This PR** - simplified fix using stack-sync detection |

**Removed:** `SyncStackDownTo()` method (44 lines)  
**What to avoid:** Don't remove stack count comparison - distinguishes
user vs programmatic navigation.

### Issues Fixed

Fixes #33379

**Related:** #23892, #29798 (verified not regressed ✅)

---------

Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
PureWeen added a commit that referenced this pull request Jan 13, 2026
…ts (#33380)

### Description of Change

Fixes long-press back button navigation not triggering `OnAppearing` and
other navigation events in Shell.

**Problem:** Test expected `OnAppearing count: 2`, got `OnAppearing
count: 1`

**Root cause:** PR #29825 replaced `SendPop()` with manual stack
synchronization (`SyncStackDownTo()`), which doesn't trigger navigation
events.

**Fix:** Simplified `DidPopItem` to use stack-sync detection:
- Stacks in sync → Shell already handled pop → return early  
- Stacks out of sync → user-initiated (long-press) → call `SendPop()`

**Key insight:** Tab tap updates Shell's stack BEFORE `DidPopItem` is
called, but iOS long-press pops directly without notifying Shell first.

**Regression chain:**
| PR | What happened |
|-----|---------------|
| #24003 | Fixed #23892 with `_popRequested` flag |
| #29825 | Removed flag, added `SyncStackDownTo()` - **broke
long-press** |
| #32456 | Added null checks, maintained broken state |
| #33380 | **This PR** - simplified fix using stack-sync detection |

**Removed:** `SyncStackDownTo()` method (44 lines)  
**What to avoid:** Don't remove stack count comparison - distinguishes
user vs programmatic navigation.

### Issues Fixed

Fixes #33379

**Related:** #23892, #29798 (verified not regressed ✅)

---------

Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
kubaflo added a commit to kubaflo/maui that referenced this pull request Jan 16, 2026
…ts (dotnet#33380)

### Description of Change

Fixes long-press back button navigation not triggering `OnAppearing` and
other navigation events in Shell.

**Problem:** Test expected `OnAppearing count: 2`, got `OnAppearing
count: 1`

**Root cause:** PR dotnet#29825 replaced `SendPop()` with manual stack
synchronization (`SyncStackDownTo()`), which doesn't trigger navigation
events.

**Fix:** Simplified `DidPopItem` to use stack-sync detection:
- Stacks in sync → Shell already handled pop → return early  
- Stacks out of sync → user-initiated (long-press) → call `SendPop()`

**Key insight:** Tab tap updates Shell's stack BEFORE `DidPopItem` is
called, but iOS long-press pops directly without notifying Shell first.

**Regression chain:**
| PR | What happened |
|-----|---------------|
| dotnet#24003 | Fixed dotnet#23892 with `_popRequested` flag |
| dotnet#29825 | Removed flag, added `SyncStackDownTo()` - **broke
long-press** |
| dotnet#32456 | Added null checks, maintained broken state |
| dotnet#33380 | **This PR** - simplified fix using stack-sync detection |

**Removed:** `SyncStackDownTo()` method (44 lines)  
**What to avoid:** Don't remove stack count comparison - distinguishes
user vs programmatic navigation.

### Issues Fixed

Fixes dotnet#33379

**Related:** dotnet#23892, dotnet#29798 (verified not regressed ✅)

---------

Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants