dotnet/maui

Crash on IOS adding items to an ObservableCollection after Render

owaits opened this issue · 7 comments

Description

I am getting a crash in IOS when adding items to an observable collection after the observable collection has been displayed in a CollectionView. I have an observable collection that has a binding to the CollectionView. When I try to clear the observable collection and then add new items the app crashes in IOS but works fine for Android.

Steps to Reproduce

I am not entirely sure of how you repro this, it does happen consistently in my app but there is a complex series of events first. I will attempt to put together a simple example to repro this. For now here is the code I believe to cause the crash and the stack trace from the output after the crash ocurrs.

public ObservableCollection<TItem> Items { get; set; } = new ObservableCollection<TItem>();

      public void Load()
      {
          Items.Clear();
          if(updateManyHandler != null)
          {
              foreach(var item in updateManyHandler())
              {
                  Items.Add(item);
              }
          }

          if (updateHandler != null)
          {
              Item = updateHandler(Id);
              Items.Add(Item);

              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Item)));
          }

          LoadPartnerAdvert();

          IsLoading = false;
      }

Version with bug

Preview 13 (current)

Last version that worked well

Preview 12

Affected platforms

iOS

Affected platform versions

iPadIOS 15

Did you find any workaround?

Not yet

Relevant log output

2022-02-17 14:29:40.878 Xamarin.PreBuilt.iOS[3837:3357137] *** Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:collectionViewAnimator:], UICollectionView.m:8456

[0:] An error occurred: 'Objective-C exception thrown.  Name: NSInternalInconsistencyException Reason: attempt to insert item 0 into section 0, but there are only 0 items in section 0 after the update
Native stack trace:
	0   CoreFoundation                      0x0000000184a22060 B2D21CFD-378C-36D5-BAF7-3F70599CFEFC + 626784
	1   libobjc.A.dylib                     0x000000019d096f54 objc_exception_throw + 60
	2   Foundation                          0x00000001862db6cc D59C6975-5AF2-37BC-93BE-43B80B4293A5 + 1246924
	3   UIKitCore                           0x000000018721b65c 8388EB03-002B-3B35-A78A-6A022894292E + 4114012
	4   UIKitCore                           0x00000001874c4574 8388EB03-002B-3B35-A78A-6A022894292E + 6903156
	5   UIKitCore                           0x0000000186edb6e4 8388EB03-002B-3B35-A78A-6A022894292E + 706276
	6   Xamarin.PreBuilt.iOS                0x0000000102b8540c Xamarin.PreBuilt.iOS + 87052
	7   Xamarin.PreBuilt.iOS                0x0000000102ea5e4c _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2112228
	8   Xamarin.PreBuilt.iOS                0x0000000102e9b438 _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2068688
	9   Xamarin.PreBuilt.iOS                0x0000000102e98ffc _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2059412
	10  Xamarin.PreBuilt.iOS                0x0000000102b854c8 Xamarin.PreBuilt.iOS + 87240
	11  libdispatch.dylib                   0x0000000184694660 A5CBAAB3-E389-3548-BAAC-FAB18411B94A + 18016
	12  libdispatch.dylib                   0x00000001846a2b60 _dispatch_main_queue_callback_4CF + 944
	13  CoreFoundation                      0x00000001849dacd4 B2D21CFD-378C-36D5-BAF7-3F70599CFEFC + 335060
	14  CoreFoundation                      0x0000000184994eac B2D21CFD-378C-36D5-BAF7-3F70599CFEFC + 48812
	15  CoreFoundation                      0x00000001849a83b8 CFRunLoopRunSpecific + 600
	16  GraphicsServices                    0x00000001a033838c GSEventRunModal + 164
	17  UIKitCore                           0x00000001873486a8 8388EB03-002B-3B35-A78A-6A022894292E + 5346984
	18  UIKitCore                           0x00000001870c77f4 UIApplicationMain + 2092
	19  Xamarin.PreBuilt.iOS                0x0000000102ea692c _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2115012
	20  Xamarin.PreBuilt.iOS                0x0000000102ea5b34 _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2111436
	21  Xamarin.PreBuilt.iOS                0x0000000102e9b33c _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2068436
	22  Xamarin.PreBuilt.iOS                0x0000000102e9983c _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2061524
	23  Xamarin.PreBuilt.iOS                0x0000000102e6bb38 _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 1873872
	24  Xamarin.PreBuilt.iOS                0x0000000102db73d4 _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 1134700
	25  Xamarin.PreBuilt.iOS                0x0000000102dbe2e0 _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 1163128
	26  Xamarin.PreBuilt.iOS                0x0000000102d7e74c _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 902116
	27  Xamarin.PreBuilt.iOS                0x0000000102d89008 _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 945312
	28  Xamarin.PreBuilt.iOS                0x0000000102ea692c _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2115012
	29  Xamarin.PreBuilt.iOS                0x0000000102ea5b34 _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2111436
	30  Xamarin.PreBuilt.iOS                0x0000000102e9b33c _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2068436
	31  Xamarin.PreBuilt.iOS                0x0000000102e9983c _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2061524
	32  Xamarin.PreBuilt.iOS                0x0000000102e6bb38 _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 1873872
	33  Xamarin.PreBuilt.iOS                0x0000000102db73d4 _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 1134700
	34  Xamarin.PreBuilt.iOS                0x0000000102dbcddc _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 1157748
	35  Xamarin.PreBuilt.iOS                0x0000000102e708f4 _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 1893772
	36  Xamarin.PreBuilt.iOS                0x0000000102bc6800 xamarin_get_original_working_directory_path + 3672
	37  Xamarin.PreBuilt.iOS                0x0000000102eda5dc _ZNK3icu6number23NumberFormatterSettingsINS0_24LocalizedNumberFormatterEE10toSkeletonER10UErrorCode + 2327156
	38  dyld                                0x000000010325da24 start + 520
'. Callstack: '   at ObjCRuntime.Runtime.ThrowNSException(IntPtr ns_exception)
   at ObjCRuntime.Runtime.throw_ns_exception(IntPtr exc)
   at UIKit.UICollectionView.InsertItems(NSIndexPath[] indexPaths)
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.<>c__DisplayClass36_0.<Add>b__0()
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.Update(Action update, NotifyCollectionChangedEventArgs args)
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.Add(NotifyCollectionChangedEventArgs args)
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.CollectionChanged(NotifyCollectionChangedEventArgs args)
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.CollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
   at System.Collections.ObjectModel.ObservableCollection`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnCollectionChanged(NotifyCollectionChangedAction action, Object item, Int32 index)
   at System.Collections.ObjectModel.ObservableCollection`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].InsertItem(Int32 index, Speaker item)
   at System.Collections.ObjectModel.Collection`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].Add(Speaker item)
   at WordAlive.ViewModels.EventDataModel`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].Load() in D:\ofilms.visualstudio.com\Repos\WordAliveApp\Trunk\WordAlive\ViewModels\EventDataModel.cs:line 121
   at WordAlive.ViewModels.EventDataModel`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].<UpdateManager_UpdateFinished>b__19_0() in D:\ofilms.visualstudio.com\Repos\WordAliveApp\Trunk\WordAlive\ViewModels\EventDataModel.cs:line 92
   at Microsoft.Maui.Dispatching.Dispatcher.<>c__DisplayClass7_0.<DispatchImplementation>b__0()
   at CoreFoundation.DispatchQueue.static_dispatcher_to_managed(IntPtr context)
   at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass)
   at WordAlive.Program.Main(String[] args) in D:\ofilms.visualstudio.com\Repos\WordAliveApp\Trunk\WordAlive\Platforms\iOS\Program.cs:line 13
--- End of stack trace from previous location ---
   at UIKit.UICollectionView.InsertItems(NSIndexPath[] indexPaths)
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.<>c__DisplayClass36_0.<Add>b__0()
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.Update(Action update, NotifyCollectionChangedEventArgs args)
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.Add(NotifyCollectionChangedEventArgs args)
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.CollectionChanged(NotifyCollectionChangedEventArgs args)
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource.CollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
   at System.Collections.ObjectModel.ObservableCollection`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnCollectionChanged(NotifyCollectionChangedAction action, Object item, Int32 index)
   at System.Collections.ObjectModel.ObservableCollection`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].InsertItem(Int32 index, Speaker item)
   at System.Collections.ObjectModel.Collection`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].Add(Speaker item)
   at WordAlive.ViewModels.EventDataModel`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].Load() in D:\ofilms.visualstudio.com\Repos\WordAliveApp\Trunk\WordAlive\ViewModels\EventDataModel.cs:line 121
   at WordAlive.ViewModels.EventDataModel`1[[MWC.BL.Speaker, MWC.Core.MD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].<UpdateManager_UpdateFinished>b__19_0() in D:\ofilms.visualstudio.com\Repos\WordAliveApp\Trunk\WordAlive\ViewModels\EventDataModel.cs:line 92
   at Microsoft.Maui.Dispatching.Dispatcher.<>c__DisplayClass7_0.<DispatchImplementation>b__0()
   at CoreFoundation.DispatchQueue.static_dispatcher_to_managed(IntPtr context)
   at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass)
   at WordAlive.Program.Main(String[] args) in D:\ofilms.visualstudio.com\Repos\WordAliveApp\Trunk\WordAlive\Platforms\iOS\Program.cs:line 13
--- End of stack trace from previous location ---
   at UIKit.UIApplication.Main(String[] args, Type principalClass, Type delegateClass)
   at WordAlive.Program.Main(String[] args) in D:\ofilms.visualstudio.com\Repos\WordAliveApp\Trunk\WordAlive\Platforms\iOS\Program.cs:line 13
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)'
The app has been terminated.

This bug is connected to VS issue https://developercommunity.visualstudio.com/t/android-and-ios-device-debugging-no-longer-availab/1668207 as after the crash you can no longer debug the app through HotReload in Visual Studio. To get debugging working again you need to delete all the files from %Temp%.

@owaits Could you provide your repro project, thanks?

Hi @owaits. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

FWIW, I think I have find the root cause of this and many other issues on iOS CollectionView. In short - the Data Source is bound twice when creating the view, causing all sorts of mad behavior (because the first registered ObservableItemsSource's callbacks are called first, but it's the second ObservableItemsSource's properties that are consulted by the UICollectionView)

I haven't gone in-depth with the investigation, but the two registrations seem to originate in ElementHandler.SetVirtualView:

Trace 1

...
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource..ctor(IEnumerable itemSource, UICollectionViewController collectionViewController, Int32 group)
   at Microsoft.Maui.Controls.Handlers.Items.ItemsSourceFactory.Create(IEnumerable itemsSource, UICollectionViewController collectionViewController)
   at Microsoft.Maui.Controls.Handlers.Items.ItemsViewController`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].CreateItemsViewSource()
   at Microsoft.Maui.Controls.Handlers.Items.GroupableItemsViewController`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].CreateItemsViewSource()
   at Microsoft.Maui.Controls.Handlers.Items.ItemsViewController`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].ViewDidLoad()
   at ObjCRuntime.Messaging.NativeHandle_objc_msgSendSuper(IntPtr , IntPtr )
   at UIKit.UIViewController.get_View()
   at Microsoft.Maui.Controls.Handlers.Items.ItemsViewHandler`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].CreateNativeView()
   at Microsoft.Maui.Handlers.ViewHandler`2[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[UIKit.UIView, Xamarin.iOS, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065]].OnCreateNativeView()
   at Microsoft.Maui.Controls.Handlers.Items.ItemsViewHandler`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnCreateNativeView()
   at Microsoft.Maui.Handlers.ViewHandler.OnCreateNativeElement()
   at Microsoft.Maui.Handlers.ElementHandler.CreateNativeElement()
   at Microsoft.Maui.Handlers.ElementHandler.SetVirtualView(IElement view)
...

Trace 2


...
   at Microsoft.Maui.Controls.Handlers.Items.ObservableItemsSource..ctor(IEnumerable itemSource, UICollectionViewController collectionViewController, Int32 group)
   at Microsoft.Maui.Controls.Handlers.Items.ItemsSourceFactory.Create(IEnumerable itemsSource, UICollectionViewController collectionViewController)
   at Microsoft.Maui.Controls.Handlers.Items.ItemsViewController`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].CreateItemsViewSource()
   at Microsoft.Maui.Controls.Handlers.Items.GroupableItemsViewController`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].CreateItemsViewSource()
   at Microsoft.Maui.Controls.Handlers.Items.ItemsViewController`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].UpdateItemsSource()
   at Microsoft.Maui.Controls.Handlers.Items.GroupableItemsViewController`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].UpdateItemsSource()
   at Microsoft.Maui.Controls.Handlers.Items.ItemsViewHandler`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MapItemsSource(ItemsViewHandler`1 handler, ItemsView itemsView)
   at Microsoft.Maui.Controls.Handlers.Items.SelectableItemsViewHandler`1[[Microsoft.Maui.Controls.GroupableItemsView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MapItemsSource(SelectableItemsViewHandler`1 handler, SelectableItemsView itemsView)
   at Microsoft.Maui.PropertyMapper`2.<>c__DisplayClass5_0[[Microsoft.Maui.Controls.CollectionView, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Microsoft.Maui.Controls.Handlers.Items.CollectionViewHandler, Microsoft.Maui.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].<Add>b__0(IElementHandler h, IElement v)
   at Microsoft.Maui.PropertyMapper.UpdatePropertyCore(String key, IElementHandler viewHandler, IElement virtualView)
   at Microsoft.Maui.PropertyMapper.UpdateProperties(IElementHandler viewHandler, IElement virtualView)
   at Microsoft.Maui.Handlers.ElementHandler.SetVirtualView(IElement view)
...

Hotfix

Since the second call is very easy to eliminate by simply removing the property mapping, I've just added the following to app startup and all problems appear to be gone:

// fix a bug in MAUI Preview 13, where the ItemsSource is registered twice, causing all sorts of weird issues
Microsoft.Maui.Controls.Handlers.Items.CollectionViewHandler.CollectionViewMapper[ItemsView.ItemsSourceProperty.PropertyName] = (_, _) => {};

Sorry for the delay in replying (I have been moving house). I have managed to confirm that the hotfix suggested by @ssimek does resolve the issue I was facing. Do you think you now have enough info to fix this bug or do you still need me to try and put together a sample app that reproduces the issue?

This is also broken on Android in RC1. Adding items to a an ObservableCollection bound to a ListView crashes on android, but not on windows. Using a CollectionView fixes the issue. Worked on both List/Collectionview on Android/Windows in Preview 14.

If you remove the Clear() does it work?