Infinite Measure Looping on iOS (we missed at lest one)
- Dominant language
- C#
- Stars
- 23.3k
- Forks
- 2k
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 290
Description
### Description
Earlier this year i reported #34042 - it was fixed.
I have now found another one of those Infinite looping bugs, one we initial missed.
I have updated the sample repo with a new example and updated the maui nuget version to 10.0.100.
Note that i ran the issue on a iOS simulator (iPhone 16 Pro - iOS 18.6). The issue is not limited to this device. We have seen the bug/issue on devices with iOS version 18 and 26. More on 18 than 26, and what fails on one model does not on another.
Due to the "randomness" of this error we have decided to implement a circuit breaker in the ViewHandler.ViewMapper. We have added a MeasureCounter attached property and subscribe to the SizeChangedEvent and set View.HeightRequest to the View.Height if the MeasureCounter is > 100 iterations. 🤮
For Example 4 i have extracted the failing code and removed parts that did not contribute to the looping. If you want i can provide more examples/samples. Please do not hesitate
### Steps to Reproduce
pull the code from the repo, run the code on iOS simulator (iPhone 16 Pro - iOS 18.6).
press "Example 4" and wait for the quick and dirty loop detector to stop the loop (stopping after 500 size changed)
The app is unresponsive while the loop is running.
### Link to public reproduction project repository
https://github.com/JohnTraDolta/MauiInfiniteMeasureLoopingBug
### Version with bug
10.0.100
### Is this a regression from previous behavior?
Yes, this used to work in .NET MAUI
### Last version that worked well
9.0.120 SR12
### Affected platforms
iOS
### Affected platform versions
iOS 18 and 26.
### Did you find any workaround?
Yes here it is
```
ViewHandler.ViewMapper.AppendToMapping("MeasureCounterFallbackHandler", (handler, iView) =>
{
if (iView is View view)
{
view.SizeChanged += ViewOnSizeChanged;
view.Unloaded += ViewOnUnloaded;
void ViewOnUnloaded(object? sender, EventArgs e)
{
if (sender is not View v)
return;
(sender as View).Unloaded -= ViewOnUnloaded;
v.SizeChanged -= ViewOnSizeChanged;
}
void ViewOnSizeChanged(object? sender, EventArgs e)
{
if (MeasureCounter.IsGreaterThanX(view, 100))
{
view.HeightRequest = view.Height;
return;
}
MeasureCounter.IncrementCount(view);
Debug.WriteLine($"[MeasureCounter:] {MeasureCounter.GetCount(view)}");
}
}
});
```
```
public class MeasureCounter
{
public static readonly BindableProperty CountProperty = BindableProperty.CreateAttached("Count", typeof(int), typeof(MeasureCounter), 0);
public static int GetCount(BindableObject bindable) => (int)bindable.GetValue(CountProperty);
public static void SetCount(BindableObject bindable, int value) => bindable.SetValue(CountProperty, value);
public static void IncrementCount(BindableObject bindable) => SetCount(bindable, GetCount(bindable) + 1);
public static bool IsGreaterThanX(BindableObject bindable, int x) => GetCount(bindable) > x;
}
```
### Relevant log output
```shell
```
Contributor guide
Research direction
Start with the linked MauiInfiniteMeasureLoopingBug repository and run Example 4 on the specified iOS simulator to reproduce the measure loop. Then trace the relevant ViewHandler.ViewMapper and SizeChanged behavior; done means Example 4 no longer makes the app unresponsive or requires the loop detector to stop it.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, ios
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100