Skip to content

[iOS 26] Fix null reference and cast safety in ShellSectionRenderer navigation#7

Merged
kubaflo merged 4 commits intomainfrom
copilot/review-dotnet-maui-pr-32456
Nov 15, 2025
Merged

[iOS 26] Fix null reference and cast safety in ShellSectionRenderer navigation#7
kubaflo merged 4 commits intomainfrom
copilot/review-dotnet-maui-pr-32456

Conversation

Copy link

Copilot AI commented Nov 14, 2025

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 from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description of Change

Addresses navigation hangs on iOS 26 when rapidly pushing/popping pages. The _navStack in ShellSection initializes with { null } and can contain null entries during lifecycle transitions. Two safety issues identified:

DidPopItem method (line 127):

  • Added null check for pages[targetIndex] before passing to SyncStackDownTo
  • Prevents NullReferenceException when accessing stack entries during rapid navigation

ElementForViewController method (line 582):

  • Changed hard cast (IPlatformViewHandler)child.Handler to safe cast child.Handler as IPlatformViewHandler
  • Prevents InvalidCastException when Handler is null, allowing null-conditional operator to function correctly
  • Aligns with pattern used in OnRemoveRequested method
// Before: Hard cast + null-conditional (inconsistent)
var renderer = (IPlatformViewHandler)child.Handler;  // Throws if null
if (viewController == renderer?.ViewController)

// After: Safe cast + null-conditional (consistent)
var renderer = child.Handler as IPlatformViewHandler;
if (viewController == renderer?.ViewController)

Issues Fixed

Fixes dotnet#32425

Original prompt

copilot --allow-all-tools --allow-all-paths
/agent pr-reviewer
please review @dotnet/mauidotnet/pull/32456


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

kubaflo and others added 3 commits November 14, 2025 23:48
Added additional null checks for page and renderer references in ShellSectionRenderer to prevent potential null reference exceptions during navigation and view controller handling.
Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>
Copilot AI changed the title [WIP] Review pull request #32456 for .NET MAUI [iOS 26] Fix null reference and cast safety in ShellSectionRenderer navigation Nov 14, 2025
Copilot AI requested a review from kubaflo November 14, 2025 23:52
@kubaflo kubaflo marked this pull request as ready for review November 15, 2025 00:16
@kubaflo kubaflo merged commit 3bdeacc into main Nov 15, 2025
@kubaflo
Copy link
Owner

kubaflo commented Nov 15, 2025

PR dotnet#32456 Review Summary

Overview

PR Title: [iOS 26] Navigation hangs after rapidly open and closing new page using Navigation.PushAsync - fix
PR Link: dotnet#32456
Issue: dotnet#32425
Author: @kubaflo
Reviewer: GitHub Copilot

Problem Statement

Navigation hangs and shows a blank white screen when rapidly opening and closing pages using Navigation.PushAsync on iOS 26 with Xcode 26 builds. This is a regression specific to iOS 26.

Original PR Changes

The PR made two key changes to src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs:

Change 1: Line 127 - Add null check for pages[targetIndex]

- if (targetIndex < 0 || targetIndex >= pages.Count)
+ if (targetIndex < 0 || targetIndex >= pages.Count || pages[targetIndex] is null)

Change 2: Line 583 - Add null-conditional operator for renderer.ViewController

- if (viewController == renderer.ViewController)
+ if (viewController == renderer?.ViewController)

Review Analysis

Copilot Review Comment #1: Null Check on Line 127

Copilot's Suggestion: Remove || pages[targetIndex] is null as redundant

Analysis:INCORRECT

Reasoning:

  • Investigation of ShellSection.cs revealed that _navStack is initialized with a null entry:
    List<Page> _navStack = new List<Page> { null };  // Line 251
  • The stack can be reset to contain null during lifecycle:
    _navStack = new List<Page> { null };  // Line 174
  • Therefore, null pages CAN exist in the Stack collection
  • The null check is NECESSARY to prevent null reference exceptions when calling SyncStackDownTo(pages[targetIndex])

Recommendation:KEEP the null check as-is from the PR

Copilot Review Comment #2: Inconsistent Cast on Line 582-583

Copilot's Suggestion: Change hard cast to safe cast

Analysis:CORRECT

Original Code:

var renderer = (IPlatformViewHandler)child.Handler;  // Line 582 - hard cast
if (viewController == renderer?.ViewController)      // Line 583 - null-conditional

Problem:

  • If child.Handler is null, the hard cast on line 582 will throw InvalidCastException
  • The null-conditional operator on line 583 never gets a chance to execute
  • This is inconsistent with the pattern used elsewhere in the file (see OnRemoveRequested at line 517-518)

Fix Applied:

var renderer = child.Handler as IPlatformViewHandler;  // Safe cast
if (viewController == renderer?.ViewController)        // Null-safe check

Recommendation:Apply the fix (changed from hard cast to as cast)

Changes Made During Review

  1. ✅ Validated that the null check on line 127 is necessary (kept as-is from PR)
  2. ✅ Fixed inconsistent cast on line 582 (changed from (IPlatformViewHandler) to as IPlatformViewHandler)
  3. ✅ Ran code formatting - no issues found
  4. ✅ Committed both the original PR changes and the review fix

Final Recommendation

APPROVE with modifications:

The PR correctly addresses the iOS 26 navigation hang issue with appropriate null checks. The review identified one valid improvement (line 582 cast consistency) which has been applied. The first review comment was incorrect and the original PR code should be kept.

Summary of All Changes:

diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
index 90c0c7fc0e..77cc4f3465 100644
--- a/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
+++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
@@ -124,7 +124,7 @@ namespace Microsoft.Maui.Controls.Platform.Compatibility
 
 			// Bounds check: ensure we have a valid index for pages array
 			int targetIndex = NavigationBar.Items.Length - 1;
-			if (targetIndex < 0 || targetIndex >= pages.Count)
+			if (targetIndex < 0 || targetIndex >= pages.Count || pages[targetIndex] is null)
 				return true;
 
 			_shellSection.SyncStackDownTo(pages[targetIndex]);
@@ -579,8 +579,8 @@ namespace Microsoft.Maui.Controls.Platform.Compatibility
 			{
 				if (child == null)
 					continue;
-				var renderer = (IPlatformViewHandler)child.Handler;
-				if (viewController == renderer.ViewController)
+				var renderer = child.Handler as IPlatformViewHandler;
+				if (viewController == renderer?.ViewController)
 					return child;
 			}

Why These Changes Are Correct:

  1. Line 127: Prevents null reference exception when accessing a page that might be null in the navigation stack during rapid push/pop operations
  2. Line 582: Prevents InvalidCastException when Handler is null, allowing the null-conditional operator on line 583 to work properly
  3. Line 583: (Original PR) Prevents null reference exception when accessing ViewController on a null renderer

All three changes work together to handle null scenarios that can occur during rapid navigation operations on iOS 26.

Testing Recommendation

The PR should be tested with:

kubaflo added a commit that referenced this pull request Nov 15, 2025
)

* [iOS 26] Fix null reference and cast safety in ShellSectionRenderer navigation (#7)

* Initial plan

* Add null checks in ShellSectionRenderer

Added additional null checks for page and renderer references in ShellSectionRenderer to prevent potential null reference exceptions during navigation and view controller handling.

* Fix inconsistent cast in ElementForViewController method

* Complete PR dotnet#32456 review with detailed analysis

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>

* Initial plan

* Applied PR dotnet#32648 changes for review

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

* Add UI test case for issue dotnet#32616

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

* Final review summary for PR dotnet#32648

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo added a commit that referenced this pull request Nov 15, 2025
* [iOS 26] Fix null reference and cast safety in ShellSectionRenderer navigation (#7)

* Initial plan

* Add null checks in ShellSectionRenderer

Added additional null checks for page and renderer references in ShellSectionRenderer to prevent potential null reference exceptions during navigation and view controller handling.

* Fix inconsistent cast in ElementForViewController method

* Complete PR dotnet#32456 review with detailed analysis

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>

* Initial plan

* Applied PR dotnet#32648 changes for review

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

* Add UI test case for issue dotnet#32616

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

* Final review summary for PR dotnet#32648

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

---------

Co-Authored-By: Copilot <198982749+Copilot@users.noreply.github.com>
Co-Authored-By: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo added a commit that referenced this pull request Nov 15, 2025
* [iOS 26] Fix null reference and cast safety in ShellSectionRenderer navigation (#7)

* Initial plan

* Add null checks in ShellSectionRenderer

Added additional null checks for page and renderer references in ShellSectionRenderer to prevent potential null reference exceptions during navigation and view controller handling.

* Fix inconsistent cast in ElementForViewController method

* Complete PR dotnet#32456 review with detailed analysis

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>

* Initial plan

* Applied PR dotnet#32648 changes for review

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

* Add UI test case for issue dotnet#32616

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

* Final review summary for PR dotnet#32648

Co-authored-by: kubaflo <42434498+kubaflo@users.noreply.github.com>

---------

Co-Authored-By: Copilot <198982749+Copilot@users.noreply.github.com>
Co-Authored-By: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo pushed a commit that referenced this pull request Jan 6, 2026
…tnet#33071)

App crashes because of changing a shared Drawable on the fly. I found
out it is the SearchBar. It only happens on Android and in large apps
with lots of screens. Unfortunately I can't reproduce it in the
TestSuite. But by reading the docs of Android this crash is common and
caused by changing a Drawable which is still referenced. With a custom
SearchBarHandler the issues is resolved.

```Backtrace (top frames):
  #00 pc 00000000006b8694  /system/lib64/libhwui.so
      android::getRootAlpha(_JNIEnv*, _jobject*, long) +4

  #1 pc 0000000002256c90  /memfd:jit-cache (deleted)
      art_jni_trampoline +112

  #2 pc 000000000223bc4c  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.-$$Nest$smnGetRootAlpha +108

  #3 pc 000000000223bb20  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable$VectorDrawableState.getAlpha +144

  #4 pc 00000000025c50e0  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.getAlpha +128

  #5 pc 00000000025c4f9c  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.getOpacity +124

  #6 pc 00000000025c1ea8  /memfd:jit-cache (deleted)
      android.widget.ImageView.isOpaque +152

  #7 pc 000000000227979c  /memfd:jit-cache (deleted)
      android.view.View.invalidateInternal +428

  #8 pc 00000000025c4790  /memfd:jit-cache (deleted)
      android.widget.ImageView.invalidateDrawable +256

  #9 pc 000000000224419c  /memfd:jit-cache (deleted)
      android.graphics.drawable.Drawable.invalidateSelf +156

  #10 pc 000000000260e710  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.setTintList +192

  #11 pc 00000000025d0094  /memfd:jit-cache (deleted)
      **android.graphics.drawable.Drawable.setTint +148**
```

### Description of Change

- Changes tinting of Androids SearchBar to unified setTint instead of
setColorFilter
-  Mutates the drawable before setting the tint.

### Issues Fixed
Issue is fixed with a custom handler for now.

Fixes dotnet#33070
PureWeen pushed a commit that referenced this pull request Jan 9, 2026
…tnet#33071)

App crashes because of changing a shared Drawable on the fly. I found
out it is the SearchBar. It only happens on Android and in large apps
with lots of screens. Unfortunately I can't reproduce it in the
TestSuite. But by reading the docs of Android this crash is common and
caused by changing a Drawable which is still referenced. With a custom
SearchBarHandler the issues is resolved.

```Backtrace (top frames):
  #00 pc 00000000006b8694  /system/lib64/libhwui.so
      android::getRootAlpha(_JNIEnv*, _jobject*, long) +4

  #1 pc 0000000002256c90  /memfd:jit-cache (deleted)
      art_jni_trampoline +112

  #2 pc 000000000223bc4c  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.-$$Nest$smnGetRootAlpha +108

  #3 pc 000000000223bb20  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable$VectorDrawableState.getAlpha +144

  #4 pc 00000000025c50e0  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.getAlpha +128

  #5 pc 00000000025c4f9c  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.getOpacity +124

  #6 pc 00000000025c1ea8  /memfd:jit-cache (deleted)
      android.widget.ImageView.isOpaque +152

  #7 pc 000000000227979c  /memfd:jit-cache (deleted)
      android.view.View.invalidateInternal +428

  #8 pc 00000000025c4790  /memfd:jit-cache (deleted)
      android.widget.ImageView.invalidateDrawable +256

  #9 pc 000000000224419c  /memfd:jit-cache (deleted)
      android.graphics.drawable.Drawable.invalidateSelf +156

  #10 pc 000000000260e710  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.setTintList +192

  #11 pc 00000000025d0094  /memfd:jit-cache (deleted)
      **android.graphics.drawable.Drawable.setTint +148**
```

### Description of Change

- Changes tinting of Androids SearchBar to unified setTint instead of
setColorFilter
-  Mutates the drawable before setting the tint.

### Issues Fixed
Issue is fixed with a custom handler for now.

Fixes dotnet#33070
kubaflo pushed a commit that referenced this pull request Jan 14, 2026
…tnet#33071)

App crashes because of changing a shared Drawable on the fly. I found
out it is the SearchBar. It only happens on Android and in large apps
with lots of screens. Unfortunately I can't reproduce it in the
TestSuite. But by reading the docs of Android this crash is common and
caused by changing a Drawable which is still referenced. With a custom
SearchBarHandler the issues is resolved.

```Backtrace (top frames):
  #00 pc 00000000006b8694  /system/lib64/libhwui.so
      android::getRootAlpha(_JNIEnv*, _jobject*, long) +4

  #1 pc 0000000002256c90  /memfd:jit-cache (deleted)
      art_jni_trampoline +112

  #2 pc 000000000223bc4c  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.-$$Nest$smnGetRootAlpha +108

  #3 pc 000000000223bb20  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable$VectorDrawableState.getAlpha +144

  #4 pc 00000000025c50e0  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.getAlpha +128

  #5 pc 00000000025c4f9c  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.getOpacity +124

  #6 pc 00000000025c1ea8  /memfd:jit-cache (deleted)
      android.widget.ImageView.isOpaque +152

  #7 pc 000000000227979c  /memfd:jit-cache (deleted)
      android.view.View.invalidateInternal +428

  #8 pc 00000000025c4790  /memfd:jit-cache (deleted)
      android.widget.ImageView.invalidateDrawable +256

  #9 pc 000000000224419c  /memfd:jit-cache (deleted)
      android.graphics.drawable.Drawable.invalidateSelf +156

  #10 pc 000000000260e710  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.setTintList +192

  #11 pc 00000000025d0094  /memfd:jit-cache (deleted)
      **android.graphics.drawable.Drawable.setTint +148**
```

### Description of Change

- Changes tinting of Androids SearchBar to unified setTint instead of
setColorFilter
-  Mutates the drawable before setting the tint.

### Issues Fixed
Issue is fixed with a custom handler for now.

Fixes dotnet#33070
kubaflo pushed a commit that referenced this pull request Jan 16, 2026
…tnet#33071)

App crashes because of changing a shared Drawable on the fly. I found
out it is the SearchBar. It only happens on Android and in large apps
with lots of screens. Unfortunately I can't reproduce it in the
TestSuite. But by reading the docs of Android this crash is common and
caused by changing a Drawable which is still referenced. With a custom
SearchBarHandler the issues is resolved.

```Backtrace (top frames):
  #00 pc 00000000006b8694  /system/lib64/libhwui.so
      android::getRootAlpha(_JNIEnv*, _jobject*, long) +4

  #1 pc 0000000002256c90  /memfd:jit-cache (deleted)
      art_jni_trampoline +112

  #2 pc 000000000223bc4c  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.-$$Nest$smnGetRootAlpha +108

  #3 pc 000000000223bb20  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable$VectorDrawableState.getAlpha +144

  #4 pc 00000000025c50e0  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.getAlpha +128

  #5 pc 00000000025c4f9c  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.getOpacity +124

  #6 pc 00000000025c1ea8  /memfd:jit-cache (deleted)
      android.widget.ImageView.isOpaque +152

  #7 pc 000000000227979c  /memfd:jit-cache (deleted)
      android.view.View.invalidateInternal +428

  #8 pc 00000000025c4790  /memfd:jit-cache (deleted)
      android.widget.ImageView.invalidateDrawable +256

  #9 pc 000000000224419c  /memfd:jit-cache (deleted)
      android.graphics.drawable.Drawable.invalidateSelf +156

  #10 pc 000000000260e710  /memfd:jit-cache (deleted)
      android.graphics.drawable.VectorDrawable.setTintList +192

  #11 pc 00000000025d0094  /memfd:jit-cache (deleted)
      **android.graphics.drawable.Drawable.setTint +148**
```

### Description of Change

- Changes tinting of Androids SearchBar to unified setTint instead of
setColorFilter
-  Mutates the drawable before setting the tint.

### Issues Fixed
Issue is fixed with a custom handler for now.

Fixes dotnet#33070
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[iOS 26] Navigation hangs after rapidly open and closing new page using Navigation.PushAsync

2 participants