MSTouch linker issues with Xamarin and ReactiveUI

While releasing a new build of our Xamarin iOS application I ran into a problem where our whole app was failing. This application was build on top of ReactiveUI. In debug mode nothing strange happend. But in release mode a lot of System.ArgumentExceptions: Set Method not found for 'PropertyName' exceptions where being thrown, causing our whole app to fail.

Problem

When I started to investigate this problem I found that the most important difference between the debug build and the release build was the ‘linker’ option in de build configurtion. While deploying a debug build this option was set to Don't link. But in release mode this option was set to Link SDK assemblies only. According to the Xamarin documentation the linker strips away code from you application that has no reference to it. This could declare why the ‘getters’ and ‘setters’ of a lot of properties couldn’t be found at runtime. But we referenced all those failing properties in our code (at least I thought so), so why where they being linked away?

I created a playground application to replicate the problem. The following code was throwing an exception on runtime while being deployed in release mode with the linker set to Link SDK assemblies only.

public partial class ViewController : ReactiveViewController, IViewFor<ViewModel>
{
    ...

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        // Throws an exception when linker is enabled
        this.OneWayBind(ViewModel, vm => vm.HideButton, v => v.MyButton.Hidden); 

        //this.MyButton.Hidden = false;
        this.ViewModel.HideButton = false;
    }
}

When I uncommented this.MyButton.Hidden = false everything worked fine again while building on release mode. At this point I remembered that ReactiveUI bindings work with reflection, something the linker doesn’t account for. Code that is only called by refelction isn’t seen by the linker, and will be stripped away. So we need a solution for the problem that properties only referenced from within a binding, and thus only being called with reflection, are being stripped away.

Solution

The Xamarin documentation states that you have to add an [Preserve()] attribute on top of the class to stop to linker from stripping away code. But since we don’t own the UIButton, it isn’t possible to add this attribute.

An other trick/solution is to reference to code that is being spripped away. This is actually being done in the ReactiveUI project for some bindings supported out of the box. This way the linker will see that this code has a reference to it, and won’t sprip it away anymore. In order to add those references I added a LinkerPreserve class to to project.

public class LinkerPreserve
{
    static LinkerPreserve()
    {
        try
        {
            default(UIButton).Hidden = default(bool);
        }
        catch(Exception)
        {
            throw new Exception("Don't call this class");
        }
    }    
}

Adding this class fixed the problem.

Conclusion

Always add a reference somewhere in your code to properties that are only being referenced from a binding expression! Otherwise the mstouch linker whil strip away your getters and setters, and things will start to fail.

Comments