dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.04k stars 1.73k forks source link

iOS App crashes when populating CollectionView #22754

Closed fryndorfer closed 4 months ago

fryndorfer commented 4 months ago

Description

When deploying my app to an iOS device - regardless if it's a simulator or a real device - the app crashes as soon as I try to populate a CollectionView with some data. Please note that this only happens with an iOS device. Android works just fine. This is the error message:

Failed to install Users/cubefinity/Library/Caches/Xamarin/mtbs/builds/matrix42.mobile.app.ui/54b14571e5b835b31407396bc73af17950c5e91e527b3a121c a1ed5ec5jbn/DebugM42/net8.0-ios/iossimulator-x64/matrix42.mobile.app.ui.app to 17993A9C-CC52-460D-A467-63D9307A0720. UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view size, parent, child, location, transforms, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes. Make a symbolic breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the debugger and see what caused this to occur, so you can avoid this action altogether if possible, or defer it until the table view has been added to a window. Table view: <_UIOutlineTableView: 0x104e65e00; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600000714a80>; backgroundColor = <UIDynamicSystemColor: 0x600007148480; name = tableBackgroundColor>; layer = <CALayer: 0x600005a28ca0>; contentOffset: 0, 0; contentSize: 0, 0; adjustedContentInset: {0, 0, 0, 0}; dataSource: <UIMoreListController: 0x10287da40>>

The data is retrieved via a http request after the NavigatedTo event fired and then used to populate the CollectionView.

This is the xaml:

<CollectionView ItemsSource="{Binding Stocktakings}">
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="model:Stocktaking">
            <Frame Margin="4" Padding="12">
                <Frame.GestureRecognizers>
                    <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type vm:StocktakingViewModel}}, Path=TapCommand}" CommandParameter="{Binding .}" />
                </Frame.GestureRecognizers>
                <Label Grid.Column="0" Text="{Binding Name}" FontAttributes="Bold" />
            </Frame>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

This is the code-behind:

public partial class StocktakingView : ContentPage
{
    StocktakingViewModel ViewModel;

    public StocktakingView(StocktakingViewModel vm)
    {
        InitializeComponent();
        BindingContext = ViewModel = vm;
    }

    protected override async void OnNavigatedTo(NavigatedToEventArgs args)
    {
        base.OnNavigatedTo(args);
        await ViewModel.LoadDataAsync();
    }
}

And this is the view model:

[ObservableProperty]
ObservableCollection<Stocktaking>? stocktakings;

public async Task LoadDataAsync()
{
    Stocktakings = new ObservableCollection<Stocktaking>(await _stocktakingService.GetAll());
}

There are other content pages in my application which also have a CollectionView in them, but those ones get populated with static data in the view models' constructor. Those pages do not throw any exception.

I already updated the workload and my NuGet packages. XCode is still running on version 15.2 and I cannot update it, because my MacBook is so old that it doesn't receive macOS updates anymore. So the MacBook is stuck on macOS 13.6.7 and since XCode 15.3+ requires macOS 14+, I cannot update it.

Steps to Reproduce

Unfortunately, I'm not able to reproduce the error in a new project. You can have a look in the reproduction project repo as I did try to reproduce it. It is exactly the same page, code-behind and view model. The only real difference is that the data in the repo is not coming from an actual http request like it is in my app. However, I did add a Task.Delay to the repo to simulate a loading state. The app from the repo is running perfectly in iOS. No exceptions, no crashes.

Link to public reproduction project repository

https://github.com/fryndorfer/MauiSample

Version with bug

8.0.21 SR4.1

Is this a regression from previous behavior?

Yes, this used to work in .NET MAUI

Last version that worked well

Unknown/Other

Affected platforms

iOS

Affected platform versions

iOS 17.0.1 (Simulator), iOS 17.2 (Simulator), iOS 17.4.1 (real device)

Did you find any workaround?

No response

Relevant log output

No response

github-actions[bot] commented 4 months ago

Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!

Open similar issues:

Closed similar issues:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

ajay-mundi commented 4 months ago

Not sure if this will necessarily help your issue. But I tend to avoid using frames at all especially in collection views. After swapping frames with borders I have found collection views to generally work for me.

PureWeen commented 4 months ago

@fryndorfer if you switch to border does it crash?

fryndorfer commented 4 months ago

@ajay-mundi @PureWeen unfortunately, it's still crashing with borders. I also tried it with a grid, but that did not do the trick either. However, I think I was able to narrow down the problem. It's somehow the fault of the service. If I get rid of await _stocktakingService.GetAll() and use insert some random data like

Stocktakings = new ObservableCollection<Stocktaking>();

for (int i = 0; i < 6; i++)
{
    Stocktakings.Add(new Stocktaking
        {
        Name = $"Stonks {i}"
    });
    await Task.Delay(250);
};

it works just fine.

I still don't know why though. The service only executes a http request and it does nothing with the UI. I tried to wrap the service call into a MainThread.InvokeOnMainThreadAsync to make sure the UI thread is waiting for the service and the data, but that did not help either.

Since it's the service that makes the app crash, I'm not sure anymore that this is Maui's fault and not mine. On the other hand, one could ask why Android is working fine. I will keep searching what causes the issue and keep you up to date. Thank you so far for the input.

fryndorfer commented 4 months ago

For anyone interested how my service is built, I pushed it to the reproduction repo.

fryndorfer commented 4 months ago

I found the issue, it was something with the service. So this issue can be closed.