Top

Linker experiences with Xamarin.iOS

Linker experiences with Xamarin.iOS

We experienced some problems in one of our latest projects with Xamarin.iOS, the kind of problems that are normally accompanied by a big headache. While everything worked like a charm over iOS simulator, something started to crash when running the app over a physical device. It was a simple app consuming data from a web service and being displayed via Master Detail pages with standard views so, what could be wrong?

Thanks to output log while debugging over device, we found that there were some errors – with their stack trace information – from System.Data.Services.Client assembly. That component calls some reflection methods and this is why it was failing, just take a look to the next image. If you have a scenario like described above, and get some reflection exceptions, maybe the origin is mtouch linker.

[code language=”text”]
Sample[3786:96666] System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Services.Client.TypeSystem —> System.ArgumentNullException: Argument cannot be null.
Parameter name: key
at System.ThrowHelper.ThrowArgumentNullException (ExceptionArgument argument) [0x00000] in /Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/throwhelper.cs:82
at System.Collections.Generic.Dictionary`2[System.Reflection.MethodInfo,System.String].Insert (System.Reflection.MethodInfo key, System.String value, Boolean add) [0x0000b] in /Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/collections/generic/dictionary.cs:315
at System.Collections.Generic.Dictionary`2[System.Reflection.MethodInfo,System.String].Add (System.Reflection.MethodInfo key, System.String value) [0x00000] in /Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/syst
em/collections/generic/dictionary.cs:185
at System.Data.Services.Client.TypeSystem..cctor () [0x0061b] in :0
— End of inner exception stack trace —
at System.Data.Services.Client.Metadata.ClientTypeUtil.TypeOrElementTypeIsEntity (System.Type type) [0x00000] in :0
at System.Data.Services.Client.DataServiceContext.ValidateExecuteParameters[Events] (System.Uri& requestUri, System.String httpMethod, System.Nullable`1& singleResult, System.Collections.Generic.List`1& bodyOperationParameters, System.Collections.Generic.List`1& uriOperationParameters, System.Data.Services.Client.OperationParameter[] operationParameters) [0x0002a] in :0
at System.Data.Services.Client.DataServiceContext.InnerBeginExecute[Events] (System.Uri requestUri, System.AsyncCallback callback, System.Object state, System.String httpMethod, System.String method, Nullable`1 singleResult, System.Data.Services.Client.OperationParameter[] operationParameters) [0x00004] in :0
at System.Data.Services.Client.DataServiceContext.BeginExecute[Events] (System.Uri requestUri, System.AsyncCallback callback, System.Object state, System.String httpMethod, Boolean singleResult, System.Data.Services.Client.OperationParameter[] operationParameters) [0x00012] in :0
at Sample.Services.WebService+d__bd`1[Sample.WcfSampleEntities.Events].MoveNext () [0x00092] in e:DevelSampleSampleSampleSampleServicesWebService.cs:390
[0:] System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Services.Client.TypeSystem —> System.ArgumentNullException: Argument cannot be null.
Parameter name: key
at System.ThrowHelper.ThrowArgumentNullException (ExceptionArgument argument) [0x00000] in /Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/throwhelper.cs:82
at System.Collections.Generic.Dictionary`2[System.Reflection.MethodInfo,System.String].Insert (System.Reflection.MethodInfo key, System.String value, Boolean add) [0x0000b] in /Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/collections/generic/dictionary.cs:315
at System.Collections.Generic.Dictionary`2[System.Reflection.MethodInfo,System.String].Add (System.Reflection.MethodInfo key, System.String value) [0x00000] in /Users/builder/data/lanes/1503/e6ebd18b/source/mono/external/referencesource/mscorlib/system/collections/generic/dictionary.cs:185
at System.Data.Services.Client.TypeSystem..cctor () [0x0061b] in :0
— End of inner exception stack trace —
at System.Data.Services.Client.Metadata.ClientTypeUtil.TypeOrElementTypeIsEntity (System.Type type) [0x00000] in :0
at System.Data.Services.Client.DataServiceContext.ValidateExecuteParameters[Events] (System.Uri& requestUri, System.String httpMethod, System.Nullable`1& singleResult, System.Collections.Generic.List`1& bodyOperationParameters, System.Collections.Generic.List`1& uriOperationParameters, System.Data.Services.Client.OperationParameter[] operationParameters) [0x0002a] in :0
at System.Data.Services.Client.DataServiceContext.InnerBeginExecute[Events] (System.Uri requestUri, System.AsyncCallback callback, System.Object state, System.String httpMethod, System.String method, Nullable`1 singleResult, System.Data.Services.Client.OperationParameter[] operationParameters) [0x00004] in :0
at System.Data.Services.Client.DataServiceContext.BeginExecute[Events] (System.Uri requestUri, System.AsyncCallback callback, System.Object state, System.String httpMethod, Boolean singleResult, System.Data.Services.Client.OperationParameter[] operationParameters) [0x00012] in :0
at Sample.Services.WebService+d__bd`1[Sample.WcfSampleEntities.Events].MoveNext () [0x00092] in e:DevelSampleSampleSampleSampleServicesWebService.cs:390

[/code]

Linker acts like a “remove all unreferenced code” tool. It means that any unreferenced assembly, class or even method or property is likely to be removed before application is packaged. Why do this? To reduce and optimize the size of packaged application. These are current available options for linker behavior:

  • Don’t link: where no assembly will be processed.
  • Link SDK assemblies only: where only platform SDK related assemblies will be optimized.
  • Link all assemblies: where any assembly could be reduced. This is the option that will leave your app as small as possible.

linker options

But, why was our application presenting different behaviors depending on whether we are dealing with simulator or device? Well, far beyond the obvious fact, simulator is just a simulator, there was a further reason. By default, for performance, when you create a new Xamarin.iOS project, it has different linker configurations for simulator and device platforms (don’t link vs SDK linking, respectively). At this point, you have two options: disable linking for devices (not recommended) or establishing the same linking level for simulator (or just work with the device) and tame the beast 🙂 using one of the techniques described in Xamarin online documentation.

In Plain Concepts we often use Preserve attribute technique thanks to MvvmCross, that adds a LinkerPleaseInclude.cs file in platform projects (like the one created from this file). If you are lucky, maybe a stack trace gives you the hint to know what portion of code is missing, and create new lines of code to add explicit references.

If the stack trace is not giving you a clear candidate after all (it was our case), you can always use –linkskip option to exclude certain assemblies from optimization process, or give it a try with XML linker configuration option, that lets you create more accurate exclusions. Only depends on the time that you have to play with it.

Sergio Escalada

Post a Comment