Skip to content

[Net11]Improve label mapping performance and ensure complete coverage including ToPlatform and subsequent property changes - #35892

Merged
kubaflo merged 26 commits into
dotnet:net11.0from
devanathan-vaithiyanathan:net11-label-perf-imp
Aug 1, 2026
Merged

[Net11]Improve label mapping performance and ensure complete coverage including ToPlatform and subsequent property changes#35892
kubaflo merged 26 commits into
dotnet:net11.0from
devanathan-vaithiyanathan:net11-label-perf-imp

Conversation

@devanathan-vaithiyanathan

Copy link
Copy Markdown
Contributor

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

  • The iOS Label performance was improved in PR Improve iOS Label performance #30864. In that PR, the Label and Entry Feature Matrix test sample and script were modified, which caused discrepancies in the expected images due to changes which is due to test sample's default property values. In this PR, I updated the test sample and re-saved the images accordingly.

  • Windows - The Entry is now unfocused, so I re-saved the latest image.

  • Android - I modified the test sample by altering the default values and re-saved two images.

Additionally, while working on the test sample changes, I identified and fixed issues in FormattedStringExtensions.

These updates improve formatted text rendering on iOS by correctly propagating span properties (font, character spacing, and line-break settings) from the label to each span. The layout logic is also more robust, falling back to MAUI’s calculated size when iOS has not yet provided a valid label size, preventing incorrect text positioning and rendering issues.

Issues Fixed

Contributing to #30864

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35892

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35892"

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Jun 12, 2026
@kubaflo

kubaflo commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

AI code review refresh for net11.0 target

Head reviewed: aedd137875fb05f9b16dcd5f09f2e8505ce97aac (commit "Candidate failure changes added", 2026-06-12)
Target branch: net11.0 · State: OPEN, Draft (WIP)

Verdict: Needs discussion (WIP)

Solid, well-reasoned approach with a large global blast radius and some scope creep. No definite bugs found in the changed source, but several items below warrant confirmation before this leaves WIP.

What the change does (independent reading of the diff)

  • Introduces a high‑priority TextMapper sub‑mapper for Label/Entry (and reorders TextButtonMapper for Button) so Text is mapped before dependent text‑style props (Font, CharacterSpacing, LineHeight, TextDecorations, alignment, TextColor). On iOS these props mutate the AttributedText produced by MapText, so ordering matters.
  • Guards MapText/MapFormatting/MapFormattedText/MapFont with IsConnectingHandler() so the full attributed‑string is not rebuilt repeatedly during handler connection (the performance win). At runtime (non‑connect) behavior is preserved.
  • EntryHandler.iOS.MapFormatting now routes through handler.UpdateValue(nameof(...)) instead of calling UpdateMaxLength/UpdateCharacterSpacing/UpdateHorizontalTextAlignment directly — single source of truth via the mapper.
  • FormattedStringExtensions.ToNSAttributedString gains a defaultLineBreakMode optional param (+ a #if !MACOS / #else NSLineBreakMode branch); PublicAPI.Unshipped updated with proper *REMOVED*+added entries for net‑ios and net‑maccatalyst.
  • New EntryHandler.iOS.MapBackground (image/null/empty handling); new Entry.MapTextTransform.

Prior review reconciliation

No prior PR reviews, review comments, or earlier fleet markers exist (only the dogfood bot comment). Nothing to reconcile; no outstanding ❌ findings.

CI status

Build 1461094 still in progress. Most build/pack/Helix/integration jobs pass; 3 iOS jobs fail: RunOniOS_MauiRelease, RunOniOS_MauiReleaseTrimFull, RunOniOS_MauiReleaseTrimFull_CoreCLR. Root cause = HybridWebView IL2026 trim‑analysis errors → NETSDK1144 "Optimizing assemblies for size failed" while building a template app. This is a trimming/template‑infra issue unrelated to the Label/Entry/Button mapping changes and appears as pre‑existing net11 preview‑ILLink noise. iOS Debug + NativeAOT + Blazor iOS all pass.

Blast radius / failure‑mode probing

The Core handler files are cross‑platform, so the mapper reordering + connection guards affect every Label, Entry, and Button instance on all platforms, not just iOS.

  • Because MapText now skips MapFormatting during connect, correctness on Android/Windows/Tizen relies entirely on the individual property maps in TextMapper running on connect. They are present, so this should be safe — please confirm with Android/Windows device + FeatureMatrix runs.
  • Verified IsConnectingHandler() exists for both IElement and IElementHandler (internal extensions), and Paint.IsNullOrEmpty() is null‑safe, so entry.Background.IsNullOrEmpty() won't NRE.

Concise findings

  1. Scope creep (discuss/split): EntryHandler.iOS.MapBackground and Entry now overriding Background mapping is unrelated to "label mapping performance." It changes iOS Entry background behavior (previously ViewHandler.MapBackground). Justify or split into its own PR.
  2. macOS PublicAPI gap (verify): the new #else (NSLineBreakMode) branch implies a MACOS compile path, but only net‑ios/net‑maccatalyst PublicAPI files were updated. Controls.Core has no net‑macos PublicAPI baseline, so likely a no‑op — confirm no MACOS target builds this with the new default‑param signature.
  3. Snapshot churn (verify): large number of iOS/Android/Windows/Mac snapshot updates. Confirm these are intended re‑renders, not silent regressions, before un‑drafting.
  4. Nit: several changed files now lack a trailing newline (\ No newline at end of file).
  5. Tests: coverage is via FeatureMatrix UI tests + snapshots; consider a focused unit test asserting Text precedes dependent props in the mapper order to lock in the invariant.

Confidence

Medium. Source‑level reasoning is high‑confidence (changes read cleanly, key invariants verified). Runtime/visual correctness across non‑iOS platforms and snapshot validity are unverified here and depend on the in‑progress CI + device/UI tests.


Automated non‑binding review. This is not an approval and does not request changes; a human maintainer owns the merge decision.

@vishnumenon2684 vishnumenon2684 changed the title [WIP][Net11]Improve label mapping performance and ensure complete coverage including ToPlatform and subsequent property changes [Net11]Improve label mapping performance and ensure complete coverage including ToPlatform and subsequent property changes Jun 15, 2026
@devanathan-vaithiyanathan
devanathan-vaithiyanathan marked this pull request as ready for review June 15, 2026 11:41

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR #35892 — [Net11] Improve Label mapping performance + complete coverage (ToPlatform & subsequent property changes)

Verdict: NEEDS_DISCUSSION (confidence: medium) — HEAD aedd137. The core change is correct — the deep reviews (opus-4.8, opus-4.6) confirmed no Label/Entry mappings were dropped and runtime (non-connect) behavior is preserved. Three precise, non-blocking items remain (2 inline + an API note); CI red is the known flake set.

What it does (sound)

Consolidates the Label/Entry/Button mappers so Text is mapped first (into a dedicated TextMapper) and IsConnectingHandler() guards skip redundant re-mapping during connect — avoiding repeated AttributedText rebuilds on iOS. The ToNSAttributedString LineBreakMode plumbing is a real fix. Mapper consolidation and ordering were independently vetted as coherent by 3 of 4 models.

Findings (inline)

  • ⚠️ EntryHandler.cs:30 — the Text-ordering comment is inaccurate (HorizontalTextAlignment/VerticalTextAlignment are declared before Text) and the resulting order diverges from Label/Button. Benign at runtime, but misleading in an ordering-focused PR. (opus-4.8 + opus-4.6 consensus.)
  • 💡 EntryHandler.cs:39EntryPriorityMapper is now orphaned dead code (confirmed via git grep at HEAD); remove it.
  • 💡 FormattedStringExtensions.cs:43 — adding the optional defaultLineBreakMode is a binary-breaking change to a shipped public Platform extension; handled via *REMOVED* in PublicAPI (gate satisfied) — flagging to confirm the break is intentional vs. adding a preserving overload.

Dropped (false positive)

A model flagged Entry.Mapper.cs MapTextTransform → MapText as "bypassing UpdateValue/user customizations." Dropped: the ReplaceMapping(TextTransform, MapText) wiring is pre-existing — the PR only adds an IsConnectingHandler() guard around it (a perf optimization), and opus-4.8 confirmed runtime behavior is preserved. Not a new defect.

CI — known flakes

Red legs are the universal set seen across all PRs today: Build Analysis, AOT macOS, RunOniOS_MauiRelease/MauiReleaseTrimFull/TrimFull_CoreCLR. The Debug-passes / Release+Trim+AOT-fails pattern matches trimming-baseline noise; RunOniOS_MauiDebug, MauiRelease_CoreCLR, MauiNativeAOT pass. Not PR-caused.

Comment thread src/Core/src/Handlers/Entry/EntryHandler.cs
Comment thread src/Core/src/Handlers/Entry/EntryHandler.cs
Comment thread src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs Outdated

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR #35892 - [Net11] Improve Label mapping performance + complete coverage

Re-review at HEAD 0c2e170 (prior review: NEEDS_DISCUSSION at aedd137). Consensus verdict from all 4 models: NEEDS_CHANGES (high confidence).

What changed

This PR continues the iOS Label/Entry/Button mapping-performance work from #30864. It introduces a high-priority TextMapper so Text is mapped before dependent text-style properties, guards MapText/MapFormatting with IsConnectingHandler() to avoid rebuilding the iOS AttributedText repeatedly during handler connection, routes EntryHandler.iOS.MapFormatting through handler.UpdateValue(...), fixes span-property propagation in FormattedStringExtensions, and re-saves a large set of Android/iOS/Mac/Windows FeatureMatrix snapshots (587 additions across 104 files).

Consensus verdict: NEEDS_CHANGES

The latest push introduced a build-breaking regression. EntryHandler.iOS.cs now declares MapBackground(IEntryHandler, IEntry) twice with an identical signature and body (lines 56-78 and 80-102) - a CS0111 "member already defined" error. The file compiles for both iOS and MacCatalyst, which directly explains the red Build macOS (Debug/Release) and Pack macOS legs in CI; the duplicate did not exist at the prior head aedd137. All four models flagged this independently, and it is the single blocking issue: remove one of the two copies and the build should recover.

Prior concerns (still unaddressed, not re-filed inline)

Two non-blocking items from the prior NEEDS_DISCUSSION review remain, but are already documented in the PR discussion, so they are not re-filed as fresh inline comments: (1) EntryPriorityMapper (EntryHandler.cs:20-23) is orphaned dead code - it is no longer passed to the Mapper constructor (which chains TextMapper -> ViewMapper) and its only entry, MaxLength, is already present in TextMapper; and (2) the comment at EntryHandler.cs:30-31 states Text is mapped before HorizontalTextAlignment, yet HorizontalTextAlignment/VerticalTextAlignment (lines 28-29) are declared before Text (line 32) - harmless for iOS UITextField (alignment is view-level) but misleading in an ordering-focused PR. The core mapper consolidation itself was independently vetted as correct: no mappings were dropped and runtime (non-connect) behavior is preserved.

Comment thread src/Core/src/Handlers/Entry/EntryHandler.iOS.cs Outdated

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR #35892 — [Net11] Improve Label mapping performance + complete coverage

Cross-pollination synthesis at HEAD 82b65cc (4 independent model reviews: gpt-5.5, claude-opus-4.8, claude-opus-4.6, gemini-3.1-pro).

Verdict: NEEDS_CHANGES (confidence: high)

Model Verdict Confidence
gpt-5.5 NEEDS_CHANGES high
claude-opus-4.6 NEEDS_CHANGES high
claude-opus-4.8 NEEDS_DISCUSSION medium
gemini-3.1-pro LGTM high

Per the most-conservative rule (NEEDS_CHANGES > NEEDS_DISCUSSION > LGTM) the synthesized verdict is NEEDS_CHANGES. The single blocking item (Finding 1) is a concrete, independently-verified test defect; the lone LGTM (gemini) simply did not catch it. 3 of 4 models did not give LGTM.

What changed

This PR continues the iOS Label/Entry/Button mapping-performance work from #30864. It introduces a high-priority TextMapper (so Text is mapped before dependent text-style properties), guards MapText/MapTextTransform with IsConnectingHandler() to avoid rebuilding iOS AttributedText repeatedly during handler connection, fixes span-property propagation in FormattedStringExtensions (adding a LineBreakMode parameter), and re-saves a large set of Android/iOS/Mac/Windows FeatureMatrix snapshots (563 additions / 98 deletions across 104 files).

Prior blocker resolved

The previous review (at an earlier head) blocked on a duplicate MapBackground(IEntryHandler, IEntry) in EntryHandler.iOS.cs causing a CS0111 build break. At HEAD 82b65cc that method is declared once (line 56) — the regression is fixed, and none of the 4 models re-flagged it.

Synthesized findings (clustered, validated against HEAD)

All findings below reference code present at HEAD 82b65cc; none were dropped. Duplicates across models were merged into one comment per unique file:line:concern.

  1. [Error] ButtonFeatureTests.cs:11MainLabel constant targets the wrong element. const string MainLabel = "ClickedEventLabel" taps a label with no gesture recognizer, so the page is never recreated. The recreating gesture is on the AutomationId="MainLabel" label (MainLabel_TappedContent = new ContentView()). Every App.Tap(MainLabel) no-ops, so the new "verify initial mappers after recreation" coverage silently re-checks stale page state. Fix: = "MainLabel". (gpt-5.5, opus-4.6)blocking.

  2. [Warning] FormattedStringExtensions.cs:43/86 — binary-breaking public API change. The public ToNSAttributedString(FormattedString…) and (Span…) overloads gain an optional trailing LineBreakMode defaultLineBreakMode. Source-compatible but ABI-breaking vs PublicAPI.Shipped.txt. The API gate is satisfied (*REMOVED* + new entries in both PublicAPI.Unshipped.txt files) and net11 permits breaks, but partner/syncfusion consumers are exactly who is affected — confirm with a maintainer and document it. A naive preserving overload would be ambiguous (CS0121). (opus-4.8, gemini) — needs discussion.

  3. [Warning] EntryHandler.cs:20-23EntryPriorityMapper is orphaned dead code. No longer passed to any mapper (the Mapper now chains TextMapper, ViewMapper) and its only entry (MaxLength) already lives in TextMapper. Remove the block. (opus-4.8, opus-4.6, gemini)

  4. [Suggestion] EntryHandler.cs:30 — inaccurate ordering comment. Comment claims Text is mapped before HorizontalTextAlignment, but alignment entries (lines 28-29) precede Text (line 32); it also references LineHeight/Decorations that Entry's mapper lacks (copied from LabelHandler). Benign at runtime; reorder to match Label/Button or fix the comment. (opus-4.8, opus-4.6)

  5. [Nit] EntryHandler.cs (+ Entry.Mapper.cs, Entry.iOS.cs, Label.Mapper.cs) — missing trailing newline. Non-blocking; .editorconfig does not enforce it; dotnet format restores it. (opus-4.8)

  6. [Suggestion] Entry.Mapper.cs:46-55 — extend the IsConnectingHandler guard to siblings. Button.Mapper.cs, Editor.Mapper.cs, SearchBar.Mapper.cs map both Text and TextTransform to MapText, so MapText runs twice at connect — the same guard would help. Out of scope for this PR; follow-up. (gemini)

Required to clear NEEDS_CHANGES

Fix Finding 1 (the MainLabel constant). Findings 3-4 are recommended cleanup for an ordering/perf-focused PR; Finding 2 needs a maintainer sign-off on the intentional binary break; Findings 5-6 are non-blocking.


public class ButtonFeatureTests : _GalleryUITest
{
const string MainLabel = "ClickedEventLabel";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 AI-Generated Review (multi-model: GPT-5.5, Claude Opus 4.8, Claude Opus 4.6, Gemini 3.1 Pro)

[Error] MainLabel constant targets the wrong element — page recreation never triggers, so the new Button coverage is ineffective

const string MainLabel = "ClickedEventLabel"; points at the event-result label (AutomationId="ClickedEventLabel", ButtonControlPage.xaml lines 66-67), which has no gesture recognizer. The label that actually recreates the page has AutomationId="MainLabel" (line 59) with <TapGestureRecognizer Tapped="MainLabel_Tapped"/> (line 61); MainLabel_Tapped sets Content = new ContentView() to rebuild the page (ButtonControlPage.xaml.cs lines 58-62).

Because every new App.Tap(MainLabel) call therefore taps ClickedEventLabel (a no-op), the page is never recreated and the "verify initial mappers after recreation" assertions re-check the same already-loaded page instead of a fresh one — silently defeating the coverage this PR adds. EntryFeatureTests and LabelFeatureTests correctly use "MainLabel".

Fix: const string MainLabel = "MainLabel";

Independently flagged by gpt-5.5 and claude-opus-4.6; verified against HEAD 82b65cc.

Comment thread src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs Outdated
Comment thread src/Core/src/Handlers/Entry/EntryHandler.cs Outdated
Comment thread src/Core/src/Handlers/Entry/EntryHandler.cs
Comment thread src/Core/src/Handlers/Entry/EntryHandler.cs Outdated
Comment thread src/Controls/src/Core/Entry/Entry.Mapper.cs
@kubaflo

This comment has been minimized.

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jun 24, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Expert Review — 2 findings

See inline comments for details.

TextTransform defaultTextTransform = TextTransform.Default)
=> formattedString.ToNSAttributedString(fontManager, defaultLineHeight, defaultHorizontalAlignment, defaultFont, defaultColor, defaultTextTransform, LineBreakMode.WordWrap, defaultCharacterSpacing: 0d);
TextTransform defaultTextTransform = TextTransform.Default,
LineBreakMode defaultLineBreakMode = LineBreakMode.WordWrap)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[major] Public API Surface Design — Adding defaultLineBreakMode as an optional trailing parameter replaces the shipped 7-parameter ToNSAttributedString signatures (the PR also marks them *REMOVED* in the iOS and MacCatalyst PublicAPI files). Optional parameters are baked into call sites, so existing compiled libraries that call the old overload will look for the old metadata signature and fail with MissingMethodException after upgrading. Preserve the exact shipped overloads as forwarders, and expose the new line-break option through a binary-compatible overload shape rather than removing the shipped methods.

#endif
EntryHandler.Mapper.ReplaceMapping<Entry, IEntryHandler>(nameof(Text), MapText);
EntryHandler.Mapper.ReplaceMapping<Entry, IEntryHandler>(nameof(TextTransform), MapText);
EntryHandler.Mapper.ReplaceMapping<Entry, IEntryHandler>(nameof(TextTransform), MapTextTransform);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[moderate] Android Platform / Mapper Lifecycle — This changes the TextTransform mapping to skip MapText while the legacy EntryHandler is connecting, but the Material3 Android mapping just below still uses EntryHandler2.Mapper.ReplaceMapping(... nameof(TextTransform), MapText). With RuntimeFeature.IsMaterial3Enabled, the TextTransform mapper is still appended to the handler and runs during initial UpdateProperties after the text/formatting mappers, so Material3 entries keep the old connection-time second text assignment that this PR is trying to remove. Add an EntryHandler2-compatible MapTextTransform overload and use it for the Material3 mapping as well.

@MauiBot MauiBot added s/agent-gate-failed AI could not verify tests catch the bug s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Jun 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 41 out of 137 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

src/Controls/src/Core/Entry/Entry.Mapper.cs:50

  • MapTextTransform is used for both EntryHandler (non-Material) and EntryHandler2 (Material3). With the current signature (IEntryHandler,…), the call to MapText(handler, entry) resolves to MapText(IEntryHandler,…) even when the mapper is running for EntryHandler2, bypassing the EntryHandler2-specific MapText overload (which updates handler.PlatformView.EditText). Adding an EntryHandler2 overload here ensures the Material3 path uses the correct platform view.
		static void MapTextTransform(IEntryHandler handler, Entry entry)
		{
			if (entry.IsConnectingHandler())
			{
				// If we're connecting the handler, we don't want to map the text multiple times.

src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs:55

  • On Android Material3 (SearchBarHandler2), TextTransform is remapped to MapTextTransform(ISearchBarHandler,…). That causes MapText(handler, searchBar) to bind to MapText(ISearchBarHandler,…) (SearchViewExtensions) instead of the SearchBarHandler2-specific MapText overload (which updates handler.PlatformView.EditText). Add a SearchBarHandler2 overload for MapTextTransform so the Material3 mapping uses the correct platform view.
		static void MapTextTransform(ISearchBarHandler handler, SearchBar searchBar)
		{
			if (searchBar.IsConnectingHandler())
			{
				// If we're connecting the handler, we don't want to map the text multiple times.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 41 out of 137 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

src/Controls/tests/TestCases.HostApp/FeatureMatrix/Label/LabelOptionsPage.xaml.cs:251

  • SimpleFormattedTextCheckBox_CheckedChanged should also keep the formatted-text options mutually exclusive; otherwise FormattedTextCheckBox can remain checked even though this handler overwrote the formatted text.
    src/Controls/tests/TestCases.HostApp/FeatureMatrix/Label/LabelOptionsPage.xaml.cs:240
  • OnFormattedTextChanged and SimpleFormattedTextCheckBox_CheckedChanged can both be checked at the same time, but they both write to the single FormattedText property. That can leave the UI in an inconsistent state (both checkboxes checked while only one formatted-text choice is actually applied). Consider making these mutually exclusive by unchecking SimpleFormattedTextCheckBox when FormattedTextCheckBox is checked.

This issue also appears on line 242 of the same file.
src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/EntryFeatureTests.cs:1178

  • Tapping hard-coded coordinates to clear focus is brittle (window size/scale/layout changes can make (50,50) hit an unintended element). Since this page exposes a stable AutomationId (MainLabel), prefer tapping that to unfocus the Entry on Windows.

@devanathan-vaithiyanathan

Copy link
Copy Markdown
Contributor Author

Looks like the build failed - could you please check?

@kubaflo , I have addressed the build failures

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 40 out of 136 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/EntryFeatureTests.cs:199

  • Avoid leaving commented-out test code in the test flow. If the pre-focus screenshot is intentionally skipped due to a known issue, keep only an explanatory comment (without the commented-out method call).
    src/Controls/tests/TestCases.HostApp/FeatureMatrix/Label/LabelOptionsPage.xaml.cs:252
  • SimpleFormattedTextCheckBox_CheckedChanged only handles the checked state; unchecking leaves the view model in a formatted-text state with Text still null, which makes the option hard to toggle/compose with other options.
    src/Controls/tests/TestCases.HostApp/FeatureMatrix/Label/LabelViewModel.cs:11
  • LabelViewModel no longer initializes either Text or FormattedText, which makes the Label Feature Matrix page render blank until the user opens Options. Consider setting a default Text value so the sample has visible content on first load (and tests/pages relying on initial text don’t regress).

@vishnumenon2684

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

@kubaflo

This comment has been minimized.

@kubaflo

This comment has been minimized.

@kubaflo

This comment has been minimized.

@github-actions

This comment has been minimized.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

AI Review Summary

@devanathan-vaithiyanathan — new AI review results are available based on this last commit: 17c9127.

Gate Partial Confidence Low Platform iOS


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ❌ FAILED

Platform: IOS · Base: net11.0 · Merge base: d07c3b24

🩺 Test does not reproduce the bug — ran the same in both states (PASS without fix, PASS with fix). The repro test is not exercising the issue. Strengthen the test before reviewing the fix.

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ ButtonFeatureTests ButtonFeatureTests ❌ PASS — 2598s ✅ PASS — 787s
🖥️ EntryFeatureTests EntryFeatureTests ❌ PASS — 2007s ✅ PASS — 690s
🔴 Without fix — 🖥️ ButtonFeatureTests: PASS ❌ · 2598s

(no coded error found; showing last 1200 chars)

s]
>>>>> 7/30/2026 4:23:27 PM VerifyVSM_Button_ResetWhileDisabled Start
>>>>> 7/30/2026 4:23:30 PM VerifyVSM_Button_ResetWhileDisabled Stop
  Passed VerifyVSM_Button_ResetWhileDisabled [3 s]
>>>>> 7/30/2026 4:23:30 PM VerifyVSM_Button_PressedAndReleasedWhileDisabled Start
>>>>> 7/30/2026 4:23:33 PM VerifyVSM_Button_PressedAndReleasedWhileDisabled Stop
  Passed VerifyVSM_Button_PressedAndReleasedWhileDisabled [2 s]
>>>>> 7/30/2026 4:23:33 PM VerifyVSM_Button_DisableAndEnable Start
>>>>> 7/30/2026 4:23:36 PM VerifyVSM_Button_DisableAndEnable Stop
  Passed VerifyVSM_Button_DisableAndEnable [3 s]
>>>>> 7/30/2026 4:23:36 PM VerifyVSM_Button_DisableAndEnableWhilePressedAndReleased Start
>>>>> 7/30/2026 4:23:40 PM VerifyVSM_Button_DisableAndEnableWhilePressedAndReleased Stop
  Passed VerifyVSM_Button_DisableAndEnableWhilePressedAndReleased [4 s]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/ButtonFeatureTests.trx
Test Run Successful.
Total tests: 84
     Passed: 84
 Total time: 12.3204 Minutes
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/ButtonFeatureTests.trx
🟢 With fix — 🖥️ ButtonFeatureTests: PASS ✅ · 787s

(no coded error found; showing last 1200 chars)

s]
>>>>> 7/30/2026 5:37:11 PM VerifyVSM_Button_ResetWhileDisabled Start
>>>>> 7/30/2026 5:37:14 PM VerifyVSM_Button_ResetWhileDisabled Stop
  Passed VerifyVSM_Button_ResetWhileDisabled [3 s]
>>>>> 7/30/2026 5:37:14 PM VerifyVSM_Button_PressedAndReleasedWhileDisabled Start
>>>>> 7/30/2026 5:37:17 PM VerifyVSM_Button_PressedAndReleasedWhileDisabled Stop
  Passed VerifyVSM_Button_PressedAndReleasedWhileDisabled [2 s]
>>>>> 7/30/2026 5:37:17 PM VerifyVSM_Button_DisableAndEnable Start
>>>>> 7/30/2026 5:37:20 PM VerifyVSM_Button_DisableAndEnable Stop
  Passed VerifyVSM_Button_DisableAndEnable [3 s]
>>>>> 7/30/2026 5:37:20 PM VerifyVSM_Button_DisableAndEnableWhilePressedAndReleased Start
>>>>> 7/30/2026 5:37:24 PM VerifyVSM_Button_DisableAndEnableWhilePressedAndReleased Stop
  Passed VerifyVSM_Button_DisableAndEnableWhilePressedAndReleased [4 s]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/ButtonFeatureTests.trx
Test Run Successful.
Total tests: 84
     Passed: 84
 Total time: 11.4841 Minutes
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/ButtonFeatureTests.trx
🔴 Without fix — 🖥️ EntryFeatureTests: PASS ❌ · 2007s

(no coded error found; showing last 1200 chars)

dToInvalid Start
>>>>> 7/30/2026 5:01:56 PM VerifyVSM_Entry_ValidToInvalid Stop
  Passed VerifyVSM_Entry_ValidToInvalid [9 s]
>>>>> 7/30/2026 5:01:56 PM VerifyVSM_Entry_Completed Start
>>>>> 7/30/2026 5:02:01 PM VerifyVSM_Entry_Completed Stop
  Passed VerifyVSM_Entry_Completed [4 s]
>>>>> 7/30/2026 5:02:01 PM VerifyVSM_Entry_CompletedAndReset Start
>>>>> 7/30/2026 5:02:06 PM VerifyVSM_Entry_CompletedAndReset Stop
  Passed VerifyVSM_Entry_CompletedAndReset [5 s]
>>>>> 7/30/2026 5:02:06 PM VerifyVSM_Entry_CompletedAndRefocused Start
>>>>> 7/30/2026 5:02:11 PM VerifyVSM_Entry_CompletedAndRefocused Stop
  Passed VerifyVSM_Entry_CompletedAndRefocused [5 s]
>>>>> 7/30/2026 5:02:11 PM VerifyVSM_Entry_DisableWhileCompleted Start
>>>>> 7/30/2026 5:02:17 PM VerifyVSM_Entry_DisableWhileCompleted Stop
  Passed VerifyVSM_Entry_DisableWhileCompleted [5 s]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/EntryFeatureTests.trx
Test Run Successful.
Total tests: 74
     Passed: 74
 Total time: 9.3061 Minutes
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/EntryFeatureTests.trx
🟢 With fix — 🖥️ EntryFeatureTests: PASS ✅ · 690s

(no coded error found; showing last 1200 chars)

dToInvalid Start
>>>>> 7/30/2026 5:48:33 PM VerifyVSM_Entry_ValidToInvalid Stop
  Passed VerifyVSM_Entry_ValidToInvalid [8 s]
>>>>> 7/30/2026 5:48:33 PM VerifyVSM_Entry_Completed Start
>>>>> 7/30/2026 5:48:37 PM VerifyVSM_Entry_Completed Stop
  Passed VerifyVSM_Entry_Completed [4 s]
>>>>> 7/30/2026 5:48:37 PM VerifyVSM_Entry_CompletedAndReset Start
>>>>> 7/30/2026 5:48:43 PM VerifyVSM_Entry_CompletedAndReset Stop
  Passed VerifyVSM_Entry_CompletedAndReset [5 s]
>>>>> 7/30/2026 5:48:43 PM VerifyVSM_Entry_CompletedAndRefocused Start
>>>>> 7/30/2026 5:48:48 PM VerifyVSM_Entry_CompletedAndRefocused Stop
  Passed VerifyVSM_Entry_CompletedAndRefocused [5 s]
>>>>> 7/30/2026 5:48:48 PM VerifyVSM_Entry_DisableWhileCompleted Start
>>>>> 7/30/2026 5:48:54 PM VerifyVSM_Entry_DisableWhileCompleted Stop
  Passed VerifyVSM_Entry_DisableWhileCompleted [5 s]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/EntryFeatureTests.trx
Test Run Successful.
Total tests: 74
     Passed: 74
 Total time: 9.3806 Minutes
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/EntryFeatureTests.trx

⚠️ Failure Details

  • ButtonFeatureTests PASSED without fix (should fail) — tests don't catch the bug
  • EntryFeatureTests PASSED without fix (should fail) — tests don't catch the bug
📁 Fix files reverted (26 files)
  • src/Controls/src/Core/Button/Button.Mapper.cs
  • src/Controls/src/Core/Button/Button.iOS.cs
  • src/Controls/src/Core/Editor/Editor.Android.cs
  • src/Controls/src/Core/Editor/Editor.Mapper.cs
  • src/Controls/src/Core/Editor/Editor.iOS.cs
  • src/Controls/src/Core/Entry/Entry.Android.cs
  • src/Controls/src/Core/Entry/Entry.Mapper.cs
  • src/Controls/src/Core/Entry/Entry.iOS.cs
  • src/Controls/src/Core/Label/Label.Mapper.cs
  • src/Controls/src/Core/Label/Label.iOS.cs
  • src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs
  • src/Controls/src/Core/SearchBar/SearchBar.Android.cs
  • src/Controls/src/Core/SearchBar/SearchBar.Mapper.cs
  • src/Controls/src/Core/SearchBar/SearchBar.iOS.cs
  • src/Core/src/Handlers/Button/ButtonHandler.cs
  • src/Core/src/Handlers/Button/ButtonHandler.iOS.cs
  • src/Core/src/Handlers/Editor/EditorHandler.cs
  • src/Core/src/Handlers/Editor/EditorHandler.iOS.cs
  • src/Core/src/Handlers/Entry/EntryHandler.cs
  • src/Core/src/Handlers/Entry/EntryHandler.iOS.cs
  • src/Core/src/Handlers/Label/LabelHandler.cs
  • src/Core/src/Handlers/Label/LabelHandler.iOS.cs
  • src/Core/src/Handlers/SearchBar/SearchBarHandler.cs
  • src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
  • src/Core/src/Platform/iOS/SearchBarExtensions.cs
  • src/Core/src/Platform/iOS/TextFieldExtensions.cs

📱 UI Tests — Button,Editor,Entry,Label,Material3,SearchBar,ViewBaseTests

Detected UI test categories: Button,Editor,Entry,Label,Material3,SearchBar,ViewBaseTests

Deep UI tests — 489 passed, 0 failed across 7 categories on platform-pool agent (replaces in-process counts above). 1 category reported 0 tests.

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Button 71/72 ✓
Editor 69/70 ✓
Entry 112/113 ✓
Label 89/91 ✓
Material3 0 tests
SearchBar 36/37 ✓
ViewBaseTests 112/112 ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

🔗 Regression Cross-Reference

🔍 Regression Cross-Reference

Overlaps with prior bug-fix PRs — same files modified, but no exact line revert detected.

File Fix PR Fixed issue(s)
src/Core/src/Handlers/Editor/EditorHandler.iOS.cs #35662 #35615
src/Core/src/Platform/iOS/TextFieldExtensions.cs #32889 #32886

🧪 Regression Tests to Verify

These tests were added by the overlapping fix PRs. Running them to verify no side-effect regressions:

Fix PR Type Test Filter
#35662 DeviceTest EditorTests EditorTests
#32889 UITest Issue32886 Issue32886

🧪 Regression Test Results

FAILED — 0 passed, 1 failed, 1 skipped

Fix PR Test Type Result
#35662 EditorTests DeviceTest ⏭️ ERROR
#32889 Issue32886 UITest ❌ FAILED

📋 Pre-Flight — Context & Validation

Issue: #30864 - Improve iOS Label performance (referenced as "Contributing to #30864"; PR has no direct Fixes/Closes issue link)
PR: #35892 - [Net11]Improve label mapping performance and ensure complete coverage including ToPlatform and subsequent property changes
Platforms Affected: iOS primary; MacCatalyst through .iOS.cs/Platform iOS APIs; Android/Windows/Mac affected by feature-matrix snapshots/tests and Android mapper additions
Files Changed: 21 implementation/source files, 12 HostApp/shared UI test files, many platform screenshot baselines

Key Findings

  • PR expands the prior iOS Label performance work from #30864 by changing mapper ordering/connection-time formatting behavior for Button, Entry, Editor, Label, and SearchBar so text is mapped before dependent formatting and duplicate attributed-text work is skipped during initial handler connection.
  • PR changes public iOS/MacCatalyst FormattedStringExtensions.ToNSAttributedString signatures by adding line-break-mode propagation; prior inline reviews noted this is a binary-breaking public API change that appears intentionally recorded in PublicAPI.Unshipped.
  • Prior inline reviews found issues that were addressed or became non-blocking: duplicate EntryHandler.iOS.MapBackground was removed; Button feature test MainLabel was corrected; dead mapper/comment/trailing-newline concerns remain non-blocking.
  • Gate result supplied by caller: Gate ❌ FAILED — tests did not behave as expected. Gate verification must not be re-run and gate/content.md must not be overwritten.
  • Mandatory regression tests for every candidate after primary tests: Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "EditorTests" and BuildAndRunHostApp.ps1 -Platform ios -TestFilter "Issue32886".

Code Review Summary

Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 0 | Suggestions: 0

Key code review findings:

  • ℹ No high-confidence code findings in the current diff.
  • ℹ Blast radius is global for common text controls: mapper ordering and connection guards affect all Button/Entry/Editor/Label/SearchBar instances.
  • ℹ CI is red/undetermined, so the review cannot be LGTM until failures are resolved or classified.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #35892 Reorder text/formatting mappers and suppress redundant formatting during handler connection, with iOS formatted-text line-break propagation and feature-matrix baseline updates ❌ FAILED (Gate supplied by caller) src/Core/src/Handlers/*, src/Controls/src/Core/*, UI feature tests/snapshots Original PR; gate failed, alternatives required

🔬 Code Review — Deep Analysis

Code Review — PR #35892

Independent Assessment

What this changes: Reorders text/formatting mapper execution for Button, Entry, Editor, Label, and SearchBar, especially iOS attributed-text paths, so initial handler connection maps text before dependent formatting properties and avoids duplicate formatting work during connection. It also adds/updates UI feature-matrix coverage and snapshots.

Inferred motivation: Reduce redundant mapper invocations and fix incomplete iOS formatted text propagation for font, character spacing, alignment, max length, and line-break behavior.

Reconciliation with PR Narrative

Author claims: The PR improves iOS label mapping performance, fixes formatted text rendering/propagation, and updates affected visual baselines/tests.

Agreement/disagreement: The code matches those claims. I did not find a high-confidence code correctness issue in the current diff.

Prior Review Reconciliation

Prior ❌ Error Finding Source Status Evidence
Earlier mapper/Material3 and API-surface findings MauiBot / Copilot prior reviews 🔄 Obsolete / not reproduced at current HEAD Latest Copilot review at 17c9127 generated no new comments; expert reviewer found no current high-confidence findings.
Build/test failures Human/MauiBot prior reviews ❌ Still unresolved as CI status Current check-run data still shows failing maui-pr, maui-pr-uitests, and maui-pr-devicetests.

Blast Radius Assessment

  • Runs for all instances: Yes — mapper ordering and connection guards affect common text controls globally.
  • Startup impact: Yes — handler initial connection/property sweep behavior changes.
  • Static/shared state: No new static mutable state beyond mapper composition changes.

External Output Contract

Consumer token/pattern Producer location Producer emission condition Consumer assumption Ordinary negative case Downstream effect
N/A N/A No changed code classifies external tool output N/A N/A N/A

CI Status

  • Required-check result: gh pr checks --required unavailable due unauthenticated gh; REST check-runs for HEAD show failures.
  • Classification: Undetermined / red CI.
  • Action taken: Invoked azdo-build-investigator; ci-analysis skill was unavailable in this environment. Confidence capped low; no LGTM.

Findings

No high-confidence code findings.

Failure-Mode Probing

  • Handler connection with default/null text: guarded MapFormatting skips during connection while reordered mapper sweep applies dependent formatting afterward.
  • Subsequent text changes after connection: MapText calls MapFormatting, which propagates dependent properties via UpdateValue.
  • Material3 Android handlers: current overloads appear wired for EntryHandler2, EditorHandler2, and SearchBarHandler2.
  • Formatted Label with line-break mode: new overload passes label line-break mode into span paragraph style.

Verdict: NEEDS_DISCUSSION

Confidence: low
Summary: Code review found no current high-confidence defect, and the expert reviewer also returned no findings. However CI is still red/undetermined from available check-run data, so this cannot be LGTM until failures are classified or resolved.


🛠️ Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix-1 Localized iOS platform text updates preserve existing attributed-string formatting when text changes; fixes placeholder character-spacing assignment bugs ✅ PASS 5 files Different from PR: no global mapper reordering, no IsConnectingHandler() guards, no handler mapper changes; all primary and mandatory regression tests passed
PR PR #35892 Reorder text/formatting mappers and suppress redundant formatting during handler connection, with iOS formatted-text line-break propagation and feature-matrix baseline updates ❌ FAILED (Gate supplied by caller) handlers/platform text files + UI tests/snapshots Original PR; gate failed before this phase

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 1 Yes Produced candidate #1: preserve formatting inside iOS platform text setters instead of changing mapper order/lifecycle behavior.
claude-opus-4.7 N/A Not run Stop condition met by candidate #1: passed primary and mandatory regression tests and is demonstrably lower blast radius than the PR fix.
gpt-5.3-codex N/A Not run Stop condition met by candidate #1.
gpt-5.5 N/A Not run Stop condition met by candidate #1.

Exhausted: No
Selected Fix: Candidate #1 — It passed all required validation and is demonstrably better than the PR's current fix for this review goal because it localizes the behavior to iOS platform text update helpers, avoids global mapper-order/lifecycle changes across all text handlers, and still covers the mandatory Editor and Issue32886 regression tests.

Candidate #1 Test Results

  • Primary: pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform ios -TestFilter "LabelFeatureTests|EntryFeatureTests|ButtonFeatureTests" — ✅ 213/213 passed.
  • Regression: pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "EditorTests" — ✅ passed, 0 failures.
  • Regression: pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform ios -TestFilter "Issue32886" — ✅ 2/2 passed.
  • Expert self-review: reviewer-findings.json contains [].

Candidate #1 Diff

See try-fix/attempt-1/fix.diff and try-fix-1/content.md.


📝 Recommended PR Title & Description

Assessment: ✏️ Recommend updating — the current title and description describe the PR's broader mapper-ordering implementation, but the winning fix is the focused try-fix-1 platform-text approach.

Recommended title

[iOS] Text controls: Preserve attributed formatting when text changes

Recommended description

### Description of Change

This updates the iOS text-control platform helpers so changing text content preserves existing attributed-string formatting instead of dropping character spacing, paragraph style, and text-decoration attributes.

The fix is localized to the iOS platform text update path:

- Adds an internal `NSAttributedString.WithText(...)` helper that creates a new attributed string with replacement text while preserving formatting attributes such as character spacing, paragraph style, strikethrough, and underline.
- Uses that helper from `LabelExtensions.UpdateTextPlainText`, `TextFieldExtensions.UpdateText`, and `TextViewExtensions.UpdateText` so subsequent text changes keep the formatting already applied by MAUI mappers.
- Fixes iOS Entry and SearchBar placeholder character-spacing updates by assigning the `WithCharacterSpacing(...)` result back to the attributed placeholder.

This avoids changing global mapper ordering or handler connection behavior for Button, Entry, Editor, Label, and SearchBar while still addressing the formatted text rendering issue.

### Issues Fixed

Contributing to #30864

🏁 Report — Final Recommendation

Comparative Candidate Report — PR #35892

Candidates compared

Rank Candidate Regression status Assessment
1 try-fix-1 ✅ Passed primary tests and mandatory regressions (EditorTests, Issue32886) Localizes the fix to iOS platform text update helpers, preserves existing attributed formatting when text changes, and fixes placeholder character-spacing assignment without changing global handler mapper order or connection lifecycle behavior.
2 pr ❌ Gate failed; regression cross-check failed/error No high-confidence code-review finding, but the fix has much broader blast radius: mapper reordering and IsConnectingHandler() guards across Button, Entry, Editor, Label, and SearchBar. The supplied gate did not prove the tests catch the bug.
3 pr-plus-reviewer ❌ Same as pr Expert reviewer produced no actionable findings, so this candidate is identical to pr and inherits the same failed-gate/regression evidence.

Winning candidate

try-fix-1 wins. It is the only candidate with passing validation evidence, and candidates that failed regression tests must rank below candidates that passed. It also has the smaller implementation surface: rather than changing mapper ordering/lifecycle behavior for every common text control instance, it makes the iOS text setters preserve attributed-string formatting when text content changes.

Why the PR fix does not win

The raw PR fix and pr-plus-reviewer may be directionally reasonable and the expert reviewer found no inline defects, but the evidence is weaker: the supplied gate failed because the selected Button/Entry tests passed without the fix, and the saved regression check reported failure/error for overlapping prior fixes. The PR also changes public iOS/MacCatalyst formatted-string API shape and global handler behavior, increasing review risk compared with the focused try-fix.


🧭 Next Steps — alternative fix proposed (try-fix-1)

Automated review — alternative fix proposed

The expert-reviewer evaluation compared the PR fix against automatically generated candidates and selected try-fix-1 as the strongest fix.

Why: try-fix-1 is the only candidate with passing primary and mandatory regression evidence. It localizes the fix to iOS platform text update helpers instead of changing global mapper ordering and handler connection behavior, so it has lower blast radius than the PR and pr-plus-reviewer candidates.

Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.

Candidate diff (try-fix-1)
diff --git a/src/Core/src/Platform/iOS/AttributedStringExtensions.cs b/src/Core/src/Platform/iOS/AttributedStringExtensions.cs
index fbaba09342..f4c9aa2225 100644
--- a/src/Core/src/Platform/iOS/AttributedStringExtensions.cs
+++ b/src/Core/src/Platform/iOS/AttributedStringExtensions.cs
@@ -95,6 +95,34 @@ namespace Microsoft.Maui.Platform
 				? attributedString.Substring(0, maxLength)
 				: attributedString;
 
+		/// <summary>
+		/// Creates a new NSAttributedString with the given text while preserving any formatting
+		/// attributes (character spacing, line height, decorations, paragraph style) from the
+		/// existing attributed string. Returns null if there are no formatting attributes to preserve.
+		/// </summary>
+		internal static NSAttributedString? WithText(this NSAttributedString? attributedString, string newText)
+		{
+			if (attributedString == null || attributedString.Length == 0 || string.IsNullOrEmpty(newText))
+				return null;
+
+			var attrs = attributedString.GetAttributes(0, out _);
+			if (attrs == null || attrs.Count == 0)
+				return null;
+
+			// Only preserve if there are formatting-specific attributes beyond the defaults
+			// that UIKit would set automatically (Font, ForegroundColor).
+			bool hasFormattingAttrs =
+				attrs.ContainsKey(UIStringAttributeKey.KerningAdjustment) ||
+				attrs.ContainsKey(UIStringAttributeKey.ParagraphStyle) ||
+				attrs.ContainsKey(UIStringAttributeKey.StrikethroughStyle) ||
+				attrs.ContainsKey(UIStringAttributeKey.UnderlineStyle);
+
+			if (!hasFormattingAttrs)
+				return null;
+
+			return new NSAttributedString(newText, attrs);
+		}
+
 		static void UpdateDecoration(NSMutableAttributedString attributedString, NSString key,
 			NSRange range, TextDecorations decorations)
 		{
diff --git a/src/Core/src/Platform/iOS/LabelExtensions.cs b/src/Core/src/Platform/iOS/LabelExtensions.cs
index 743173d166..98081a5218 100644
--- a/src/Core/src/Platform/iOS/LabelExtensions.cs
+++ b/src/Core/src/Platform/iOS/LabelExtensions.cs
@@ -105,7 +105,19 @@ namespace Microsoft.Maui.Platform
 
 		internal static void UpdateTextPlainText(this UILabel platformLabel, IText label)
 		{
-			platformLabel.Text = label.Text;
+			var newText = label.Text ?? string.Empty;
+
+			// Preserve formatting attributes (character spacing, line height, decorations)
+			// when replacing text content, so that formatting is not lost regardless of
+			// mapper property ordering.
+			var preserved = platformLabel.AttributedText?.WithText(newText);
+			if (preserved != null)
+			{
+				platformLabel.AttributedText = preserved;
+				return;
+			}
+
+			platformLabel.Text = newText;
 		}
 	}
 }
diff --git a/src/Core/src/Platform/iOS/SearchBarExtensions.cs b/src/Core/src/Platform/iOS/SearchBarExtensions.cs
index a8b498e4ed..a5f16baafe 100644
--- a/src/Core/src/Platform/iOS/SearchBarExtensions.cs
+++ b/src/Core/src/Platform/iOS/SearchBarExtensions.cs
@@ -80,8 +80,8 @@ namespace Microsoft.Maui.Platform
 
 			var placeholder = searchBar.Placeholder ?? string.Empty;
 			var placeholderColor = searchBar.PlaceholderColor is Color color ? color.ToPlatform() : ColorExtensions.PlaceholderColor;
-			textField.AttributedPlaceholder = new NSAttributedString(str: placeholder, foregroundColor: placeholderColor);
-			textField.AttributedPlaceholder.WithCharacterSpacing(searchBar.CharacterSpacing);
+			var attributed = new NSAttributedString(str: placeholder, foregroundColor: placeholderColor);
+			textField.AttributedPlaceholder = attributed.WithCharacterSpacing(searchBar.CharacterSpacing) ?? attributed;
 		}
 
 		public static void UpdateFont(this UISearchBar uiSearchBar, ITextStyle textStyle, IFontManager fontManager)
diff --git a/src/Core/src/Platform/iOS/TextFieldExtensions.cs b/src/Core/src/Platform/iOS/TextFieldExtensions.cs
index 7d1040697b..fb4e01d0c1 100644
--- a/src/Core/src/Platform/iOS/TextFieldExtensions.cs
+++ b/src/Core/src/Platform/iOS/TextFieldExtensions.cs
@@ -11,7 +11,17 @@ namespace Microsoft.Maui.Platform
 	{
 		public static void UpdateText(this UITextField textField, IEntry entry)
 		{
-			textField.Text = entry.Text;
+			var newText = entry.Text;
+
+			// Preserve formatting attributes when replacing text content
+			var preserved = textField.AttributedText?.WithText(newText ?? string.Empty);
+			if (preserved != null)
+			{
+				textField.AttributedText = preserved;
+				return;
+			}
+
+			textField.Text = newText;
 		}
 
 		public static void UpdateTextColor(this UITextField textField, ITextStyle textStyle)
@@ -109,7 +119,9 @@ namespace Microsoft.Maui.Platform
  				? new NSAttributedString(placeholder)
  				: new NSAttributedString(str: placeholder, foregroundColor: foregroundColor.ToPlatform());
 
-			textField.AttributedPlaceholder.WithCharacterSpacing(entry.CharacterSpacing);
+			var spacedPlaceholder = textField.AttributedPlaceholder?.WithCharacterSpacing(entry.CharacterSpacing);
+			if (spacedPlaceholder != null)
+				textField.AttributedPlaceholder = spacedPlaceholder;
 		}
 
 		public static void UpdateIsReadOnly(this UITextField textField, IEntry entry)
diff --git a/src/Core/src/Platform/iOS/TextViewExtensions.cs b/src/Core/src/Platform/iOS/TextViewExtensions.cs
index 5c9594fad5..0f19359eb2 100644
--- a/src/Core/src/Platform/iOS/TextViewExtensions.cs
+++ b/src/Core/src/Platform/iOS/TextViewExtensions.cs
@@ -10,6 +10,15 @@ namespace Microsoft.Maui.Platform
 		{
 			string text = editor.Text;
 
+			// Preserve formatting attributes when replacing text content
+			var preserved = textView.AttributedText?.WithText(text ?? string.Empty);
+			if (preserved != null)
+			{
+				if (textView.AttributedText?.Value != text)
+					textView.AttributedText = preserved;
+				return;
+			}
+
 			if (textView.Text != text)
 				textView.Text = text;
 		}

@kubaflo

This comment has been minimized.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Tests Failure Analysis

@devanathan-vaithiyanathan — test-failure review results are available based on commit 17c9127.

Maintainers can request a fresh review after new comments, commits, or CI runs by commenting /review tests.

Overall Not ready Failures 217 Regressed vs base 187 Baseline 6 on base

Test Failure Review: Not ready - click to expand

Overall verdict: Not ready. Of 217 distinct failures, 187 are deterministic regressions vs the net11.0 base branch (red on the PR, green on all 5 recent base builds sampled per pipeline) — dominated by ~150 visual-snapshot UI regressions across Label, Button, Entry, Slider, Switch, CollectionView and VisualTransform, which line up directly with this PR's Label mapping / ToPlatform rendering changes. Only 6 distinct failures also appear on base; the remainder are indeterminate infra/device-test noise. This needs a human sign-off on the visual diffs before merge.

  • ✗ PR-related — Visual-snapshot UI regressions across Label/VSM/VisualTransform (~150 tests): rendering diffs red on PR and green on every sampled base build, concentrated in the exact controls this PR re-maps; e.g. VerifyVSM_Label_InitialState. Strongly consistent with the label-mapping / ToPlatform property-change work.
  • i Uncertain — Blazor template build failures (~40 legs): BuildMauiBlazorWebSolution fails with ASPDEPR011 (UseWebAssemblyDebugging obsolete) — an ASP.NET Core template/SDK deprecation unrelated to Label mapping, so the regressed-vs-base signal here more likely reflects an SDK roll than this PR; e.g. BuildMauiBlazorWebSolution(... --interactivity WebAssembly ...).
  • i Uncertain — Unexplained / aborted / unverified CI legs (~30 legs): 22 failed build legs yielded no extractable failure, 1 aborted check (MacCatalyst UITests CoreCLR Controls Shell cancelled), 3 device-test checks read green but Failed==0 could not be confirmed, plus device-test build errors — none deterministically attributable.
  • i Uncertain — Flaky-on-base infra & BlazorWebView JS tests (~15 tests): Android SDK provisioning (platform-tools;35.0.2 / avdmanager) and InvokeJavaScriptMethodThatThrows* tests are green on base on too few samples to confirm either way; e.g. InvokeJavaScriptMethodThatThrowsString.

Coverage: 141 checks · 118 passing · 23 failing · 0 pending · 0 inaccessible · 1 unmapped · 22 unexplained build legs · 0 unaccounted failing checks · 1 aborted failing checks · 0 canceled-build checks · 3 device-test unverified · 30 unattributed · 187 regressed-vs-base. Deterministic ceiling: Not ready — 187 leg/failures are red on the PR but green on all sampled base builds.

Builds (this PR): maui-pr 1531102, maui-pr-devicetests 1531165, maui-pr-uitests 1531162. Base sampling (net11.0, 5 recent builds per definition): maui-pr 1535343, maui-pr-devicetests 1535345, maui-pr-uitests 1535443.

Recommended action

A human should review the ~150 Label/VSM/VisualTransform visual-snapshot regressions — confirm whether the rendering changes are intended (and update baseline images) or a genuine regression from the mapping rework — before this PR is merged.

Visual failure comparisons

Full-resolution CI baseline, actual, and diff images are embedded below. They supplement the failure classification and do not change the deterministic verdict ceiling.
Relationship labels use deterministic exact test-and-platform baseline evidence plus exact changed snapshot/test scope; missing or mixed evidence remains Needs human investigation.

TitleBarWithSmallHeightShell - windows - Needs human investigation - visual comparison

CI reported 0.80% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
TitleBarWithSmallHeightShell baseline TitleBarWithSmallHeightShell actual TitleBarWithSmallHeightShell diff
TitleBarWithLargeHeightShell - windows - Needs human investigation - visual comparison

CI reported 0.80% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
TitleBarWithLargeHeightShell baseline TitleBarWithLargeHeightShell actual TitleBarWithLargeHeightShell diff
NavBarResetsColorAfterSmallTitleBarShell - windows - Needs human investigation - visual comparison

CI reported 0.80% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
NavBarResetsColorAfterSmallTitleBarShell baseline NavBarResetsColorAfterSmallTitleBarShell actual NavBarResetsColorAfterSmallTitleBarShell diff
NavBarResetsColorAfterLargeTitleBarShell - windows - Needs human investigation - visual comparison

CI reported 0.80% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
NavBarResetsColorAfterLargeTitleBarShell baseline NavBarResetsColorAfterLargeTitleBarShell actual NavBarResetsColorAfterLargeTitleBarShell diff
TitleBarWithSmallHeight - windows - Needs human investigation - visual comparison

CI reported 0.80% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
TitleBarWithSmallHeight baseline TitleBarWithSmallHeight actual TitleBarWithSmallHeight diff
TitleBarWithLargeHeight - windows - Needs human investigation - visual comparison

CI reported 0.80% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
TitleBarWithLargeHeight baseline TitleBarWithLargeHeight actual TitleBarWithLargeHeight diff
NavBarResetsColorAfterSmallTitleBar - windows - Needs human investigation - visual comparison

CI reported 0.80% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
NavBarResetsColorAfterSmallTitleBar baseline NavBarResetsColorAfterSmallTitleBar actual NavBarResetsColorAfterSmallTitleBar diff
NavBarResetsColorAfterLargeTitleBar - windows - Needs human investigation - visual comparison

CI reported 0.80% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
NavBarResetsColorAfterLargeTitleBar baseline NavBarResetsColorAfterLargeTitleBar actual NavBarResetsColorAfterLargeTitleBar diff
VerifyBackgroundColorCleared - windows - Needs human investigation - visual comparison

CI reported 0.79% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
VerifyBackgroundColorCleared baseline VerifyBackgroundColorCleared actual VerifyBackgroundColorCleared diff
SecondCustomPageWithFlowDirectionChanged - windows - Needs human investigation - visual comparison

CI reported 0.79% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
SecondCustomPageWithFlowDirectionChanged baseline SecondCustomPageWithFlowDirectionChanged actual SecondCustomPageWithFlowDirectionChanged diff
CollectionViewSelectionShouldClear - windows - Needs human investigation - visual comparison

CI reported 0.52% difference in build 1531162.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
CollectionViewSelectionShouldClear baseline CollectionViewSelectionShouldClear actual CollectionViewSelectionShouldClear diff
Visual output was bounded for comment safety; 13 additional comparison(s) were omitted. 10 additional visual comparison(s) were omitted by publisher bounds (deduplication, the comparison cap, the discovery/publish time budget, or assets that failed validation).

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

Labels

area-controls-label Label, Span partner/syncfusion Issues / PR's with Syncfusion collaboration platform/ios platform/macos macOS / Mac Catalyst s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-gate-failed AI could not verify tests catch the bug s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants