Fix AOT trim warnings on net11.0 (iOS + Android) for Integration tests AOT failure - #36125
Conversation
iOS/MacCatalyst fix:
- Rename [Register("__UIGestureRecognizer")] to a unique name to avoid
duplicate ObjC class registration with the SDK's internal type.
- Remove redundant [Preserve(Conditional = true)] on Activated(). The
[Export("target:")] already makes it a static-registrar root under
full trimming. The [Preserve] generated a preserve descriptor in
apply-preserve-attribute.xml that the linker couldn't resolve against
UIKit.UIGestureRecognizer.Callback<T>, causing IL2009.
Android fix:
- Update expected AOT warning baseline for Android SDK 37.0.0:
- Method index shifted from |4_1 to |5_1
- New warnings for AndroidReflectionJniValueManager and
ManagedTypeManager constructors
Fixes: dotnet#36081
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36125Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36125" |
|
Hey there @@SubhikshaSf4851! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@SubhikshaSf4851 — new AI review results are available based on this last commit:
b276285. To request a fresh review after new comments or commits, comment/review rerun.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate Result: ⚠️ INCONCLUSIVE
Platform: IOS
⚠️ verify-tests-fail.ps1exited before writing a verification report. Diagnostics below.
Exit code: 3
Likely cause:
- Test detection failed — no runnable tests were found in the PR diff.
Gate output log (last 60 lines)
📁 Output directory: CustomAgentLogsTmp/PRState/36125/PRAgent/gate/verify-tests-fail
🔍 Detecting base branch and merge point...
No PR detected, scanning remote branches for closest base...
✅ Base branch: net11.0 (via closest-merge-base)
✅ Merge base commit: cfea02d9
(1 commits ahead of net11.0)
╔═══════════════════════════════════════════════════════════╗
║ FULL VERIFICATION MODE ║
╠═══════════════════════════════════════════════════════════╣
║ Fix files detected - will verify: ║
║ 1. Tests FAIL without fix ║
║ 2. Tests PASS with fix ║
╚═══════════════════════════════════════════════════════════╝
✅ Fix files (1):
- src/Controls/src/Core/Platform/iOS/CustomPressGestureRecognizer.cs
🔍 Auto-detecting test filter from changed test files...
⚠️ No tests detected in this PR.
Searched for: UI tests, unit tests, XAML tests, device tests
Consider adding tests via write-tests-agent.
📋 Pre-Flight — Context & Validation
Issue: #36081 - [ci-scan-net11] AOT macOS integration tests fail every build — IL2009 warning from apply-preserve-attribute.xml on iOS/MacCatalyst (net11.0)
PR: #36125 - Fix AOT trim warnings on net11.0 (iOS + Android) for Integration tests AOT failure
Platforms Affected: iOS, MacCatalyst, Android NativeAOT warning baseline
Files Changed: 2 implementation/test-infrastructure, 0 test
Key Findings
- The linked issue reports repeatable net11.0 AOT macOS integration failures:
IL2009fromapply-preserve-attribute.xmlforActivated(T)onUIKit.UIGestureRecognizer.Callback<T>. - The PR's iOS fix removes
[Preserve(Conditional = true)]fromCustomPressGestureRecognizer.Callback.Activatedand changes the nested callback ObjC registration from__UIGestureRecognizerto a unique MAUI-specific name. - The PR also updates Android NativeAOT expected warning messages after Android SDK warning text/signature drift.
- No PR inline review comments or prior ❌ review findings were found via the public API surfaces available in this unauthenticated environment.
- Impacted UI test categories: NONE for UI test category selection; this is an AOT/integration-test warning regression, not a UI behavior test-category change.
Code Review Summary
Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 1 | Suggestions: 0
Key code review findings:
⚠️ src/Controls/src/Core/Platform/iOS/CustomPressGestureRecognizer.cs:35-46— removing[Preserve(Conditional = true)]relies on[Export("target:")]/static registrar behavior to keep a selector-only callback rooted under linked/AOT builds; runtime selector dispatch should be explicitly verified.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #36125 | Remove conditional preserve from Activated, rename callback registration to a unique ObjC name, and update Android NativeAOT expected warning baseline. |
src/Controls/src/Core/Platform/iOS/CustomPressGestureRecognizer.cs, src/TestUtils/src/Microsoft.Maui.IntegrationTests/Utilities/BuildWarningsUtilities.cs |
Original PR; code review warning is selector callback preservation evidence gap. |
🔬 Code Review — Deep Analysis
Code Review — PR #36125
Independent Assessment
What this changes: Removes a conditional preserve marker from the iOS CustomPressGestureRecognizer selector callback, gives its ObjC registration a unique name, and updates the Android NativeAOT warning baseline.
Inferred motivation: Fix net11 AOT/integration-test failures caused by stale Android warnings and an iOS linker IL2009 warning from the old preserve descriptor.
Reconciliation with PR Narrative
Author claims: [Export("target:")] is sufficient to keep Activated, and the duplicate __UIGestureRecognizer registration caused the iOS warning; Android warnings changed with the SDK.
Agreement/disagreement: The duplicate registration explanation and Android baseline update match the diff. The preservation claim is plausible but is the main risk: the method is still invoked only via selector dispatch, so I would want evidence that the static registrar roots it under linked/AOT builds.
Prior Review Reconciliation
No prior ❌ Error findings found. REST review surfaces showed 0 reviews, 0 inline comments, and 3 issue comments without ❌/[major]/[moderate] findings.
Blast Radius Assessment
- Runs for all instances: No — only views with
PointerGestureRecognizeron iOS/MacCatalyst. - Startup impact: No direct startup impact.
- Static/shared state: No new static/shared state.
CI Status
- Required-check result: Undetermined via required-check command —
gh pr checks --requiredfailed due missing GitHub auth. - Classification: Pending/undetermined. REST check-runs for head
b276285...show relevant AOT macOS and RunOniOS NativeAOT jobs passing, but overallmaui-prand several jobs were still queued/in progress; one unrelated-lookingagentcheck was failed. - Action taken: Confidence capped low; no LGTM.
Findings
⚠️ Warning — Selector callback preservation relies on unproven registrar behavior
src/Controls/src/Core/Platform/iOS/CustomPressGestureRecognizer.cs:35-46
The old [Preserve(Conditional = true)] was removed from Activated, but this method is still reached only through Selector.GetHandle("target:") / [Export("target:")], not a managed call. If [Export] does not root the method in the linked/AOT case, pointer press gestures could fail at runtime even though the IL2009 warning disappears. Consider keeping preservation now that the ObjC registration name is unique, or document/verify the static-registrar rooting behavior with a linked/AOT runtime test.
Failure-Mode Probing
- Full trim/AOT selector dispatch: build warnings appear fixed, but runtime selector invocation is not proven by the baseline update alone.
- Duplicate ObjC registration: unique registration name should remove the collision with SDK
UIGestureRecognizer.Callback<T>. - Android warning drift: exact-message baseline remains intentionally strict; future SDK warning changes will fail tests again by design.
- Handler disconnect/reconnect: unchanged; weak-reference guards in the caller still prevent stale handler use.
Verdict: NEEDS_DISCUSSION
Confidence: low
Summary: The fix likely addresses the reported CI warnings, and the Android baseline update is straightforward. I would not mark LGTM while required CI is still pending/undetermined and the removed preserve attribute depends on registrar/linker behavior that should be explicitly verified.
🛠️ Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix-1 | Restore [Preserve(Conditional = true)] while keeping the PR's unique callback [Register]. |
1 file | Tests could not run because .dotnet/packs/Microsoft.Maui.Sdk is missing. The candidate directly tests whether duplicate ObjC registration, not Preserve itself, caused IL2009. |
|
| 2 | try-fix-2 | Add DynamicDependency from the action constructor to Callback.Activated; keep unique [Register] and no MAUI Preserve. |
1 file | Tests could not run because .dotnet/packs/Microsoft.Maui.Sdk is missing. Strongest theoretical alternative because it explicitly roots the selector method without the Preserve XML path. |
|
| 3 | try-fix-3 | Remove selector callback path and invoke the managed action directly from touch overrides. | 1 file | Tests could not run because .dotnet/packs/Microsoft.Maui.Sdk is missing. Expert self-review found a major behavior-timing risk. |
|
| 4 | try-fix-4 | Use targeted linker/trimmer descriptor rooting for the callback method. | 0 files | Rejected during expert review: no existing transitive descriptor packaging pattern in this repo area; speculative infrastructure wiring. | |
| PR | PR #36125 | Remove [Preserve], keep [Export], use unique callback [Register], update Android NativeAOT baseline. |
2 files | Original PR; gate could not build/run. Code review warning remains around Export-only selector rooting evidence. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| gpt-5.5 expert | 1 | Yes | Candidate 1: restore MAUI Preserve after unique registration. |
| gpt-5.5 expert | 2 | Yes | Candidate 2: use DynamicDependency instead of MAUI Preserve or Export-only rooting. |
| gpt-5.5 expert | 3 | Yes | Candidate 3: remove selector callback helper and invoke action directly from touch overrides. |
| claude-opus-4.6 | 4 | Yes | Suggested switching pointer handling to UIHoverGestureRecognizer; not applied because the bug is in the press recognizer path and this does not preserve secondary-button press semantics by itself. |
| claude-opus-4.7 | 4 | Yes | Suggested targeted linker root descriptor; recorded as candidate 4 and rejected as speculative without repo packaging pattern. |
| gpt-5.3-codex | 4 | Yes | Suggested ILLink descriptor XML; same disposition as candidate 4. |
| gpt-5.5 | 4 | Yes | Suggested using a different recognizer target instance with exported method; not applied because it retains selector rooting and is a variation on the same target/action mechanism rather than a stronger alternative to candidates 1/2. |
Exhausted: Yes
Selected Fix: None — no candidate passed tests or was demonstrably better than the PR's fix in this environment. Candidate #2 is the strongest theoretical alternative because it avoids both Export-only rooting and the MAUI Preserve XML path, but it remains unverified. Candidate #3 is not recommended due the major behavior-timing risk; candidate #4 is not recommended due speculative build/package wiring.
📝 Recommended PR Title & Description
Assessment: ✏️ Recommend updating — the description is detailed and accurate, but the title should include MacCatalyst and follow the platform/component formula.
Recommended title
[iOS/MacCatalyst/Android] IntegrationTests: Fix net11.0 NativeAOT trim warning baselines
Recommended description
## Problem 1: iOS/MacCatalyst — IL2009 trim warning
When building a fully-trimmed `net11.0-ios` or `net11.0-maccatalyst` MAUI template app, the linker emits:
warning IL2009: Could not find method 'System.Void Activated(T)' on type 'UIKit.UIGestureRecognizer.Callback`1'
This generates an `apply-preserve-attribute.xml` file in the linker cache. The AOT test infrastructure (`BuildWarningsUtilities.AssertWarnings`) treats any unexpected warning file as a failure, and the iOS/MacCatalyst expected baseline is empty — so the test fails.
### Root Cause
`CustomPressGestureRecognizer.Callback` has `[Preserve(Conditional = true)]` on its `Activated` method. This tells the linker to emit a preserve descriptor in `apply-preserve-attribute.xml`. The descriptor references the declaring type by its ObjC registration name `__UIGestureRecognizer`.
In .NET 11, the iOS SDK refactored its internal `UIGestureRecognizer` callback to a generic type (`UIKit.UIGestureRecognizer.Callback<T>`). When the linker resolves the preserve descriptor by the shared ObjC name `__UIGestureRecognizer`, it now binds to the SDK's generic type — which has no `Activated(T)` member matching the expected signature → `IL2009`.
This did not happen on `main`/`candidate` (.NET 10 SDK) because the SDK's callback was non-generic there.
### Fix
1. **Remove `[Preserve(Conditional = true)]`** — The `[Export("target:")]` attribute already makes `Activated()` a static-registrar root under full trimming. The `[Preserve]` is redundant and was the source of the broken descriptor.
2. **Rename `[Register("__UIGestureRecognizer")]` to a unique name** — Eliminates the latent duplicate ObjC class name collision with the SDK's internal type.
## Problem 2: Android — stale warning baseline
The Android SDK updated from `36.x` to `37.0.0-ci.main.51` on `net11.0`, which changed the warning signatures:
| Change | Old | New |
|---|---|---|
| Method index | `g__MakeGenericType|4_1` | `g__MakeGenericType|5_1` |
| New warnings | — | `AndroidReflectionJniValueManager` ctor, `ManagedTypeManager` ctor |
The test expected the old signatures and failed with `"Expected warning message was not found"`.
Fixes: https://github.com/dotnet/maui/issues/36081
🏁 Report — Final Recommendation
Comparative Analysis — PR #36125
Candidate Ranking
| Rank | Candidate | Regression result | Assessment |
|---|---|---|---|
| 1 | pr-plus-reviewer |
Winner. The expert reviewer found no actionable issues, so this is identical to the PR fix but with expert review applied. It is the most localized fix that matches the root cause and avoids speculative additional trim annotations or gesture behavior changes. | |
| 2 | pr |
Equivalent code to pr-plus-reviewer, but ranked below the reviewed candidate label because Phase 1 explicitly validated that no reviewer changes were needed. |
|
| 3 | try-fix-2 |
Strongest theoretical alternative: adds DynamicDependency to explicitly root Callback.Activated while avoiding the MAUI Preserve XML path. It is not selected because it adds unverified trim annotation surface to solve a concern the expert review did not find actionable. |
|
| 4 | try-fix-1 |
Keeps explicit preservation after the unique registration rename. It may be viable, but it reintroduces the same MAUI Preserve descriptor mechanism involved in the original failure, making it less attractive than the PR's descriptor-removal approach. | |
| 5 | try-fix-3 |
Removes selector dispatch entirely, but changes callback timing by invoking the action directly from touch overrides. Its own expert self-review found a major gesture timing/simultaneous-recognition risk. | |
| 6 | try-fix-4 |
Rejected design-level candidate. It has no final diff and would require speculative linker descriptor/package wiring not established in this repo area. |
Winning Candidate
pr-plus-reviewer is the single winning candidate.
The expert review produced no inline findings and no actionable sandbox feedback, so pr-plus-reviewer is identical to the submitted PR fix. It wins because it directly addresses both documented warning failures with the smallest behavioral surface: iOS/MacCatalyst keeps the existing target/action gesture callback path while removing the problematic preserve descriptor source and duplicate ObjC registration name; Android updates only the strict expected warning baseline.
Test Result Handling
No candidate passed regression tests in this environment, but none failed regression tests either. The PR gate and try-fix runs were inconclusive/blocked by environment issues, so the ranking is based on code-review merit, blast radius, and candidate risk rather than treating any candidate as test-failed.
Why the Alternatives Did Not Win
try-fix-2 is the best fallback if maintainers want explicit trimmer rooting beyond [Export], but it remains unverified and the expert reviewer did not identify missing rooting as an actionable defect. try-fix-1 preserves more aggressively but brings back the descriptor mechanism implicated in the original warning. try-fix-3 changes gesture dispatch semantics, and try-fix-4 has no concrete implementation.
🧭 Next Steps — review latest findings
No alternative fix was selected for this run. Review the session findings and CI results before merging.
|
@simonrozsival @jonathanpeppers this look alright? |
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 whether this change resolves your issue. Thank you!
Problem 1: iOS/MacCatalyst — IL2009 trim warning
When building a fully-trimmed
net11.0-iosornet11.0-maccatalystMAUI template app, the linker emits:warning IL2009: Could not find method 'System.Void Activated(T)' on type 'UIKit.UIGestureRecognizer.Callback`1'
This generates an
apply-preserve-attribute.xmlfile in the linker cache. The AOT test infrastructure (BuildWarningsUtilities.AssertWarnings) treats any unexpected warning file as a failure, and the iOS/MacCatalyst expected baseline is empty — so the test fails.Root Cause
CustomPressGestureRecognizer.Callbackhas[Preserve(Conditional = true)]on itsActivatedmethod. This tells the linker to emit a preserve descriptor inapply-preserve-attribute.xml. The descriptor references the declaring type by its ObjC registration name__UIGestureRecognizer.In .NET 11, the iOS SDK refactored its internal
UIGestureRecognizercallback to a generic type (UIKit.UIGestureRecognizer.Callback<T>). When the linker resolves the preserve descriptor by the shared ObjC name__UIGestureRecognizer, it now binds to the SDK's generic type — which has noActivated(T)member matching the expected signature →IL2009.This did not happen on
main/candidate(.NET 10 SDK) because the SDK's callback was non-generic there.Fix
[Preserve(Conditional = true)]— The[Export("target:")]attribute already makesActivated()a static-registrar root under full trimming. The[Preserve]is redundant and was the source of the broken descriptor.[Register("__UIGestureRecognizer")]to a unique name — Eliminates the latent duplicate ObjC class name collision with the SDK's internal type.Problem 2: Android — stale warning baseline
The Android SDK updated from
36.xto37.0.0-ci.main.51onnet11.0, which changed the warning signatures:g__MakeGenericType|4_1g__MakeGenericType|5_1AndroidReflectionJniValueManagerctor,ManagedTypeManagerctorThe test expected the old signatures and failed with
"Expected warning message was not found".Fixes: #36081