adospace / reactorui-maui

MauiReactor is a MVU UI framework built on top of .NET MAUI
MIT License
568 stars 47 forks source link

ListView with Hot Reload crashes on iOS #112

Closed Code-DJ closed 1 year ago

Code-DJ commented 1 year ago

Not sure if this is an underlying MAUI problem, but ListView crashes on iOS after saving a couple of times with Hot Reload enabled.

Make a ProductItem change, save a couple of times. See it crash.

dotnet build -t:Run -f net7.0-ios /p:_DeviceName=:v2:udid=EECA5B10-A549-4D07-8A60-0CA944B62766 dotnet-maui-reactor -f net7.0-ios

Unhandled Exception:
  System.InvalidCastException: Specified cast is not valid.
     at ObjCRuntime.Runtime.ThrowException(IntPtr gchandle)
     at UIKit.UIApplication.UIApplicationMain(Int32 argc, String[] argv, IntPtr principalClassName, IntPtr delegateClassName)
     at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass)
     at OrderingApp.Program.Main(String[] args)

Made the following changes to the OrderingApp:

:
:
class MainPage : Component<MainPageState>
{
    private MauiControls.ListView? _listView;

    public override VisualNode Render()
    {
        return new ContentPage
        {
            new Grid("260, *", "*")
            {
                new Header()
                    .SelectedType(State.SelectedType)
                    .OnTypeSelected(productType => OnSelectedType(productType, true)),

                new ListView(Microsoft.Maui.Controls.ListViewCachingStrategy.RecycleElementAndDataTemplate, listView => _listView = listView)
                    .HasUnevenRows(true)
                    .ItemsSource(ProductItem.Items, RenderProductItem)
                    // .OnScrolled(OnListViewScrolled)
                    .GridRow(1)
                    .Margin(24,20),

                new Cart()
                    .Item(State.SelectedItemIndex == null ? null : ProductItem.Items[State.SelectedItemIndex.Value])
                    .OnClose(()=>SetState(s => s.SelectedItemIndex = null))
                    .GridRowSpan(2)
            }
        };
    }

    private ViewCell RenderProductItem(ProductItem item)
    {
        return new ViewCell
        {
            new Border
            {
                new Grid("*", "115,*")
                {
                    new Image($"{item.Image}.png")
                        .HeightRequest(140)
                        .WidthRequest(140)
                        .TranslationX(-20)
                        ,

                    new Grid("20,*,24", "*")
                    {
                        new Label(item.Title)
                            .FontFamily("MulishSemiBold")
                            .FontSize(18)
                            .VStart()
                            .TextColor(Colors.Black),

                        new Label(item.Description)
                            .FontFamily("MulishRegular")
                            .FontSize(12)
                            .VCenter()
                            .TextColor(Color.FromArgb("#121212"))
                            .GridRow(1),

                        new Label($"${item.Cost}")
                            .FontFamily("MulishSemiBold")
                            .FontSize(16)
                            .VStart()
                            .TextColor(Colors.Black)
                            .GridRow(2),
                    }
                    .GridColumn(1)
                    .Margin(12,15),

                    new Border
                    { 
                        new Label("+ ADD")
                            .FontFamily("MulishBold")
                            .TextColor(Colors.White)
                            .VCenter()
                            .HCenter()
                    }
                    .OnTapped(()=>SetState(s => s.SelectedItemIndex = ProductItem.Items.IndexOf(item)))
                    .WidthRequest(78)
                    .HeightRequest(34)
                    .BackgroundColor(Theme.PrimaryColor)
                    .StrokeShape(new RoundRectangle().CornerRadius(13,0,0,13))
                    .GridColumn(1)
                    .HEnd()
                    .VEnd()
                }
            }
            .StrokeShape(new RoundRectangle().CornerRadius(13))
            .Background(new MauiControls.LinearGradientBrush(new MauiControls.GradientStopCollection
                {
                    new MauiControls.GradientStop(Theme.PrimaryLightColor, 0.0537f),
                    new MauiControls.GradientStop(Colors.White, 0.9738f)
                },
                new Point(0, 0.5),
                new Point(1, 0.5)))
            .Margin(0,0,0,12)
            .HeightRequest(132)
        };
    }
:
:
}
adospace commented 1 year ago

On Android seems working fine...

adospace commented 1 year ago

seems I'm not able to reproduce this issue, working fine on ios even after hot reloading it several times. Can you check your environment, this is mine: dotnet workload list

Installed Workload Id Manifest Version Installation Source

maui-ios 7.0.86/7.0.100 SDK 7.0.300
maui-android 7.0.86/7.0.100 SDK 7.0.300

Use dotnet workload search to find additional workloads to install.

also can you downgrade mauireactor to 1.0.129 to see if it's still not working

Code-DJ commented 1 year ago

Hi, I have the following workloads:

dotnet workload list

Installed Workload Id Manifest Version Installation Source

wasm-tools 7.0.8/7.0.100 SDK 7.0.300 ios 16.4.7067/7.0.100 SDK 7.0.300 maui 7.0.86/7.0.100 SDK 7.0.300

dotnet --version

7.0.305

downgraded to 1.0.129 still see a crash in OrderingApp. It's like creating new objects for the data on the same page and binding it causes the System.InvalidCastException: Specified cast is not valid. exception on Hot Reload.

The workaround for me has been to load the data on the previous page and send it as props.

Passing data as props doesn't crash the app but it blanks out the screen when you make changes. The props are lost by Hot Reload. If your previous screen is also a ListView, you have to go back further as that screen will also blank out by Hot Reload.

Code-DJ commented 1 year ago

Well - I removed all the workloads and added only maui-ios, maui-android to match yours. Now it doesn't crash. The screen still clears and I have to back out to previous and come back in but at least it's not crashing.

Do you have any ideas on how to keep the state with Hot Reload? else we can close this issue. Thank you!

adospace commented 1 year ago

Hot-reload only keeps the component state in memory between iterations. Generally speaking, if you render props you need to copy the props to state.

Code-DJ commented 1 year ago

Yes, I am copying the props to the state in OnMounted and OnPropsChanged - using SetState.

In testing, OnPropsChanged gets called on Hot Reload but the prop is null so may be that's causing it to clear. Will test.

UPDATE:

adospace commented 1 year ago

I'm going to close this issue because from what I can see now it's something not related to MauiReactor, feel free to continue commenting if you find anything I can help on

Code-DJ commented 1 year ago

@adospace may be the crashes are related to this https://github.com/dotnet/maui/issues/11203 - fixed in .net 8.0-preview1