[iOS] Fix Switch custom colors on iOS 26#35385
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35385Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35385" |
|
Hey there @@AdamEssenmacher! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
There was a problem hiding this comment.
Pull request overview
This PR addresses an iOS 26 regression where UISwitch can ignore MAUI Switch custom colors by opting into UIKit’s classic sliding switch style (UISwitchStyle.Sliding) when MAUI track/thumb colors are set, while leaving default (unstyled) switches on UISwitchStyle.Automatic.
Changes:
- Updates iOS
UISwitchcolor mapping to switch styles on iOS 26+ when colors are customized, and reapply colors after style changes. - Ensures native
ThumbTintColoris cleared when MAUIThumbColoris reset tonull, and makesSwitch.ThumbColornotify the handler on change. - Adds iOS device-handler tests and a new Appium visual regression issue test/page for iOS 26 switch custom colors.
Reviewed changes
Copilot reviewed 6 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/Core/src/Platform/iOS/SwitchExtensions.cs | Applies iOS 26+ preferred style switching and re-applies track/thumb colors when UIKit may rebuild internal views. |
| src/Controls/src/Core/Switch/Switch.cs | Adds a ThumbColor bindable-property changed callback to update the handler mapping. |
| src/Core/tests/DeviceTests/Handlers/Switch/SwitchHandlerTests.iOS.cs | Adds iOS 26 style-selection assertions and validates thumb tint clearing behavior. |
| src/Controls/tests/TestCases.HostApp/MauiProgram.cs | Allows iOS HostApp startup-page selection via the test environment variable (previously MacCatalyst-only). |
| src/Controls/tests/TestCases.HostApp/Issues/Issue35257.cs | Adds a HostApp repro page with default vs custom-colored switches for visual validation. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35257.cs | Adds an iOS Appium screenshot test to validate initial custom color rendering. |
kubaflo
left a comment
There was a problem hiding this comment.
Looks like there some test failures caused by dark/light themes
2c8d97f to
b72e82d
Compare
- SKILL.md platform table: add /Handlers/*/iOS/, /Handlers/*/MacCatalyst/, and /Handlers/*/Windows/ to platform rows. Mirrors the Android row's handler-subdirectory pattern. iOS-directory row maps to platform/ios ONLY (not dual with platform/macos) because handler /iOS/ directories compile for iOS TFM only, unlike the *.iOS.cs file-extension pattern which compiles for both iOS and MacCatalyst. - eval.yaml PR #35461 scenario: rename to flag scope-restriction intent, add platform/android positive assertion (the PR touches Android files) and forbidden-label negatives for i/regression, partner/syncfusion, t/bug — those labels already exist on the PR but our labeler must NOT apply them. - eval.yaml PR #35385 scenario: add platform/macos and platform/windows assertions. The PR touches Platform/Windows/, Platform/Android/, and *.iOS.cs files — that last one triggers BOTH platform/ios AND platform/macos per our file-extension rule. - eval.yaml XAML scenario: rename 'issue' -> 'PR' (prompt targets a PR). - workflow.md frontmatter description: update from generic 'appropriate labels chosen from the existing repository label set' to explicitly state 'area-* and platform/* ONLY, does NOT apply triage, status, priority, type, severity, partner, regression, or any other label families'. Locked-yml regenerated by gh aw compile. Adversarial review findings deliberately NOT applied: - (?i) regex prefix: invalidated — skill-validator already passes RegexOptions.IgnoreCase and StringComparison.OrdinalIgnoreCase, so case is handled at the framework level. - output_not_contains 'area-' / 'platform/' on noop scenarios: too risky — agent prose may legitimately reference these prefixes when explaining why no labels apply. - Issue #35448 prompt change: existing-label contamination is a framework limitation (substring match in prose); not worth a scenario-level fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/review -b feature/refactor-copilot-yml -p ios |
MauiBot
left a comment
There was a problem hiding this comment.
🤖 Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against #2 automatically generated candidates and selected try-fix-2 as the strongest fix.
Why: try-fix-2 passed the iOS SwitchHandler regression tests and provides the same iOS 26 custom-color behavior with less persistent state than the PR fix. It avoids queued reapply state and duplicate trait handling by materializing the Sliding-style subview hierarchy before applying colors, then relying on idempotent lifecycle reapply as a safety net.
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-2`)
diff --git a/src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs b/src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs
index 01fc9c0bb7..42109f5ee8 100644
--- a/src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs
+++ b/src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs
@@ -1,8 +1,7 @@
using System;
-using System.Threading.Tasks;
-using CoreFoundation;
using Foundation;
using Microsoft.Maui.Graphics;
+using Microsoft.Maui.Platform;
using ObjCRuntime;
using UIKit;
using RectangleF = CoreGraphics.CGRect;
@@ -21,7 +20,7 @@ namespace Microsoft.Maui.Handlers
protected override UISwitch CreatePlatformView()
{
- return new UISwitch(RectangleF.Empty);
+ return new MauiSwitch(RectangleF.Empty);
}
protected override void ConnectHandler(UISwitch platformView)
@@ -74,51 +73,35 @@ namespace Microsoft.Maui.Handlers
{
_virtualView = new(virtualView);
_platformView = new(platformView);
+ (platformView as MauiSwitch)?.Connect(virtualView);
platformView.ValueChanged += OnControlValueChanged;
#if MACCATALYST
_windowDidBecomeKeyObserver = NSNotificationCenter.DefaultCenter.AddObserver(
new NSString("NSWindowDidBecomeKeyNotification"), _ =>
{
- if (PlatformView is not null)
+ if (PlatformView is not null && VirtualView is ISwitch view)
{
- UpdateTrackOffColor(PlatformView);
+ PlatformView.UpdateTrackColor(view);
+ PlatformView.UpdateThumbColor(view);
}
});
#elif IOS
_willEnterForegroundObserver = NSNotificationCenter.DefaultCenter.AddObserver(
UIApplication.WillEnterForegroundNotification, _ =>
{
- if (PlatformView is not null)
+ if (PlatformView is not null && VirtualView is ISwitch view && view.TrackColor is not null)
{
- UpdateTrackOffColor(PlatformView);
+ PlatformView.UpdateTrackColor(view);
}
});
#endif
}
- // Ensures the Switch track "OFF" color is updated correctly after system-level UI resets.
- // This is necessary because UIKit may re-apply default styles to internal views during certain lifecycle events,
- // especially when the app enters the background and returns to the foreground.
- void UpdateTrackOffColor(UISwitch platformView)
- {
- DispatchQueue.MainQueue.DispatchAsync(async () =>
- {
- if (!platformView.On)
- {
- await Task.Delay(10); // Small delay, necessary to allow UIKit to complete its internal layout and styling processes before re-applying the custom color
-
- if (VirtualView is ISwitch view && view.TrackColor is not null)
- {
- platformView.UpdateTrackColor(view);
- }
- }
- });
- }
-
public void Disconnect(UISwitch platformView)
{
platformView.ValueChanged -= OnControlValueChanged;
+ (platformView as MauiSwitch)?.Disconnect();
if (_willEnterForegroundObserver is not null)
{
diff --git a/src/Core/src/Platform/iOS/MauiSwitch.cs b/src/Core/src/Platform/iOS/MauiSwitch.cs
index e56ce5bb7b..f156730447 100644
--- a/src/Core/src/Platform/iOS/MauiSwitch.cs
+++ b/src/Core/src/Platform/iOS/MauiSwitch.cs
@@ -1,16 +1,15 @@
using System;
-using CoreFoundation;
using CoreGraphics;
using UIKit;
namespace Microsoft.Maui.Platform
{
+ // Stateless subclass: lifecycle hooks perform a single readiness-gated synchronous
+ // re-application of TrackColor/ThumbColor. No queued state, no deferred dispatch.
+ // Re-applying the same colors is idempotent, so no re-entry guard is needed.
internal class MauiSwitch : UISwitch
{
WeakReference<ISwitch>? _virtualView;
- bool _colorReapplyQueued;
- bool _isReapplyingColors;
- bool _needsColorReapply;
public MauiSwitch(CGRect frame) : base(frame)
{
@@ -18,21 +17,12 @@ namespace Microsoft.Maui.Platform
public void Connect(ISwitch virtualView)
{
- _virtualView = new(virtualView);
- SetNeedsColorReapply();
+ _virtualView = new WeakReference<ISwitch>(virtualView);
}
public void Disconnect()
{
_virtualView = null;
- _needsColorReapply = false;
- }
-
- public void SetNeedsColorReapply()
- {
- _needsColorReapply = true;
- SetNeedsLayout();
- QueueColorReapply();
}
public override void MovedToWindow()
@@ -40,62 +30,39 @@ namespace Microsoft.Maui.Platform
base.MovedToWindow();
if (Window is not null)
{
- SetNeedsColorReapply();
+ TryApplyColors();
}
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
- QueueColorReapply();
+ TryApplyColors();
}
public override void TraitCollectionDidChange(UITraitCollection? previousTraitCollection)
{
base.TraitCollectionDidChange(previousTraitCollection);
- SetNeedsColorReapply();
+ TryApplyColors();
}
- void QueueColorReapply()
+ void TryApplyColors()
{
- if (_colorReapplyQueued || !_needsColorReapply)
+ if (_virtualView is null || !_virtualView.TryGetTarget(out var view))
{
return;
}
- _colorReapplyQueued = true;
-
- DispatchQueue.MainQueue.DispatchAsync(() =>
- {
- _colorReapplyQueued = false;
- TryReapplyColors();
- });
- }
-
- void TryReapplyColors()
- {
- if (_isReapplyingColors || !_needsColorReapply || !this.IsReadyForColorReapply())
+ // Gate on readiness: track subview must exist and have non-zero bounds,
+ // otherwise the BackgroundColor set in ApplyTrackColor would land on
+ // the wrong subview (the root cause of the iOS 26+ initial-render bug).
+ if (!this.IsReadyForColorReapply())
{
return;
}
- if (_virtualView is null || !_virtualView.TryGetTarget(out var virtualView))
- {
- return;
- }
-
- _isReapplyingColors = true;
-
- try
- {
- this.ApplyTrackColor(virtualView);
- this.ApplyThumbColor(virtualView);
- _needsColorReapply = false;
- }
- finally
- {
- _isReapplyingColors = false;
- }
+ this.ApplyTrackColor(view);
+ this.ApplyThumbColor(view);
}
}
}
diff --git a/src/Core/src/Platform/iOS/SwitchExtensions.cs b/src/Core/src/Platform/iOS/SwitchExtensions.cs
index 0133b681f7..100de0ff7f 100644
--- a/src/Core/src/Platform/iOS/SwitchExtensions.cs
+++ b/src/Core/src/Platform/iOS/SwitchExtensions.cs
@@ -19,6 +19,18 @@ namespace Microsoft.Maui.Platform
return;
}
+ // Switch to Sliding style first (iOS/MacCatalyst 26+ only, and only when custom
+ // colors are present). Forcing synchronous layout materializes the new style's
+ // track subview tree BEFORE we reach into Subviews — which is the root cause
+ // of the iOS 26+ initial-render bug (we used to set BackgroundColor on a
+ // subview that hadn't been materialized for the Sliding style yet).
+ uiSwitch.UpdatePreferredStyleAndMaterialize(view);
+
+ uiSwitch.ApplyTrackColor(view);
+ }
+
+ internal static void ApplyTrackColor(this UISwitch uiSwitch, ISwitch view)
+ {
var uIView = GetTrackSubview(uiSwitch);
if (uIView is null)
@@ -62,9 +74,61 @@ namespace Microsoft.Maui.Platform
if (view == null)
return;
- Graphics.Color thumbColor = view.ThumbColor;
- if (thumbColor != null)
- uiSwitch.ThumbTintColor = thumbColor?.ToPlatform();
+ uiSwitch.UpdatePreferredStyleAndMaterialize(view);
+ uiSwitch.ApplyThumbColor(view);
+ }
+
+ internal static void ApplyThumbColor(this UISwitch uiSwitch, ISwitch view)
+ {
+ uiSwitch.ThumbTintColor = view.ThumbColor?.ToPlatform();
+ }
+
+ // Switches to UISwitchStyle.Sliding on iOS/MacCatalyst 26+ when custom colors are
+ // requested, then synchronously forces UIKit to materialize the new style's
+ // subview tree via LayoutIfNeeded. This avoids any need for deferred reapply
+ // queues: colors land on the correct (now-materialized) subview on the first try.
+ static void UpdatePreferredStyleAndMaterialize(this UISwitch uiSwitch, ISwitch view)
+ {
+#if IOS || MACCATALYST
+ if (!IsSlidingStyleRequiredForCustomColors())
+ {
+ return;
+ }
+
+ var preferredStyle = view.TrackColor is not null || view.ThumbColor is not null
+ ? UISwitchStyle.Sliding
+ : UISwitchStyle.Automatic;
+
+ if (uiSwitch.PreferredStyle != preferredStyle)
+ {
+ uiSwitch.PreferredStyle = preferredStyle;
+ uiSwitch.SetNeedsLayout();
+ // Synchronous layout pass: materialize the new style's track subview
+ // hierarchy NOW, before the caller reaches into Subviews to set colors.
+ uiSwitch.LayoutIfNeeded();
+ }
+#endif
+ }
+
+ internal static bool IsReadyForColorReapply(this UISwitch uiSwitch)
+ {
+ var trackSubview = uiSwitch.GetTrackSubview();
+
+ return uiSwitch.Window is not null
+ && trackSubview is not null
+ && uiSwitch.Bounds.Width > 0
+ && uiSwitch.Bounds.Height > 0
+ && trackSubview.Bounds.Width > 0
+ && trackSubview.Bounds.Height > 0;
+ }
+
+ static bool IsSlidingStyleRequiredForCustomColors()
+ {
+#if IOS || MACCATALYST
+ return OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26);
+#else
+ return false;
+#endif
}
internal static UIView? GetTrackSubview(this UISwitch uISwitch)
|
/review -b feature/refactor-copilot-yml -p ios |
MauiBot
left a comment
There was a problem hiding this comment.
🤖 Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against #2 automatically generated candidates and selected try-fix-2 as the strongest fix.
Why: try-fix-2 won because it is the only candidate with recorded passing targeted iOS Switch regression results and it preserves the native lifecycle observation required for iOS 26 color reapplication. It also improves the PR design by keeping MAUI virtual-view state and color application in SwitchProxy instead of inside the native UISwitch subclass.
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-2`)
diff --git a/src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs b/src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs
index 20bad79c73..dcfaa11880 100644
--- a/src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs
+++ b/src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs
@@ -68,12 +68,17 @@ namespace Microsoft.Maui.Handlers
NSObject? _willEnterForegroundObserver;
NSObject? _windowDidBecomeKeyObserver;
+ bool _colorReapplyQueued;
public void Connect(ISwitch virtualView, UISwitch platformView)
{
_virtualView = new(virtualView);
_platformView = new(platformView);
- (platformView as MauiSwitch)?.Connect(virtualView);
+ if (platformView is MauiSwitch mauiSwitch)
+ {
+ mauiSwitch.ColorReapplyRequested += OnColorReapplyRequested;
+ mauiSwitch.SetNeedsColorReapply();
+ }
platformView.ValueChanged += OnControlValueChanged;
#if MACCATALYST
@@ -118,28 +123,65 @@ namespace Microsoft.Maui.Handlers
if (!platformView.On && VirtualView is ISwitch view && view.TrackColor is not null)
{
platformView.UpdateTrackColor(view);
- (platformView as MauiSwitch)?.SetNeedsColorReapply();
+ RequestColorReapply(platformView);
}
});
}
void UpdateThumbAndTrackColor(UISwitch platformView)
{
+ RequestColorReapply(platformView);
+ }
+
+ void RequestColorReapply(UISwitch platformView)
+ {
+ if (platformView is MauiSwitch mauiSwitch)
+ {
+ mauiSwitch.SetNeedsColorReapply();
+ }
+ else
+ {
+ QueueColorReapply(platformView);
+ }
+ }
+
+ void OnColorReapplyRequested(object? sender, EventArgs e)
+ {
+ if (sender is UISwitch platformView)
+ {
+ QueueColorReapply(platformView);
+ }
+ }
+
+ void QueueColorReapply(UISwitch platformView)
+ {
+ if (_colorReapplyQueued)
+ {
+ return;
+ }
+
+ _colorReapplyQueued = true;
+
DispatchQueue.MainQueue.DispatchAsync(() =>
{
- if (VirtualView is not ISwitch view || PlatformView is null)
+ _colorReapplyQueued = false;
+
+ if (VirtualView is not ISwitch view || PlatformView is null || !platformView.IsReadyForColorReapply())
return;
platformView.UpdateTrackColor(view);
platformView.UpdateThumbColor(view);
- (platformView as MauiSwitch)?.SetNeedsColorReapply();
});
}
public void Disconnect(UISwitch platformView)
{
platformView.ValueChanged -= OnControlValueChanged;
- (platformView as MauiSwitch)?.Disconnect();
+ if (platformView is MauiSwitch mauiSwitch)
+ {
+ mauiSwitch.ColorReapplyRequested -= OnColorReapplyRequested;
+ mauiSwitch.Disconnect();
+ }
if (_willEnterForegroundObserver is not null)
{
diff --git a/src/Core/src/Platform/iOS/MauiSwitch.cs b/src/Core/src/Platform/iOS/MauiSwitch.cs
index e56ce5bb7b..518254542e 100644
--- a/src/Core/src/Platform/iOS/MauiSwitch.cs
+++ b/src/Core/src/Platform/iOS/MauiSwitch.cs
@@ -7,30 +7,22 @@ namespace Microsoft.Maui.Platform
{
internal class MauiSwitch : UISwitch
{
- WeakReference<ISwitch>? _virtualView;
bool _colorReapplyQueued;
- bool _isReapplyingColors;
- bool _needsColorReapply;
- public MauiSwitch(CGRect frame) : base(frame)
- {
- }
+ internal event EventHandler? ColorReapplyRequested;
- public void Connect(ISwitch virtualView)
+ public MauiSwitch(CGRect frame) : base(frame)
{
- _virtualView = new(virtualView);
- SetNeedsColorReapply();
}
public void Disconnect()
{
- _virtualView = null;
- _needsColorReapply = false;
+ ColorReapplyRequested = null;
+ _colorReapplyQueued = false;
}
public void SetNeedsColorReapply()
{
- _needsColorReapply = true;
SetNeedsLayout();
QueueColorReapply();
}
@@ -58,7 +50,7 @@ namespace Microsoft.Maui.Platform
void QueueColorReapply()
{
- if (_colorReapplyQueued || !_needsColorReapply)
+ if (_colorReapplyQueued)
{
return;
}
@@ -68,34 +60,8 @@ namespace Microsoft.Maui.Platform
DispatchQueue.MainQueue.DispatchAsync(() =>
{
_colorReapplyQueued = false;
- TryReapplyColors();
+ ColorReapplyRequested?.Invoke(this, EventArgs.Empty);
});
}
-
- void TryReapplyColors()
- {
- if (_isReapplyingColors || !_needsColorReapply || !this.IsReadyForColorReapply())
- {
- return;
- }
-
- if (_virtualView is null || !_virtualView.TryGetTarget(out var virtualView))
- {
- return;
- }
-
- _isReapplyingColors = true;
-
- try
- {
- this.ApplyTrackColor(virtualView);
- this.ApplyThumbColor(virtualView);
- _needsColorReapply = false;
- }
- finally
- {
- _isReapplyingColors = false;
- }
- }
}
}
|
I’m open to trying the I agree the architecture is appealing: The reason I haven’t applied it directly is that the current fix is more targeted and preserves the existing switch-specific state machine:
So the current PR is not introducing a completely separate competing pattern; it is extending the existing switch handler split. The tradeoff is that I’m happy to go that route if we agree the cleaner boundary is worth the broader production refactor in this PR. Otherwise, I think the current targeted fix plus the test hardening is the lower-risk path for the iOS 26 regression. |
|
/review -b feature/refactor-copilot-yml -p ios |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
|
|
||
| try | ||
| { | ||
| this.ApplyTrackColor(virtualView); |
There was a problem hiding this comment.
[moderate] Native Defaults Preservation — MauiSwitch re-applies colors for every switch, including switches with both TrackColor and ThumbColor unset. On iOS 26 the default switch remains UISwitchStyle.Automatic, but this path still calls ApplyTrackColor, which sets the internal off-track subview to SecondarySystemFill for TrackColor == null. Concrete scenario: a default off switch is attached or gets a trait/window/layout reapply; the PR can overwrite UIKit's iOS 26 Automatic/Liquid Glass default even though no custom color was requested. Please skip lifecycle reapply when both colors are null, or avoid mutating the track in that case, and add a default-rendering or clear-both-colors check so Automatic defaults are preserved.
|
/review -b feature/refactor-copilot-yml -p ios |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
|
|
||
| try | ||
| { | ||
| this.ApplyTrackColor(virtualView); |
There was a problem hiding this comment.
[moderate] Native Defaults Preservation — TryReapplyColors reapplies track/thumb colors even when the MAUI switch has neither TrackColor nor ThumbColor set. For an unstyled/off iOS 26 UISwitchStyle.Automatic switch, ApplyTrackColor writes SecondarySystemFill into the internal track subview after lifecycle resets such as MovedToWindow or trait changes, so UIKit's Automatic/Liquid Glass defaults can be overwritten even though the switch is uncustomized. Please only arm/reapply this lifecycle path when virtualView.TrackColor is not null || virtualView.ThumbColor is not null.
|
/review -b feature/refactor-copilot-yml -p ios |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
| platformView.UpdateThumbColor(view); | ||
| } | ||
| } | ||
| platformView.UpdateTrackColor(view); |
There was a problem hiding this comment.
[moderate] Native Defaults Preservation — UpdateThumbAndTrackColor runs on connect for every iOS/MacCatalyst 26 switch and unconditionally calls UpdateTrackColor(view). For an uncustomized off switch (TrackColor == null and ThumbColor == null), UpdateTrackColor keeps PreferredStyle as Automatic but then ApplyTrackColor writes SecondarySystemFill into the private track subview, so initial attach can overwrite UIKit's iOS 26 Automatic/Liquid Glass default even though no custom color was requested. Please skip this connect reapply unless a custom track or thumb color is set, or avoid mutating the track when both are null.
|
/review -b feature/refactor-copilot-yml |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
|
|
||
| try | ||
| { | ||
| this.ApplyTrackColor(virtualView); |
There was a problem hiding this comment.
[major] Native defaults preservation — TryReapplyColors reapplies track/thumb colors even when both TrackColor and ThumbColor are null. For an uncustomized off switch on iOS/MacCatalyst 26, the style can remain Automatic, but ApplyTrackColor still writes SecondarySystemFill into the internal track subview and ApplyThumbColor writes null into ThumbTintColor, so layout/trait/window reapply paths can mutate UIKit's native Liquid Glass/default rendering. Please gate reapply on custom-color presence, or make the low-level apply methods no-op when the switch is Automatic with no custom colors, and add a regression test that verifies default/native rendering is preserved rather than only checking PreferredStyle.
| [InlineData(true, true)] | ||
| public async Task CustomColorsUseSlidingStyleOniOS26(bool hasTrackColor, bool hasThumbColor) | ||
| { | ||
| if (!OperatingSystem.IsIOSVersionAtLeast(26)) |
There was a problem hiding this comment.
[moderate] Regression prevention — The new iOS 26 tests only check OperatingSystem.IsIOSVersionAtLeast(26), but the production fix also changes MacCatalyst 26 behavior (IsMacCatalystVersionAtLeast(26) and the NSWindowDidBecomeKeyNotification reapply path). On MacCatalyst these tests skip, leaving the MacCatalyst-specific switch style/reapply behavior untested. Please use a shared iOS-or-MacCatalyst 26 guard or add MacCatalyst-specific coverage.
🤖 AI Summary
📊 Review Session —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
📱 SwitchHandlerTests (CustomColorsUseSlidingStyleOniOS26, CustomColorsRenderOnInitialOffStateOniOS26, DefaultSwitchUsesAutomaticStyleOniOS26, ThumbColorClearsWhenResetOniOS26, CustomColorsReapplyAfterMovedToWindowOniOS26, CustomColorsUpdateAfterAppThemeChangeOniOS26) Category=Switch |
✅ FAIL — 494s | ❌ FAIL — 1018s |
🔴 Without fix — 📱 SwitchHandlerTests (CustomColorsUseSlidingStyleOniOS26, CustomColorsRenderOnInitialOffStateOniOS26, DefaultSwitchUsesAutomaticStyleOniOS26, ThumbColorClearsWhenResetOniOS26, CustomColorsReapplyAfterMovedToWindowOniOS26, CustomColorsUpdateAfterAppThemeChangeOniOS26): FAIL ✅ · 494s
(truncated to last 15,000 chars)
8Ho/H4/F4PB6Px+PxeNoTudAn8P+c/P13F+wscnhBNJf57q80rOfFMJ8wzotgvCCWn8bCXmi9kfnEsRjRLBkviOWh8T5Kw6eaZ9v5XEZ+3TZ81yiGZRWHF8TSaSzgvAjywpAfAxWDXF777Mr9dwJcCO49sCG4V1IhOGYLIS+OplgML4hzZ64nXTWurwPpA9UHcgz4XeAWkCLoXhABceBGwVXBPAPub4A1wAi4EbCDdQHkBdFoORrXl3xRnsXTKAYFyGaQPaA+BvIBEIO6Cwq/BX290HcCLumA9WVYq2BlAEUHIRAbqFo4VYHDU/D+Kjg0CiN/DyMPQhSCvRR4sS6GvEiWTRReEGfPLDdAThBrQa8HdxsEt8LAari2CB8pw/UhXCmwSkEnUJb0P87VdyguLdyKhUkHJxJ4awp2TsGOEdj9NAw9Bsn7IIfBcLo45oszzvriPIsjCw5niWEt6MPg/gD4FdhwDdysYFsI1ytYDxTIFZKQlqICV/vEgqjaRrl6qACRhfdj2Gnh+TfhuYfgwF8Da+vCmEsUmUs5K7wgFsd8VkFthiCC+DroehDuKMDtCrYo6AOC3H9
05-28 04:26:14.295 7872 7932 I DOTNET : Exception stack traces: at Microsoft.Maui.DeviceTests.AssertionExtensions.<>c__DisplayClass32_0.<AssertContainsColor>b__0()
05-28 04:26:14.295 7872 7932 I DOTNET : at System.Threading.Tasks.Task`1[[Android.Graphics.Bitmap, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065]].InnerInvoke()
05-28 04:26:14.295 7872 7932 I DOTNET : at System.Threading.Tasks.Task.<>c.<.cctor>b__288_0(Object obj)
05-28 04:26:14.295 7872 7932 I DOTNET : at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread , ExecutionContext , ContextCallback , Object )
05-28 04:26:14.295 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.295 7872 7932 I DOTNET : at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread , ExecutionContext , ContextCallback , Object )
05-28 04:26:14.295 7872 7932 I DOTNET : at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& , Thread )
05-28 04:26:14.295 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.295 7872 7932 I DOTNET : at Microsoft.Maui.DeviceTests.AssertionExtensions.AssertContainsColor(View view, Color expectedColor, IMauiContext mauiContext, Nullable`1 tolerance)
05-28 04:26:14.295 7872 7932 I DOTNET : at Microsoft.Maui.DeviceTests.HandlerTestBasement.<>c__DisplayClass18_0.<<ValidateHasColor>b__0>d.MoveNext()
05-28 04:26:14.295 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.295 7872 7932 I DOTNET : at Microsoft.Maui.Dispatching.DispatcherExtensions.<>c__DisplayClass3_0.<<DispatchAsync>b__0>d.MoveNext()
05-28 04:26:14.295 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.295 7872 7932 I DOTNET : at Microsoft.Maui.Dispatching.DispatcherExtensions.<>c__DisplayClass2_0`1.<<DispatchAsync>b__0>d[[System.Boolean, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
05-28 04:26:14.295 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.295 7872 7932 I DOTNET : at Microsoft.Maui.DeviceTests.SwitchHandlerTests.ThumbColorUpdatesCorrectly()
05-28 04:26:14.295 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.295 7872 7932 I DOTNET : Execution time: 0.2217571
05-28 04:26:14.295 7872 7932 I DOTNET : Test trait name: Category
05-28 04:26:14.295 7872 7932 I DOTNET : value: Switch
05-28 04:26:14.296 7872 7932 I DOTNET : [FAIL] Thumb Color Updates Correctly
05-28 04:26:14.296 7872 7932 I DOTNET : [FAIL] Thumb Color Updates Correctly Test name: Thumb Color Updates Correctly
05-28 04:26:14.296 7872 7932 I DOTNET : Assembly: [Microsoft.Maui.Core.DeviceTests, Version=10.0.0.0, Culture=neutral, PublicKeyToken=null]
05-28 04:26:14.297 7872 7932 I DOTNET : Exception messages: Color Color [A=255, R=255, G=0, B=0] not found. This is what it looked like:<img>iVBORw0KGgoAAAANSUhEUgAAAIQAAACECAYAAABRRIOnAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAA/rSURBVHic7Z17jB3Vfcc/v3Nm7mMf9u567QVjMDYuD2NMk7agKGkxhTTpU6iCVG1FGlo1UdWmUtWHQqMKXKlVioRKojRRWjVUrdSWh1pUgigtLyUNJBQwxtjYYDCY9WO99q69r7t3Zs45/WPu7J293vWuvXvte+n5SKM7unfuvM53fr/f+Z3HgMfj8Xg8Ho/H4/F4PB6Px+PxeDwej8fj8Xg8Ho/H4/F4PB6Px+PxeDwej8fj8Xg8Ho/H4/F4PB6Px+PxeNoTudAn8P+c/P13F+wscnhBNJf57q80rOfFMJ8wzotgvCCWn8bCXmi9kfnEsRjRLBkviOWh8T5Kw6eaZ9v5XEZ+3TZ81yiGZRWHF8TSaSzgvAjywpAfAxWDXF777Mr9dwJcCO49sCG4V1IhOGYLIS+OplgML4hzZ64nXTWurwPpA9UHcgz4XeAWkCLoXhABceBGwVXBPAPub4A1wAi4EbCDdQHkBdFoORrXl3xRnsXTKAYFyGaQPaA+BvIBEIO6Cwq/BX290HcCLumA9WVYq2BlAEUHIRAbqFo4VYHDU/D+Kjg0CiN/DyMPQhSCvRR4sS6GvEiWTRReEGfPLDdAThBrQa8HdxsEt8LAari2CB8pw/UhXCmwSkEnUJb0P87VdyguLdyKhUkHJxJ4awp2TsGOEdj9NAw9Bsn7IIfBcLo45oszzvriPIsjCw5niWEt6MPg/gD4FdhwDdysYFsI1ytYDxTIFZKQlqICV/vEgqjaRrl6qACRhfdj2Gnh+TfhuYfgwF8Da+vCmEsUmUs5K7wgFsd8VkFthiCC+DroehDuKMDtCrYo6AOC3H9
05-28 04:26:14.297 7872 7932 I DOTNET : at System.Threading.Tasks.Task.<>c.<.cctor>b__288_0(Object obj)
05-28 04:26:14.297 7872 7932 I DOTNET : at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread , ExecutionContext , ContextCallback , Object )
05-28 04:26:14.297 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.297 7872 7932 I DOTNET : at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread , ExecutionContext , ContextCallback , Object )
05-28 04:26:14.297 7872 7932 I DOTNET : at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& , Thread )
05-28 04:26:14.297 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.297 7872 7932 I DOTNET : at Microsoft.Maui.DeviceTests.AssertionExtensions.AssertContainsColor(View view, Color expectedColor, IMauiContext mauiContext, Nullable`1 tolerance)
05-28 04:26:14.297 7872 7932 I DOTNET : at Microsoft.Maui.DeviceTests.HandlerTestBasement.<>c__DisplayClass18_0.<<ValidateHasColor>b__0>d.MoveNext()
05-28 04:26:14.297 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.297 7872 7932 I DOTNET : at Microsoft.Maui.Dispatching.DispatcherExtensions.<>c__DisplayClass3_0.<<DispatchAsync>b__0>d.MoveNext()
05-28 04:26:14.297 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.297 7872 7932 I DOTNET : at Microsoft.Maui.Dispatching.DispatcherExtensions.<>c__DisplayClass2_0`1.<<DispatchAsync>b__0>d[[System.Boolean, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
05-28 04:26:14.297 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.297 7872 7932 I DOTNET : at Microsoft.Maui.DeviceTests.SwitchHandlerTests.ThumbColorUpdatesCorrectly()
05-28 04:26:14.297 7872 7932 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.297 7872 7932 I DOTNET : Execution time: 0.2217571
05-28 04:26:14.297 7872 7932 I DOTNET : Test trait name: Category
05-28 04:26:14.297 7872 7932 I DOTNET : value: Switch
05-28 04:26:14.297 7872 7932 I DOTNET :
05-28 04:26:14.303 7872 7932 I DOTNET : [PASS] RotationX Initialize Correctly
05-28 04:26:14.310 7872 7932 I DOTNET : [PASS] RotationX Initialize Correctly
05-28 04:26:14.312 7872 7932 I DOTNET : [PASS] Null Thumb Color Doesn't Crash
05-28 04:26:14.316 7872 7932 I DOTNET : [PASS] Setting Semantic Description makes element accessible
05-28 04:26:14.320 7872 7932 I DOTNET : [PASS] Is Toggled Initializes Correctly
05-28 04:26:14.330 7872 7932 I DOTNET : [PASS] FlowDirection is set correctly
05-28 04:26:14.334 7872 7932 I DOTNET : [PASS] FlowDirection is set correctly
05-28 04:26:14.339 7872 7932 I DOTNET : [PASS] Opacity is set correctly
05-28 04:26:14.345 7872 7932 I DOTNET : [PASS] Opacity is set correctly
05-28 04:26:14.345 7872 7932 I DOTNET : [IGNORED] Semantic Hint is set correctly
05-28 04:26:14.349 7872 7932 I DOTNET : [PASS] DisconnectHandlerDoesntCrash
05-28 04:26:14.350 7872 7932 I DOTNET : [PASS] HandlersHaveAllExpectedContructors
05-28 04:26:14.353 7872 7932 I DOTNET : [PASS] Rotation Initialize Correctly
05-28 04:26:14.358 7872 7932 I DOTNET : [PASS] Rotation Initialize Correctly
05-28 04:26:14.361 7872 7932 I DOTNET : [PASS] MinimumHeightInitializes
05-28 04:26:14.362 7872 7932 I DOTNET : [PASS] MinimumHeightInitializes
05-28 04:26:14.410 7872 7932 I DOTNET : [PASS] Visibility is set correctly
05-28 04:26:14.411 7872 7932 I DOTNET : [PASS] Visibility is set correctly
05-28 04:26:14.413 7872 7932 I DOTNET : [PASS] Updating Native Is On property updates Virtual View
05-28 04:26:14.415 7872 7932 I DOTNET : [PASS] Native View Bounds are not empty
05-28 04:26:14.417 7872 7932 I DOTNET : [PASS] Native View Bounds are not empty
05-28 04:26:14.419 7872 7932 I DOTNET : [PASS] ScaleY Initialize Correctly
05-28 04:26:14.422 7872 7932 I DOTNET : [PASS] ScaleY Initialize Correctly
05-28 04:26:14.425 7872 7932 I DOTNET : [PASS] Clip Initializes ContainerView Correctly
05-28 04:26:14.430 7872 7932 I DOTNET : [PASS] Automation Id is set correctly
05-28 04:26:14.432 7872 7932 I DOTNET : [PASS] Null Semantics Doesnt throw exception
05-28 04:26:14.441 7872 7932 I DOTNET : [PASS] Semantic Heading is set correctly
05-28 04:26:14.445 7872 7932 I DOTNET : [PASS] Setting Semantic Hint makes element accessible
05-28 04:26:14.451 7872 7932 I DOTNET : Microsoft.Maui.DeviceTests.SwitchHandlerTests 1.2676205 ms
05-28 04:26:14.462 7872 7893 I DOTNET : Failed tests:
05-28 04:26:14.463 7872 7893 I DOTNET : 1) [FAIL] Thumb Color Updates Correctly Test name: Thumb Color Updates Correctly
05-28 04:26:14.463 7872 7893 I DOTNET : Assembly: [Microsoft.Maui.Core.DeviceTests, Version=10.0.0.0, Culture=neutral, PublicKeyToken=null]
05-28 04:26:14.463 7872 7893 I DOTNET : Exception messages: Color Color [A=255, R=255, G=0, B=0] not found. This is what it looked like:<img>iVBORw0KGgoAAAANSUhEUgAAAIQAAACECAYAAABRRIOnAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAA/rSURBVHic7Z17jB3Vfcc/v3Nm7mMf9u567QVjMDYuD2NMk7agKGkxhTTpU6iCVG1FGlo1UdWmUtWHQqMKXKlVioRKojRRWjVUrdSWh1pUgigtLyUNJBQwxtjYYDCY9WO99q69r7t3Zs45/WPu7J293vWuvXvte+n5SKM7unfuvM53fr/f+Z3HgMfj8Xg8Ho/H4/F4PB6Px+PxeDwej8fj8Xg8Ho/H4/F4PB6Px+PxeDwej8fj8Xg8Ho/H4/F4PB6Px+PxeNoTudAn8P+c/P13F+wscnhBNJf57q80rOfFMJ8wzotgvCCWn8bCXmi9kfnEsRjRLBkviOWh8T5Kw6eaZ9v5XEZ+3TZ81yiGZRWHF8TSaSzgvAjywpAfAxWDXF777Mr9dwJcCO49sCG4V1IhOGYLIS+OplgML4hzZ64nXTWurwPpA9UHcgz4XeAWkCLoXhABceBGwVXBPAPub4A1wAi4EbCDdQHkBdFoORrXl3xRnsXTKAYFyGaQPaA+BvIBEIO6Cwq/BX290HcCLumA9WVYq2BlAEUHIRAbqFo4VYHDU/D+Kjg0CiN/DyMPQhSCvRR4sS6GvEiWTRReEGfPLDdAThBrQa8HdxsEt8LAari2CB8pw/UhXCmwSkEnUJb0P87VdyguLdyKhUkHJxJ4awp2TsGOEdj9NAw9Bsn7IIfBcLo45oszzvriPIsjCw5niWEt6MPg/gD4FdhwDdysYFsI1ytYDxTIFZKQlqICV/vEgqjaRrl6qACRhfdj2Gnh+TfhuYfgwF8Da+vCmEsUmUs5K7wgFsd8VkFthiCC+DroehDuKMDtCrYo6AOC3H9
05-28 04:26:14.463 7872 7893 I DOTNET : at System.Threading.Tasks.Task.<>c.<.cctor>b__288_0(Object obj)
05-28 04:26:14.463 7872 7893 I DOTNET : at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread , ExecutionContext , ContextCallback , Object )
05-28 04:26:14.463 7872 7893 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.463 7872 7893 I DOTNET : at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread , ExecutionContext , ContextCallback , Object )
05-28 04:26:14.463 7872 7893 I DOTNET : at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& , Thread )
05-28 04:26:14.463 7872 7893 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.463 7872 7893 I DOTNET : at Microsoft.Maui.DeviceTests.AssertionExtensions.AssertContainsColor(View view, Color expectedColor, IMauiContext mauiContext, Nullable`1 tolerance)
05-28 04:26:14.463 7872 7893 I DOTNET : at Microsoft.Maui.DeviceTests.HandlerTestBasement.<>c__DisplayClass18_0.<<ValidateHasColor>b__0>d.MoveNext()
05-28 04:26:14.463 7872 7893 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.463 7872 7893 I DOTNET : at Microsoft.Maui.Dispatching.DispatcherExtensions.<>c__DisplayClass3_0.<<DispatchAsync>b__0>d.MoveNext()
05-28 04:26:14.463 7872 7893 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.463 7872 7893 I DOTNET : at Microsoft.Maui.Dispatching.DispatcherExtensions.<>c__DisplayClass2_0`1.<<DispatchAsync>b__0>d[[System.Boolean, System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
05-28 04:26:14.463 7872 7893 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.463 7872 7893 I DOTNET : at Microsoft.Maui.DeviceTests.SwitchHandlerTests.ThumbColorUpdatesCorrectly()
05-28 04:26:14.463 7872 7893 I DOTNET : --- End of stack trace from previous location ---
05-28 04:26:14.463 7872 7893 I DOTNET : Execution time: 0.2217571
05-28 04:26:14.463 7872 7893 I DOTNET : Test trait name: Category
05-28 04:26:14.463 7872 7893 I DOTNET : value: Switch
05-28 04:26:14.463 7872 7893 I DOTNET :
05-28 04:26:14.493 7872 7893 I DOTNET : Xml file was written to the provided writer.
05-28 04:26:14.493 7872 7893 I DOTNET : Tests run: 1323 Passed: 66 Inconclusive: 0 Failed: 1 Ignored: 1253
�[41m�[30mfail�[39m�[22m�[49m: Non-success instrumentation exit code: 1, expected: 0
�[40m�[32minfo�[39m�[22m�[49m: <<XHARNESS_RESULT_START>>
{
"version": 1,
"machineName": "runnervmfuwn7",
"exitCode": 1,
"exitCodeName": "TESTS_FAILED",
"platform": "android",
"instrumentationExitCode": 1,
"device": "emulator-5554",
"deviceOsVersion": "API 30",
"architecture": "x86_64",
"files": [
{
"name": "testResults.xml",
"type": "test-results"
},
{
"name": "adb-logcat-com.microsoft.maui.core.devicetests-default.log",
"type": "logcat"
}
]
}
<<XHARNESS_RESULT_END>>
�[40m�[32minfo�[39m�[22m�[49m: Attempting to remove apk 'com.microsoft.maui.core.devicetests'..
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 uninstall com.microsoft.maui.core.devicetests'
�[40m�[32minfo�[39m�[22m�[49m: Successfully uninstalled com.microsoft.maui.core.devicetests
XHarness exit code: 1 (TESTS_FAILED)
Tests completed with exit code: 1
🟢 With fix — 📱 SwitchHandlerTests (CustomColorsUseSlidingStyleOniOS26, CustomColorsRenderOnInitialOffStateOniOS26, DefaultSwitchUsesAutomaticStyleOniOS26, ThumbColorClearsWhenResetOniOS26, CustomColorsReapplyAfterMovedToWindowOniOS26, CustomColorsUpdateAfterAppThemeChangeOniOS26): FAIL ❌ · 1018s
(truncated to last 15,000 chars)
n.Runtime.Android.dll -> Xamarin.AndroidX.Navigation.Runtime.Android.dll.so
[116/129] System.Threading.Thread.dll -> System.Threading.Thread.dll.so
[117/129] System.Text.Json.dll -> System.Text.Json.dll.so
[50/129] Xamarin.AndroidX.Navigation.UI.dll -> Xamarin.AndroidX.Navigation.UI.dll.so
[118/129] System.Threading.ThreadPool.dll -> System.Threading.ThreadPool.dll.so
[119/129] System.Threading.dll -> System.Threading.dll.so
[120/129] System.Xml.Linq.dll -> System.Xml.Linq.dll.so
[51/129] Xamarin.AndroidX.RecyclerView.dll -> Xamarin.AndroidX.RecyclerView.dll.so
[121/129] System.Xml.ReaderWriter.dll -> System.Xml.ReaderWriter.dll.so
[122/129] System.Xml.XDocument.dll -> System.Xml.XDocument.dll.so
[123/129] System.dll -> System.dll.so
[52/129] Xamarin.AndroidX.SavedState.SavedState.Android.dll -> Xamarin.AndroidX.SavedState.SavedState.Android.dll.so
[124/129] netstandard.dll -> netstandard.dll.so
[125/129] Mono.Android.Export.dll -> Mono.Android.Export.dll.so
[53/129] Xamarin.AndroidX.SwipeRefreshLayout.dll -> Xamarin.AndroidX.SwipeRefreshLayout.dll.so
[126/129] Mono.Android.Runtime.dll -> Mono.Android.Runtime.dll.so
[54/129] Xamarin.AndroidX.ViewPager.dll -> Xamarin.AndroidX.ViewPager.dll.so
[127/129] Java.Interop.dll -> Java.Interop.dll.so
[55/129] Xamarin.AndroidX.ViewPager2.dll -> Xamarin.AndroidX.ViewPager2.dll.so
[56/129] Xamarin.Google.Android.Material.dll -> Xamarin.Google.Android.Material.dll.so
[57/129] Xamarin.Kotlin.StdLib.dll -> Xamarin.Kotlin.StdLib.dll.so
[58/129] Xamarin.KotlinX.Coroutines.Core.Jvm.dll -> Xamarin.KotlinX.Coroutines.Core.Jvm.dll.so
[59/129] Xamarin.KotlinX.Serialization.Core.Jvm.dll -> Xamarin.KotlinX.Serialization.Core.Jvm.dll.so
[60/129] xunit.abstractions.dll -> xunit.abstractions.dll.so
[128/129] Mono.Android.dll -> Mono.Android.dll.so
[61/129] xunit.assert.dll -> xunit.assert.dll.so
[62/129] xunit.core.dll -> xunit.core.dll.so
[63/129] xunit.execution.dotnet.dll -> xunit.execution.dotnet.dll.so
[64/129] xunit.runner.utility.netcoreapp10.dll -> xunit.runner.utility.netcoreapp10.dll.so
[65/129] System.Collections.Concurrent.dll -> System.Collections.Concurrent.dll.so
[66/129] System.Collections.Immutable.dll -> System.Collections.Immutable.dll.so
[67/129] System.Collections.NonGeneric.dll -> System.Collections.NonGeneric.dll.so
[68/129] System.Collections.Specialized.dll -> System.Collections.Specialized.dll.so
[69/129] System.Collections.dll -> System.Collections.dll.so
[70/129] System.ComponentModel.Primitives.dll -> System.ComponentModel.Primitives.dll.so
[71/129] System.ComponentModel.TypeConverter.dll -> System.ComponentModel.TypeConverter.dll.so
[72/129] System.ComponentModel.dll -> System.ComponentModel.dll.so
[73/129] System.Console.dll -> System.Console.dll.so
[74/129] System.Diagnostics.Debug.dll -> System.Diagnostics.Debug.dll.so
[75/129] System.Diagnostics.DiagnosticSource.dll -> System.Diagnostics.DiagnosticSource.dll.so
[129/129] System.Private.CoreLib.dll -> System.Private.CoreLib.dll.so
[76/129] System.Diagnostics.Process.dll -> System.Diagnostics.Process.dll.so
[77/129] System.Diagnostics.Tools.dll -> System.Diagnostics.Tools.dll.so
[78/129] System.Diagnostics.TraceSource.dll -> System.Diagnostics.TraceSource.dll.so
[79/129] System.Diagnostics.Tracing.dll -> System.Diagnostics.Tracing.dll.so
[80/129] System.Drawing.Primitives.dll -> System.Drawing.Primitives.dll.so
[81/129] System.Drawing.dll -> System.Drawing.dll.so
[82/129] System.Formats.Asn1.dll -> System.Formats.Asn1.dll.so
[83/129] System.Globalization.dll -> System.Globalization.dll.so
[84/129] System.IO.Compression.Brotli.dll -> System.IO.Compression.Brotli.dll.so
[85/129] System.IO.Compression.dll -> System.IO.Compression.dll.so
[86/129] System.IO.FileSystem.dll -> System.IO.FileSystem.dll.so
[87/129] System.IO.Pipelines.dll -> System.IO.Pipelines.dll.so
[88/129] System.IO.dll -> System.IO.dll.so
[89/129] System.Linq.Expressions.dll -> System.Linq.Expressions.dll.so
[90/129] System.Linq.dll -> System.Linq.dll.so
[91/129] System.Memory.dll -> System.Memory.dll.so
[92/129] System.Net.Http.dll -> System.Net.Http.dll.so
[93/129] System.Net.NameResolution.dll -> System.Net.NameResolution.dll.so
[94/129] System.Net.Primitives.dll -> System.Net.Primitives.dll.so
[95/129] System.Net.Requests.dll -> System.Net.Requests.dll.so
[96/129] System.Net.Sockets.dll -> System.Net.Sockets.dll.so
[97/129] System.Numerics.Vectors.dll -> System.Numerics.Vectors.dll.so
[98/129] System.ObjectModel.dll -> System.ObjectModel.dll.so
[99/129] System.Private.Uri.dll -> System.Private.Uri.dll.so
[100/129] System.Private.Xml.Linq.dll -> System.Private.Xml.Linq.dll.so
[101/129] System.Private.Xml.dll -> System.Private.Xml.dll.so
[102/129] System.Reflection.Extensions.dll -> System.Reflection.Extensions.dll.so
[103/129] System.Reflection.TypeExtensions.dll -> System.Reflection.TypeExtensions.dll.so
[104/129] System.Reflection.dll -> System.Reflection.dll.so
[105/129] System.Runtime.Extensions.dll -> System.Runtime.Extensions.dll.so
[106/129] System.Runtime.InteropServices.RuntimeInformation.dll -> System.Runtime.InteropServices.RuntimeInformation.dll.so
[107/129] System.Runtime.InteropServices.dll -> System.Runtime.InteropServices.dll.so
[108/129] System.Runtime.Loader.dll -> System.Runtime.Loader.dll.so
[109/129] System.Runtime.Numerics.dll -> System.Runtime.Numerics.dll.so
[110/129] System.Runtime.dll -> System.Runtime.dll.so
[111/129] System.Security.Cryptography.dll -> System.Security.Cryptography.dll.so
[112/129] System.Text.Encoding.dll -> System.Text.Encoding.dll.so
[113/129] System.Text.Encodings.Web.dll -> System.Text.Encodings.Web.dll.so
[114/129] System.Text.Json.dll -> System.Text.Json.dll.so
[115/129] System.Text.RegularExpressions.dll -> System.Text.RegularExpressions.dll.so
[116/129] System.Threading.Tasks.dll -> System.Threading.Tasks.dll.so
[117/129] System.Threading.Thread.dll -> System.Threading.Thread.dll.so
[118/129] System.Threading.ThreadPool.dll -> System.Threading.ThreadPool.dll.so
[119/129] System.Threading.dll -> System.Threading.dll.so
[120/129] System.Xml.Linq.dll -> System.Xml.Linq.dll.so
[121/129] System.Xml.ReaderWriter.dll -> System.Xml.ReaderWriter.dll.so
[122/129] System.Xml.XDocument.dll -> System.Xml.XDocument.dll.so
[123/129] System.dll -> System.dll.so
[124/129] netstandard.dll -> netstandard.dll.so
[125/129] Java.Interop.dll -> Java.Interop.dll.so
[126/129] Mono.Android.Export.dll -> Mono.Android.Export.dll.so
[127/129] Mono.Android.Runtime.dll -> Mono.Android.Runtime.dll.so
[128/129] Mono.Android.dll -> Mono.Android.dll.so
[129/129] System.Private.CoreLib.dll -> System.Private.CoreLib.dll.so
Build succeeded.
/home/vsts/work/1/s/src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt(23,1): warning RS0025: The symbol 'override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void' appears more than once in the public API files (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [/home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj::TargetFramework=net10.0-android36.0]
1 Warning(s)
0 Error(s)
Time Elapsed 00:06:47.62
[11.0.0-prerelease.26230.4+92962e5c46ac08a66ded4c5696209cc60f1a232f] XHarness command issued: android test --app /home/vsts/work/1/s/artifacts/bin/Core.DeviceTests/Release/net10.0-android/com.microsoft.maui.core.devicetests-Signed.apk --package-name com.microsoft.maui.core.devicetests --device-id emulator-5554 -o artifacts/log --timeout 01:00:00 -v --arg TestFilter=Category=Switch
�[40m�[37mdbug�[39m�[22m�[49m: ADBRunner using ADB.exe supplied from /home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/tools/net10.0/any/../../../runtimes/any/native/adb/linux/adb
�[40m�[37mdbug�[39m�[22m�[49m: Full resolved path:'/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb'
�[40m�[32minfo�[39m�[22m�[49m: Will attempt to find device supporting architectures: 'arm64-v8a', 'x86_64'
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb start-server'
�[40m�[37mdbug�[39m�[22m�[49m:
�[40m�[32minfo�[39m�[22m�[49m: Finding attached devices/emulators...
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb devices -l'
�[40m�[37mdbug�[39m�[22m�[49m: Found 1 possible devices
�[40m�[37mdbug�[39m�[22m�[49m: Evaluating output line for device serial: emulator-5554 device product:sdk_gphone_x86_64 model:sdk_gphone_x86_64 device:generic_x86_64_arm64 transport_id:1
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 shell getprop ro.product.cpu.abilist'
�[40m�[37mdbug�[39m�[22m�[49m: Found 1 possible devices. Using 'emulator-5554'
�[40m�[32minfo�[39m�[22m�[49m: Active Android device set to serial 'emulator-5554'
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 -s emulator-5554 shell getprop ro.product.cpu.abi'
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 -s emulator-5554 shell getprop ro.build.version.sdk'
�[40m�[32minfo�[39m�[22m�[49m: Waiting for device to be available (max 5 minutes)
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 wait-for-device'
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 -s emulator-5554 shell getprop sys.boot_completed'
�[40m�[37mdbug�[39m�[22m�[49m: sys.boot_completed = '1'
�[40m�[37mdbug�[39m�[22m�[49m: Waited 0 seconds for device boot completion
�[40m�[37mdbug�[39m�[22m�[49m: Working with emulator-5554 (API 30)
�[40m�[37mdbug�[39m�[22m�[49m: Check current adb install and/or package verification settings
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 shell settings get global verifier_verify_adb_installs'
�[40m�[37mdbug�[39m�[22m�[49m: verifier_verify_adb_installs = 0
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 shell settings get global package_verifier_enable'
�[40m�[37mdbug�[39m�[22m�[49m: package_verifier_enable =
�[40m�[1m�[33mwarn�[39m�[22m�[49m: Installing debug apks on a device might be rejected with INSTALL_FAILED_VERIFICATION_FAILURE. Make sure to set 'package_verifier_enable' to '0'
�[40m�[32minfo�[39m�[22m�[49m: Attempting to remove apk 'com.microsoft.maui.core.devicetests'..
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 uninstall com.microsoft.maui.core.devicetests'
�[41m�[30mfail�[39m�[22m�[49m: Waiting for command timed out: execution may be compromised
�[41m�[30mfail�[39m�[22m�[49m: Error: Exit code: -2
Std out:
�[40m�[32minfo�[39m�[22m�[49m: Attempting to install /home/vsts/work/1/s/artifacts/bin/Core.DeviceTests/Release/net10.0-android/com.microsoft.maui.core.devicetests-Signed.apk
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 install /home/vsts/work/1/s/artifacts/bin/Core.DeviceTests/Release/net10.0-android/com.microsoft.maui.core.devicetests-Signed.apk'
�[41m�[30mfail�[39m�[22m�[49m: Waiting for command timed out: execution may be compromised
�[40m�[1m�[33mwarn�[39m�[22m�[49m: Installation failed; Will make one attempt to restart ADB server and the device, then retry the install
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 kill-server'
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 start-server'
�[40m�[37mdbug�[39m�[22m�[49m:
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 reboot'
�[41m�[1m�[37mcrit�[39m�[22m�[49m: Failed to reboot the device
Exit code: 1
Std out:
Std err:
error: device still authorizing
Microsoft.DotNet.XHarness.Android.AdbFailureException: Failed to reboot the device
Exit code: 1
Std out:
Std err:
error: device still authorizing
at Microsoft.DotNet.XHarness.Android.Execution.ProcessExecutionResults.ThrowIfFailed(String failureMessage) in /_/src/Microsoft.DotNet.XHarness.Android/Execution/IAdbProcessManager.cs:line 24
at Microsoft.DotNet.XHarness.Android.AdbRunner.RebootAndroidDevice() in /_/src/Microsoft.DotNet.XHarness.Android/AdbRunner.cs:line 130
at Microsoft.DotNet.XHarness.Android.AdbRunner.InstallApk(String apkPath) in /_/src/Microsoft.DotNet.XHarness.Android/AdbRunner.cs:line 690
at Microsoft.DotNet.XHarness.CLI.Commands.Android.AndroidInstallCommand.InvokeHelper(ILogger logger, String apkPackageName, String appPackagePath, IReadOnlyCollection`1 requestedArchitectures, String deviceId, Nullable`1 apiVersion, TimeSpan bootTimeoutSeconds, AdbRunner runner, IDiagnosticsData diagnosticsData) in /_/src/Microsoft.DotNet.XHarness.CLI/Commands/Android/AndroidInstallCommand.cs:line 138
at Microsoft.DotNet.XHarness.CLI.Commands.Android.AndroidTestCommand.InvokeCommand(ILogger logger) in /_/src/Microsoft.DotNet.XHarness.CLI/Commands/Android/AndroidTestCommand.cs:line 42
at Microsoft.DotNet.XHarness.CLI.Android.AndroidCommand`1.InvokeInternal(ILogger logger) in /_/src/Microsoft.DotNet.XHarness.CLI/Commands/Android/AndroidCommand.cs:line 32
XHarness exit code: 91 (ADB_FAILURE)
Tests completed with exit code: 91
⚠️ Failure Details
- ❌ SwitchHandlerTests (CustomColorsUseSlidingStyleOniOS26, CustomColorsRenderOnInitialOffStateOniOS26, DefaultSwitchUsesAutomaticStyleOniOS26, ThumbColorClearsWhenResetOniOS26, CustomColorsReapplyAfterMovedToWindowOniOS26, CustomColorsUpdateAfterAppThemeChangeOniOS26) FAILED with fix (should pass)
📁 Fix files reverted (237 files)
eng/Signing.propseng/pipelines/ci-copilot.ymlsrc/BlazorWebView/src/Maui/Android/BlazorAndroidWebView.cssrc/BlazorWebView/src/Maui/Android/BlazorWebViewHandler.Android.cssrc/BlazorWebView/src/Maui/Android/WebKitWebViewClient.cssrc/Compatibility/Core/src/Android/Renderers/SwipeViewRenderer.cssrc/Compatibility/Core/src/MacOS/Extensions/NSMenuExtensions.cssrc/Compatibility/Core/src/iOS/EventTracker.cssrc/Compatibility/Core/src/iOS/Renderers/SwipeViewRenderer.cssrc/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapPinsGallery.xamlsrc/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapPinsGallery.xaml.cssrc/Controls/src/Build.Tasks/SetPropertiesVisitor.cssrc/Controls/src/Core/ActionSheetArguments.cssrc/Controls/src/Core/AlertArguments.cssrc/Controls/src/Core/BindableObject.cssrc/Controls/src/Core/BindableProperty.cssrc/Controls/src/Core/Button/Button.iOS.cssrc/Controls/src/Core/Compatibility/Android/Resources/layout/flyoutcontent.axmlsrc/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/Android/SearchHandlerAppearanceTracker.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFragmentContainer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SearchHandlerAppearanceTracker.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutLayoutManager.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRootRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellTableViewController.cssrc/Controls/src/Core/Editor/Editor.Mapper.cssrc/Controls/src/Core/Editor/Editor.iOS.cssrc/Controls/src/Core/Element/Element.cssrc/Controls/src/Core/Entry/Entry.Mapper.cssrc/Controls/src/Core/Entry/Entry.iOS.cssrc/Controls/src/Core/FlyoutPage/FlyoutPage.cssrc/Controls/src/Core/FormattedString.cssrc/Controls/src/Core/GestureElement.cssrc/Controls/src/Core/Handlers/Items/Android/Adapters/GroupableItemsViewAdapter.cssrc/Controls/src/Core/Handlers/Items/Android/Adapters/ItemsViewAdapter.cssrc/Controls/src/Core/Handlers/Items/Android/GridLayoutSpanSizeLookup.cssrc/Controls/src/Core/Handlers/Items/Android/ItemContentView.cssrc/Controls/src/Core/Handlers/Items/Android/MauiCarouselRecyclerView.cssrc/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cssrc/Controls/src/Core/Handlers/Items/Android/TemplatedItemViewHolder.cssrc/Controls/src/Core/Handlers/Items/CarouselViewHandler.Windows.cssrc/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cssrc/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cssrc/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cssrc/Controls/src/Core/Handlers/Items2/iOS/GroupableItemsViewController2.cssrc/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cssrc/Controls/src/Core/Handlers/Items2/iOS/ItemsViewDelegator2.cssrc/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cssrc/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cssrc/Controls/src/Core/Handlers/Shapes/Polygon/PolygonHandler.Android.cssrc/Controls/src/Core/Handlers/Shapes/Polygon/PolygonHandler.Tizen.cssrc/Controls/src/Core/Handlers/Shapes/Polygon/PolygonHandler.Windows.cssrc/Controls/src/Core/Handlers/Shapes/Polygon/PolygonHandler.cssrc/Controls/src/Core/Handlers/Shapes/Polygon/PolygonHandler.iOS.cssrc/Controls/src/Core/Handlers/Shapes/Polyline/PolylineHandler.Android.cssrc/Controls/src/Core/Handlers/Shapes/Polyline/PolylineHandler.Tizen.cssrc/Controls/src/Core/Handlers/Shapes/Polyline/PolylineHandler.Windows.cssrc/Controls/src/Core/Handlers/Shapes/Polyline/PolylineHandler.cssrc/Controls/src/Core/Handlers/Shapes/Polyline/PolylineHandler.iOS.cssrc/Controls/src/Core/Handlers/Shell/ShellItemHandler.Windows.cssrc/Controls/src/Core/Handlers/Shell/Windows/ShellFlyoutItemView.cssrc/Controls/src/Core/Handlers/Shell/Windows/ShellView.cssrc/Controls/src/Core/Hosting/AppHostBuilderExtensions.cssrc/Controls/src/Core/ImageElement.cssrc/Controls/src/Core/Internals/WeakEventProxy.cssrc/Controls/src/Core/Items/SelectionList.cssrc/Controls/src/Core/Label/Label.Mapper.cssrc/Controls/src/Core/Label/Label.cssrc/Controls/src/Core/Label/Label.iOS.cssrc/Controls/src/Core/ListView/ListView.cssrc/Controls/src/Core/NavigationPage/NavigationPage.cssrc/Controls/src/Core/Page/Page.cssrc/Controls/src/Core/Platform/AlertManager/AlertManager.Android.cssrc/Controls/src/Core/Platform/AlertManager/AlertManager.cssrc/Controls/src/Core/Platform/Android/BottomNavigationViewUtils.cssrc/Controls/src/Core/Platform/Android/DragAndDropGestureHandler.cssrc/Controls/src/Core/Platform/Android/GenericAnimatorListener.cssrc/Controls/src/Core/Platform/Android/TabbedPageManager.cssrc/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.iOS.cssrc/Controls/src/Core/Platform/Windows/CollectionView/ScrollHelpers.cssrc/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cssrc/Controls/src/Core/Platform/iOS/Extensions/LabelExtensions.cssrc/Controls/src/Core/PromptArguments.cssrc/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txtsrc/Controls/src/Core/RadioButton/RadioButton.cssrc/Controls/src/Core/RadioButton/RadioButtonGroup.cssrc/Controls/src/Core/RadioButton/RadioButtonGroupController.cssrc/Controls/src/Core/ResourcesExtensions.cssrc/Controls/src/Core/Setter.cssrc/Controls/src/Core/Shadow.cssrc/Controls/src/Core/Shapes/Shape.cssrc/Controls/src/Core/Shell/Shell.cssrc/Controls/src/Core/Shell/ShellNavigationManager.cssrc/Controls/src/Core/SwipeView/SwipeItem.cssrc/Controls/src/Core/SwipeView/SwipeItemView.cssrc/Controls/src/Core/SwipeView/SwipeItems.cssrc/Controls/src/Core/SwipeView/SwipeView.cssrc/Controls/src/Core/TabbedPage/TabbedPage.Windows.cssrc/Controls/src/Core/VisualElement/VisualElement.cssrc/Controls/src/Core/VisualStateManager.cssrc/Controls/src/Core/WebView/WebViewSource.cssrc/Controls/src/Core/Window/Window.Android.cssrc/Controls/src/Core/Window/Window.cssrc/Controls/src/SourceGen/NodeSGExtensions.cssrc/Controls/src/Xaml/ApplyPropertiesVisitor.cssrc/Controls/src/Xaml/MarkupExtensions/OnIdiomExtension.cssrc/Controls/src/Xaml/MarkupExtensions/StaticResourceExtension.cssrc/Core/maps/src/Handlers/Map/MapHandler.Android.cssrc/Core/maps/src/Handlers/Map/MapHandler.iOS.cssrc/Core/maps/src/Handlers/MapPin/MapPinHandler.Android.cssrc/Core/maps/src/Platform/iOS/MauiMKMapView.cssrc/Core/maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txtsrc/Core/src/Diagnostics/DiagnosticsManager.cssrc/Core/src/Diagnostics/IDiagnosticsManager.cssrc/Core/src/Diagnostics/Instrumentation/DiagnosticInstrumentation.cssrc/Core/src/Diagnostics/Instrumentation/LayoutArrangeInstrumentation.cssrc/Core/src/Diagnostics/Instrumentation/LayoutDiagnosticMetrics.cssrc/Core/src/Diagnostics/Instrumentation/LayoutMeasureInstrumentation.cssrc/Core/src/Graphics/MauiDrawable.Android.cssrc/Core/src/Handlers/Button/ButtonHandler.Android.cssrc/Core/src/Handlers/Button/ButtonHandler.cssrc/Core/src/Handlers/Button/ButtonHandler.iOS.cssrc/Core/src/Handlers/DatePicker/DatePickerHandler.Android.cssrc/Core/src/Handlers/DatePicker/DatePickerHandler.MacCatalyst.cssrc/Core/src/Handlers/Editor/EditorHandler.iOS.cssrc/Core/src/Handlers/Entry/EntryHandler.cssrc/Core/src/Handlers/Entry/EntryHandler.iOS.cssrc/Core/src/Handlers/FlyoutView/FlyoutViewHandler.Android.cssrc/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Standard.cssrc/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Tizen.cssrc/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Windows.cssrc/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cssrc/Core/src/Handlers/Image/ImageHandler.Windows.cssrc/Core/src/Handlers/Image/ImageHandler.iOS.cssrc/Core/src/Handlers/Label/LabelHandler.cssrc/Core/src/Handlers/Label/LabelHandler.iOS.cssrc/Core/src/Handlers/Picker/PickerHandler.iOS.cssrc/Core/src/Handlers/ProgressBar/ProgressBarHandler.iOS.cssrc/Core/src/Handlers/RadioButton/RadioButtonHandler.cssrc/Core/src/Handlers/RadioButton/RadioButtonHandler.iOS.cssrc/Core/src/Handlers/RefreshView/RefreshViewHandler.Windows.cssrc/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cssrc/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cssrc/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cssrc/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cssrc/Core/src/Handlers/SearchBar/SearchBarHandler2.Android.cssrc/Core/src/Handlers/ShapeView/ShapeViewHandler.Standard.cssrc/Core/src/Handlers/ShapeView/ShapeViewHandler.cssrc/Core/src/Handlers/Stepper/StepperHandler.iOS.cssrc/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Android.cssrc/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.iOS.cssrc/Core/src/Handlers/Switch/SwitchHandler.iOS.cssrc/Core/src/Handlers/TimePicker/TimePickerHandler.Android.cssrc/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cssrc/Core/src/Handlers/TimePicker/TimePickerHandler.cssrc/Core/src/Handlers/TimePicker/TimePickerHandler.iOS.cssrc/Core/src/Handlers/WebView/WebViewHandler.Android.cssrc/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cssrc/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cssrc/Core/src/Platform/Android/ActivityIndicatorExtensions.cssrc/Core/src/Platform/Android/ActivityResultCallbackRegistry.cssrc/Core/src/Platform/Android/BorderDrawable.cssrc/Core/src/Platform/Android/ContainerView.cssrc/Core/src/Platform/Android/EditTextExtensions.cssrc/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cssrc/Core/src/Platform/Android/MauiAppCompatActivity.cssrc/Core/src/Platform/Android/MauiHybridWebView.cssrc/Core/src/Platform/Android/MauiHybridWebViewClient.cssrc/Core/src/Platform/Android/MauiScrollView.cssrc/Core/src/Platform/Android/MauiSearchView.cssrc/Core/src/Platform/Android/MauiSwipeRefreshLayout.cssrc/Core/src/Platform/Android/MauiSwipeView.cssrc/Core/src/Platform/Android/MauiWebView.cssrc/Core/src/Platform/Android/MauiWebViewClient.cssrc/Core/src/Platform/Android/Navigation/NavigationRootManager.cssrc/Core/src/Platform/Android/RadioButtonExtensions.cssrc/Core/src/Platform/Android/SafeAreaExtensions.cssrc/Core/src/Platform/Android/SearchViewExtensions.cssrc/Core/src/Platform/Android/TimePickerExtensions.cssrc/Core/src/Platform/Windows/ContentPanel.cssrc/Core/src/Platform/Windows/LayoutPanel.cssrc/Core/src/Platform/Windows/MauiPasswordTextBox.cssrc/Core/src/Platform/Windows/MauiToolbar.xaml.cssrc/Core/src/Platform/Windows/RadioButtonExtensions.cssrc/Core/src/Platform/Windows/RootNavigationView.cssrc/Core/src/Platform/Windows/ScrollViewerExtensions.cssrc/Core/src/Platform/Windows/TimePickerExtensions.cssrc/Core/src/Platform/Windows/WebViewExtensions.cssrc/Core/src/Platform/iOS/ButtonExtensions.cssrc/Core/src/Platform/iOS/KeyboardAcceleratorExtensions.cssrc/Core/src/Platform/iOS/LayerExtensions.cssrc/Core/src/Platform/iOS/MauiPageControl.cssrc/Core/src/Platform/iOS/MauiScrollView.cssrc/Core/src/Platform/iOS/MauiSwipeView.cssrc/Core/src/Platform/iOS/MauiTextView.cssrc/Core/src/Platform/iOS/MauiView.cssrc/Core/src/Platform/iOS/PickerExtensions.cssrc/Core/src/Platform/iOS/SwitchExtensions.cssrc/Core/src/Platform/iOS/TabbedViewExtensions.cssrc/Core/src/Platform/iOS/TextFieldExtensions.cssrc/Core/src/Platform/iOS/TimePickerExtensions.cssrc/Core/src/Platform/iOS/WrapperView.cssrc/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txtsrc/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txtsrc/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txtsrc/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txtsrc/Core/src/ViewExtensions.cssrc/Core/src/WindowExtensions.cssrc/Essentials/src/AssemblyInfo/AssemblyInfo.shared.cssrc/Essentials/src/Clipboard/Clipboard.shared.cssrc/Essentials/src/FilePicker/FilePicker.tizen.cssrc/Essentials/src/FileSystem/FileSystemUtils.android.cssrc/Essentials/src/FileSystem/FileSystemUtils.shared.cssrc/Essentials/src/MainThread/MainThread.netstandard.cssrc/Essentials/src/MainThread/MainThread.shared.cssrc/Essentials/src/MediaPicker/MediaPicker.ios.cssrc/Essentials/src/MediaPicker/MediaPicker.tizen.cssrc/Essentials/src/Types/Shared/WebUtils.shared.cssrc/Graphics/src/Graphics/Platforms/Android/PlatformGraphicsView.cssrc/Graphics/src/Graphics/Platforms/MaciOS/PlatformCanvas.cssrc/Graphics/src/Graphics/Platforms/Windows/PlatformGraphicsView.cssrc/Graphics/src/Graphics/Platforms/iOS/PlatformGraphicsView.cssrc/SingleProject/Resizetizer/src/GenerateTizenManifest.cssrc/SingleProject/Resizetizer/src/SkiaSharpSvgTools.cs
New files (not reverted):
src/Controls/src/Core/Handlers/Items/iOS/IScrollTrackingDelegator.cssrc/Controls/src/Core/Platform/AlertManager/DelegateAlertSubscription.cssrc/Core/src/Handlers/HybridWebView/HybridWebViewHelper.cssrc/Core/src/Platform/Android/AppbarLayoutExtensions.cssrc/Core/src/Platform/Android/IBackNavigationState.cssrc/Core/src/Platform/Android/RefreshViewWebViewScrollCapture.cssrc/Core/src/Platform/Windows/MauiLayoutAutomationPeer.cssrc/Core/src/Platform/iOS/MauiProgressView.cssrc/Core/src/Platform/iOS/MauiSwitch.cssrc/Core/src/ScreenshotDispatch.cs
🧪 UI Tests — ViewBaseTests
Detected UI test categories: ViewBaseTests
🔍 Regression Cross-Reference
🔍 Regression Cross-Reference
🟢 No regression risks detected. No labeled bug-fix PRs in the last 6 months touched the modified files.
🔍 Pre-Flight — Context & Validation
Issue: #35257 - iOS 26 Switch default color for Off and On is incorrect + Off Color is not applied at start + Thumb Colors is not applied
PR: #35385 - [iOS] Fix Switch custom colors on iOS 26
Platforms Affected: iOS, MacCatalyst
Files Changed: 3 implementation, 2 test
Key Findings
- The PR changes iOS/MacCatalyst Switch handling to use
UISwitchStyle.Slidingfor custom-colored switches on iOS/MacCatalyst 26+, reapply colors after lifecycle/style changes, clear thumb tint when reset, and add focused handler/UI coverage. - The linked issue reports iOS 26+ Switch custom OffColor/TrackColor and ThumbColor rendering regressions; validation comments confirm reproducibility on iOS.
- Existing unresolved review feedback focuses on preserving native Automatic/Liquid Glass defaults for unstyled switches; reapply paths can still call color application when both
TrackColorandThumbColorare null. - Gate was already completed separately and failed; per instruction it was not re-run and
gate/content.mdwas not modified.
Code Review Summary
Verdict: NEEDS_DISCUSSION
Confidence: medium
Errors: 0 | Warnings: 1 | Suggestions: 0
Key code review findings:
⚠️ src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs:133andsrc/Core/src/Platform/iOS/MauiSwitch.cs:91reapply track/thumb colors without first checking whetherTrackColororThumbColoris set. For an uncustomized off switch on iOS/MacCatalyst 26,UpdatePreferredStylekeepsUISwitchStyle.Automatic, butApplyTrackColorcan write fallbackSecondarySystemFillinto the internal track subview, conflicting with native default preservation.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #35385 | Use MauiSwitch, UISwitchStyle.Sliding for custom colors, lifecycle reapply, thumb reset clearing, and tests |
❌ FAILED (Gate, pre-run) | SwitchHandler.iOS.cs, MauiSwitch.cs, SwitchExtensions.cs, tests |
Original PR; gate output left intact |
🔬 Code Review — Deep Analysis
Code Review — PR #35385
Independent Assessment
What this changes: The PR changes the iOS/MacCatalyst Switch handler to create a MauiSwitch subclass, switch UISwitch to Sliding style on iOS/MacCatalyst 26+ when MAUI track/thumb colors are set, and reapply track/thumb colors after connect, layout, window attach, and trait changes. It also adds focused iOS device tests and enables HostApp startup test selection on iOS.
Inferred motivation: iOS 26 appears to reset or ignore custom Switch colors under the default/automatic switch style, so the change opts customized switches into the classic style and retries color application after UIKit lifecycle/style resets.
Reconciliation with PR Narrative
Author claims: The PR fixes #35257 by using UISwitchStyle.Sliding only for switches with non-null MAUI colors, leaves unstyled switches as Automatic to preserve native iOS 26 appearance, reapplies colors after lifecycle resets, clears thumb tint when reset, and adds handler/Appium visual coverage.
Agreement/disagreement: The code matches the custom-color/style and reapply goals, and the added tests cover initial render, reset, reattach, and theme-change paths. The remaining concern is with the native-default preservation claim: new reapply paths still call UpdateTrackColor/ApplyTrackColor for uncustomized switches, which can mutate the private off-track subview even while PreferredStyle remains Automatic.
Findings
⚠️ Warning — Unstyled iOS 26 switches can still have native Automatic defaults overwritten
src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs:133 and src/Core/src/Platform/iOS/MauiSwitch.cs:91 reapply track/thumb colors without first checking whether TrackColor or ThumbColor is set. For an uncustomized off switch on iOS/MacCatalyst 26, UpdatePreferredStyle keeps UISwitchStyle.Automatic, but ApplyTrackColor then writes the fallback SecondarySystemFill into the internal track subview. That conflicts with the stated goal of preserving the native Automatic/Liquid Glass default for unstyled switches. This is already present as unresolved PR feedback, so I would not add a duplicate inline comment, but it should be addressed or explicitly accepted before merge.
Devil's Advocate
The fallback off-track assignment existed before this PR in the normal TrackColor mapper path, so the new code may not be the first place that can touch an unstyled switch. However, this PR adds explicit connect/lifecycle reapply paths and claims unstyled iOS 26 switches preserve native defaults; those added paths make the default-rendering risk more likely and directly relevant. The expert reviewer returned no new inline findings beyond existing unresolved comments. CI could not be checked through gh pr checks because the local CLI is unauthenticated; GitHub check data available via API shows overall status pending with Build Analysis still in progress.
Verdict: NEEDS_DISCUSSION
Confidence: medium
Summary: The custom-color approach is generally sound and the tests are targeted, but there is an unresolved native-default preservation concern on newly added reapply paths. I would not call this LGTM until that concern is resolved or a maintainer explicitly accepts the behavior, and pending CI also prevents an LGTM verdict.
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix-1 / claude-opus-4.6 | Gate lifecycle color reapply/application on custom color existence (HasCustomColors) |
3 files | Android cannot validate iOS 26 UISwitch behavior; self-review clean. Full diff: try-fix/attempt-1/fix.diff |
|
| 2 | try-fix-2 / claude-opus-4.7 | Preserve native defaults in low-level color application by skipping writes when iOS/MacCatalyst 26+ switch remains Automatic |
3 files | Simplest candidate; Android run cannot validate iOS-only path; self-review clean. Full diff: try-fix/attempt-2/fix.diff |
|
| 3 | try-fix-3 / gpt-5.3-codex | Capture and restore native UIKit switch defaults when MAUI custom colors are absent | 3 files | More complex stateful default preservation; Android cannot validate iOS-only path; self-review clean. Full diff: try-fix/attempt-3/fix.diff |
|
| 4 | try-fix-4 / gpt-5.5 | Drive custom-color refresh through handler mapper UpdateValue ordering rather than direct lifecycle apply |
3 files | Preserves mapper extensibility but still blocked on Android-only validation; self-review clean. Full diff: try-fix-4/fix.diff |
|
| PR | PR #35385 | Current PR fix: MauiSwitch, style switching, lifecycle reapply, thumb reset clearing, tests |
❌ FAILED (Gate) | 5 files | Gate failed before this phase and was not re-run |
Candidate Details
| # | Failure Analysis / Learning |
|---|---|
| 1 | Broad custom-color gates address default preservation but are invasive across lifecycle and helper code. Android test execution cannot exercise the iOS/MacCatalyst .iOS.cs path. |
| 2 | Moving default preservation into ApplyTrackColor/ApplyThumbColor is the smallest alternative and centralizes protection at the setter layer. Android validation remains non-demonstrative for iOS 26. |
| 3 | Native default capture/restore is conceptually robust for reset scenarios, but adds state and lifecycle complexity that is harder to justify without iOS 26 validation. |
| 4 | Mapper-driven refresh aligns with handler extensibility (UpdateValue) but is more complex than candidate 2 and still needs iOS 26 execution to prove lifecycle behavior. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 1 | Yes | Candidate 1: lifecycle/custom-color gate. |
| claude-opus-4.7 | 1 | Yes | Candidate 2: low-level Automatic/native-default preservation. |
| gpt-5.3-codex | 1 | Yes | Candidate 3: native default capture/restore. |
| gpt-5.5 | 1 | Yes | Candidate 4: handler/mapper refresh orchestration. |
| all | 2 | No passing candidate | All meaningfully different approaches were blocked by the caller-specified Android platform, which cannot validate the iOS 26 UIKit behavior. |
Exhausted: Yes — four distinct approaches were generated and tested as far as the Android platform allows; none can be marked pass because the target bug/fix path is iOS/MacCatalyst-specific.
Selected Fix: None — no candidate passed all tests or was demonstrably better under the required Android validation. Candidate #2 is the most promising alternative on design grounds because it is the smallest and centralizes native-default preservation, but it still requires iOS/MacCatalyst 26+ validation before it can be preferred over the PR.
📋 Report — Final Recommendation
Comparative Report — PR #35385
Candidate ranking
| Rank | Candidate | Result status | Assessment |
|---|---|---|---|
| 1 | pr-plus-reviewer |
Not rerun; Android cannot validate iOS/MacCatalyst 26 behavior | Best candidate. It keeps the PR's lifecycle/style fix and applies the expert reviewer's minimal native-default preservation guard plus MacCatalyst test coverage correction. |
| 2 | try-fix-2 |
Blocked | Strongest independent try-fix. It centralizes preservation in ApplyTrackColor/ApplyThumbColor, which is the smallest robust way to prevent Automatic/Liquid Glass defaults from being overwritten. It does not include the expert reviewer's MacCatalyst test guard correction. |
| 3 | try-fix-1 |
Blocked | Correctly gates lifecycle reapply on custom-color presence, but spreads the guard across more call sites and is more invasive than setter-layer preservation. |
| 4 | try-fix-4 |
Blocked | Mapper-driven refresh respects handler extensibility, but adds orchestration complexity beyond what is needed to fix the reviewer-identified default-preservation bug. |
| 5 | try-fix-3 |
Blocked | Capturing/restoring native defaults is conceptually robust, but adds state and lifecycle complexity that is harder to justify without iOS/MacCatalyst 26 validation. |
| 6 | pr |
Gate failed | The submitted PR fixes custom-color rendering paths but still has the expert-confirmed native-default mutation issue for uncustomized iOS/MacCatalyst 26 switches. Candidates with failed regression/gate behavior rank below candidates that have not failed and address the identified issue. |
Decision
Winner: pr-plus-reviewer.
The raw PR has an actionable correctness/regression issue: it can preserve PreferredStyle.Automatic while still writing fallback/null tint values into UIKit internals during reapply. pr-plus-reviewer keeps the useful parts of the PR and applies the smallest targeted correction at the low-level color application layer, so all current and future reapply callers preserve native defaults when no custom color is set. It also improves test coverage so MacCatalyst 26 does not silently skip the same behavior.
Notes on test status
The gate result supplied to this phase was failed and was not rerun. All STEP 5a try-fix candidates were marked blocked because the requested Android platform cannot validate iOS/MacCatalyst 26 UISwitch behavior; their observed Android failures were not tied to the iOS-only changes. Because no candidate has a clean platform-appropriate pass, the ranking is based on correctness, regression risk, and simplicity, while still ranking the raw PR below candidates that address the expert-confirmed issue.
Description of Change
Fixes iOS 26
Switchcustom color rendering by using UIKit's public classic sliding switch style when MAUI switch colors are customized.On iOS 26, the automatic/liquid
UISwitchstyle can ignore MAUIOffColor/OnColortrack tinting andThumbColor. This change opts switches with a non-null MAUI track or thumb color intoUISwitchStyle.Slidingon iOS 26+, while leaving unstyled switches asUISwitchStyle.Automaticto preserve the native default appearance.This also:
ThumbTintColorwhenThumbColoris reset tonull.Issue35257Appium visual regression coverage from [iOS 26] Fix Switch ThumbColor and OffColor not applied on initial load #35400 and adds focused iOS handler tests for the style, reset, theme-change, and reattachment paths.Conflict resolution:
inflight/current.Issue35257HostApp page, Appium test, andIssue35257_*snapshots as the source of truth for visual coverage.SwitchCustomColorsRenderOnInitialStatesnapshot/test shape during conflict resolution.Validation performed:
dotnet build src/Core/src/Core.csproj -f net10.0-ios26.0 -c Debug --no-restoredotnet build src/Controls/tests/TestCases.iOS.Tests/Controls.TestCases.iOS.Tests.csproj -c Debug --no-restoreIssues Fixed
Fixes #35257