I'm working on a project with Xamarin.iOS and MvvmCross (v5.6.3) and I have a very typical UISlider on one view controller with its value binded to a float
property on its view-model.
set.Bind(MySlider).For(x => x.Value).To(vm => vm.FloatProperty).TwoWay();
When the view unloads and try to dispose the existing bindings (like while navigating to another view-model) I'm getting the following unhandled exception with a SIGABRT
:
Unhandled Exception:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> UIKit.UIKitThreadAccessException: UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.
at UIKit.UIApplication.EnsureUIThread () [0x00023] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/UIKit/UIApplication.cs:88
at UIKit.UIControl.RemoveTarget (Foundation.NSObject target, System.IntPtr sel, UIKit.UIControlEvent events) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/UIKit/UIControl.g.cs:235
at UIKit.UIControl.RemoveTarget (System.EventHandler notification, UIKit.UIControlEvent events) [0x00057] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/UIKit/UIControl.cs:116
at UIKit.UIControl.remove_ValueChanged (System.EventHandler value) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/UIKit/UIControl.cs:209
at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/corlib/System.Reflection/MonoMethod.cs:305
--- End of inner exception stack trace ---
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00046] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/corlib/System.Reflection/MonoMethod.cs:313
at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/reflection/methodbase.cs:229
at MvvmCross.Platform.WeakSubscription.MvxWeakEventSubscription`1[TSource].RemoveEventHandler () [0x00024] in <6adc0d5857264558a9d45778a78ae02a>:0
at MvvmCross.Platform.WeakSubscription.MvxWeakEventSubscription`1[TSource].Dispose (System.Boolean disposing) [0x00003] in <6adc0d5857264558a9d45778a78ae02a>:0
at MvvmCross.Platform.WeakSubscription.MvxWeakEventSubscription`1[TSource].Dispose () [0x00000] in <6adc0d5857264558a9d45778a78ae02a>:0
at MvvmCross.Binding.iOS.Target.MvxUISliderValueTargetBinding.Dispose (System.Boolean isDisposing) [0x0001b] in <614c9ef828c14ba687a40ec2656f480f>:0
at MvvmCross.Binding.Bindings.MvxBinding.Finalize () [0x00000] in <866b1e46764b48aab0d408952a6f006f>:0
2018-04-09 23:15:20.210 MyProject.iOS[73770:3302608] Unhandled managed exception:
Exception has been thrown by the target of an invocation. (System.Reflection.TargetInvocationException)
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00046] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/corlib/System.Reflection/MonoMethod.cs:313
at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/reflection/methodbase.cs:229
at MvvmCross.Platform.WeakSubscription.MvxWeakEventSubscription`1[TSource].RemoveEventHandler () [0x00024] in <6adc0d5857264558a9d45778a78ae02a>:0
at MvvmCross.Platform.WeakSubscription.MvxWeakEventSubscription`1[TSource].Dispose (System.Boolean disposing) [0x00003] in <6adc0d5857264558a9d45778a78ae02a>:0
at MvvmCross.Platform.WeakSubscription.MvxWeakEventSubscription`1[TSource].Dispose () [0x00000] in <6adc0d5857264558a9d45778a78ae02a>:0
at MvvmCross.Binding.iOS.Target.MvxUISliderValueTargetBinding.Dispose (System.Boolean isDisposing) [0x0001b] in <614c9ef828c14ba687a40ec2656f480f>:0
at MvvmCross.Binding.Bindings.MvxBinding.Finalize () [0x00000] in <866b1e46764b48aab0d408952a6f006f>:0
--- inner exception ---
UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread. (UIKit.UIKitThreadAccessException)
at UIKit.UIApplication.EnsureUIThread () [0x00023] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/UIKit/UIApplication.cs:88
at UIKit.UIControl.RemoveTarget (Foundation.NSObject target, System.IntPtr sel, UIKit.UIControlEvent events) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/UIKit/UIControl.g.cs:235
at UIKit.UIControl.RemoveTarget (System.EventHandler notification, UIKit.UIControlEvent events) [0x00057] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/UIKit/UIControl.cs:116
at UIKit.UIControl.remove_ValueChanged (System.EventHandler value) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/UIKit/UIControl.cs:209
at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke(System.Reflection.MonoMethod,object,object[],System.Exception&)
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.9.1.24/src/Xamarin.iOS/mcs/class/corlib/System.Reflection/MonoMethod.cs:305
2018-04-09 23:15:20.210 MyProject.iOS[73770:3302608] critical: Stacktrace:
2018-04-09 23:15:20.211 MyProject.iOS[73770:3302608] critical:
Native stacktrace:
2018-04-09 23:15:20.212 MyProject.iOS[73770:3302608] critical: 0 MyProject.iOS 0x00000001038b4574 mono_handle_native_crash + 244
2018-04-09 23:15:20.212 MyProject.iOS[73770:3302608] critical: 1 libsystem_platform.dylib 0x000000010bbecf5a _sigtramp + 26
2018-04-09 23:15:20.213 MyProject.iOS[73770:3302608] critical: 2 ??? 0x0000000103dd308a 0x0 + 4359794826
2018-04-09 23:15:20.213 MyProject.iOS[73770:3302608] critical: 3 libsystem_c.dylib 0x000000010b8820eb abort + 127
2018-04-09 23:15:20.213 MyProject.iOS[73770:3302608] critical: 4 MyProject.iOS 0x0000000103a5b2df xamarin_unhandled_exception_handler + 47
2018-04-09 23:15:20.214 MyProject.iOS[73770:3302608] critical: 5 MyProject.iOS 0x000000010391b854 mono_invoke_unhandled_exception_hook + 148
2018-04-09 23:15:20.214 MyProject.iOS[73770:3302608] critical: 6 MyProject.iOS 0x00000001039c2a3e mono_thread_internal_unhandled_exception + 110
2018-04-09 23:15:20.215 MyProject.iOS[73770:3302608] critical: 7 MyProject.iOS 0x000000010391d15a mono_gc_run_finalize + 842
2018-04-09 23:15:20.215 MyProject.iOS[73770:3302608] critical: 8 MyProject.iOS 0x00000001039fc9ba sgen_gc_invoke_finalizers + 234
2018-04-09 23:15:20.215 MyProject.iOS[73770:3302608] critical: 9 MyProject.iOS 0x000000010391eae4 finalizer_thread + 756
2018-04-09 23:15:20.215 MyProject.iOS[73770:3302608] critical: 10 MyProject.iOS 0x00000001039c3600 start_wrapper + 704
2018-04-09 23:15:20.215 MyProject.iOS[73770:3302608] critical: 11 libsystem_pthread.dylib 0x000000010bbfe6c1 _pthread_body + 340
2018-04-09 23:15:20.216 MyProject.iOS[73770:3302608] critical: 12 libsystem_pthread.dylib 0x000000010bbfe56d _pthread_body + 0
2018-04-09 23:15:20.216 MyProject.iOS[73770:3302608] critical: 13 libsystem_pthread.dylib 0x000000010bbfdc5d thread_start + 13
2018-04-09 23:15:20.216 MyProject.iOS[73770:3302608] critical:
=================================================================
Got a SIGABRT while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
The binding works fine, this happens only when I try to navigate to another view-model the view is being unloaded.
Am I missing something? Or is this maybe a bug on MvvmCross?
Edit: Finally checked that this issue occurs when the view is being unloaded, regardless I'm navigating to another view-model or not. I've removed the references where I'm pointing to a navigation issue.
There is currently a bug in MvvmCross 5.x.x with respect to the UISlider
. The issue has been fixed with PR 2750 which will ship in version 6.0.0
In the meantime, you should be able to override the binding to fix it for your project by creating a custom binding with the fix. The issue is in the Dispose
.
public class FixedMvxUISliderValueTargetBinding : MvxPropertyInfoTargetBinding<UISlider>
{
private IDisposable _subscription;
public FixedMvxUISliderValueTargetBinding(object target, PropertyInfo targetPropertyInfo)
: base(target, targetPropertyInfo) { }
protected override void SetValueImpl(object target, object value)
{
var view = target as UISlider;
if (view == null) return;
view.Value = (float)value;
}
private void HandleSliderValueChanged(object sender, EventArgs e)
{
var view = View;
if (view == null) return;
FireValueChanged(view.Value);
}
public override MvxBindingMode DefaultMode => MvxBindingMode.TwoWay;
public override void SubscribeToEvents()
{
var slider = View;
if (slider == null)
{
MvxBindingTrace.Trace(
MvxTraceLevel.Error,
"Error - UISlider is null in MvxUISliderValueTargetBinding");
return;
}
_subscription = slider.WeakSubscribe(
nameof(slider.ValueChanged),
HandleSliderValueChanged);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (isDisposing)
{
_subscription?.Dispose();
_subscription = null;
}
}
}
Then in your Setup.cs
register the binding. Note, make sure that you call base.FillTargetFactories
first.
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterPropertyInfoBindingFactory(
typeof(FixedMvxUISliderValueTargetBinding),
typeof(UISlider),
nameof(UISlider.Value));
}