Description
Describe the bug 🐞
Executing a command inside an iOS app throws an UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.'
exception.
Step to reproduce
- Create a new MAUI project and install the
ReactiveUI.Maui
nuget - Create a viewmodel with a reactive command
- Create a page and bind the viewmodel
- Create a toolbar item on that page and bind the command (via code behind)
- Start the app and touch the button
- Get an
UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.'
exception.
Stack trace: at UIKit.UIApplication.EnsureUIThread() in /Users/builder/azdo/_work/1/s/xamarin-macios/src/UIKit/UIApplication.cs:line 111 at UIKit.UIBarButtonItem.set_Enabled(Boolean value) in /Users/builder/azdo/_work/1/s/xamarin-macios/src/build/dotnet/ios/generated-sources/UIKit/UIBarButtonItem.g.cs:line 1036 at Microsoft.Maui.Controls.Compatibility.Platform.iOS.ToolbarItemExtensions.PrimaryToolbarItem.UpdateIsEnabled() at Microsoft.Maui.Controls.Compatibility.Platform.iOS.ToolbarItemExtensions.PrimaryToolbarItem.OnPropertyChanged(Object sender, PropertyChangedEventArgs e) at Microsoft.Maui.Controls.BindableObject.OnPropertyChanged(String propertyName) at Microsoft.Maui.Controls.Element.OnPropertyChanged(String propertyName) at Microsoft.Maui.Controls.BindableObject.SetValueActual(BindableProperty property, BindablePropertyContext context, Object value, Boolean currentlyApplying, SetValueFlags attributes, SetterSpecificity specificity, Boolean silent) at Microsoft.Maui.Controls.BindableObject.SetValueCore(BindableProperty property, Object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes, SetterSpecificity specificity) at Microsoft.Maui.Controls.BindableObject.SetValue(BindableProperty property, Object value, SetterSpecificity specificity) at Microsoft.Maui.Controls.BindableObjectExtensions.RefreshPropertyValue(BindableObject self, BindableProperty property, Object value) at Microsoft.Maui.Controls.MenuItem.Microsoft.Maui.Controls.Internals.ICommandElement.CanExecuteChanged(Object sender, EventArgs e) at ReactiveUI.ReactiveCommandBase`2[[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263],[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263]].OnCanExecuteChanged(Boolean newValue) in /_/src/ReactiveUI/ReactiveCommand/ReactiveCommandBase.cs:line 143 at System.Reactive.AnonymousSafeObserver`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Sink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ForwardOnNext(Boolean value) at System.Reactive.IdentitySink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Subjects.FastImmediateObserver`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].EnsureActive(Int32 count) at System.Reactive.Subjects.FastImmediateObserver`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].EnsureActive() at System.Reactive.Subjects.ReplaySubject`1.ReplayBase[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Subjects.ReplaySubject`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Sink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ForwardOnNext(Boolean value) at System.Reactive.IdentitySink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Sink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ForwardOnNext(Boolean value) at System.Reactive.Linq.ObservableImpl.DistinctUntilChanged`2._[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Sink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ForwardOnNext(Boolean value) at System.Reactive.Linq.ObservableImpl.CombineLatest`3._.SecondObserver[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Sink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ForwardOnNext(Boolean value) at System.Reactive.IdentitySink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Subjects.FastImmediateObserver`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].EnsureActive(Int32 count) at System.Reactive.Subjects.FastImmediateObserver`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].EnsureActive() at System.Reactive.Subjects.ReplaySubject`1.ReplayBase[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Subjects.ReplaySubject`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Sink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ForwardOnNext(Boolean value) at System.Reactive.IdentitySink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Sink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ForwardOnNext(Boolean value) at System.Reactive.Linq.ObservableImpl.DistinctUntilChanged`2._[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Sink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ForwardOnNext(Boolean value) at System.Reactive.IdentitySink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Boolean value) at System.Reactive.Sink`1[[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ForwardOnNext(Boolean value) at System.Reactive.Linq.ObservableImpl.Select`2.Selector._[[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(Int32 value) at System.Reactive.Sink`1[[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ForwardOnNext(Int32 value) at System.Reactive.Linq.ObservableImpl.Scan`2._[[ReactiveUI.ReactiveCommand`2.ExecutionInfo[[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263],[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263]], ReactiveUI, Version=19.5.0.0, Culture=neutral, PublicKeyToken=null],[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnNext(ExecutionInfo value) at System.Reactive.SafeObserver`1.WrappingSafeObserver[[ReactiveUI.ReactiveCommand`2.ExecutionInfo[[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263],[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263]], ReactiveUI, Version=19.5.0.0, Culture=neutral, PublicKeyToken=null]].OnNext(ExecutionInfo value) at System.Reactive.Sink`1[[ReactiveUI.ReactiveCommand`2.ExecutionInfo[[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263],[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263]], ReactiveUI, Version=19.5.0.0, Culture=neutral, PublicKeyToken=null]].ForwardOnNext(ExecutionInfo value) at System.Reactive.ObserveOnObserverLongRunning`1[[ReactiveUI.ReactiveCommand`2.ExecutionInfo[[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263],[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263]], ReactiveUI, Version=19.5.0.0, Culture=neutral, PublicKeyToken=null]].Drain() at System.Reactive.ObserveOnObserverLongRunning`1.<>c[[ReactiveUI.ReactiveCommand`2.ExecutionInfo[[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263],[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263]], ReactiveUI, Version=19.5.0.0, Culture=neutral, PublicKeyToken=null]].<.cctor>b__17_0(ObserveOnObserverLongRunning`1 self, ICancelable cancelable) at System.Reactive.Concurrency.DefaultScheduler.LongRunning.LongScheduledWorkItem`1.<>c[[System.Reactive.ObserveOnObserverLongRunning`1[[ReactiveUI.ReactiveCommand`2.ExecutionInfo[[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263],[System.Reactive.Unit, System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263]], ReactiveUI, Version=19.5.0.0, Culture=neutral, PublicKeyToken=null]], System.Reactive, Version=6.0.0.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263]].<.ctor>b__3_0(Object thisObject) at System.Reactive.Concurrency.ConcurrencyAbstractionLayerImpl.<>c.<StartThread>b__8_0(Object itemObject) at System.Threading.Thread.StartCallback()
Reproduction repository
Can be created if desired
Expected behavior
To not thow an exception on executing a ReactiveCommand.
Screenshots 🖼️
No response
IDE
Visual Studio 2022
Operating system
iOS
Version
.net8.0
Device
iphone 12 mini
ReactiveUI Version
ReactiveUI.Maui 19.5.41
Additional information ℹ️
No response