Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin.iOS Linker removes members in class needed for reflection

In a Xamarin project, I set the linker behaviour to "Link all references". When doing that, assemblies get optimized by removing unused members. One problem, it's not actually aware that we need them for reflection. I know there is the Preserve attribute that we can put to tell the linker to keep all symbols in the class, but what if the assembly is a DLL or just not an iOS project (doesn't reference the monotouch assemblies, and can't). Is there a way I can set all classes from a library to "preserve", ignore it, or only set certain classes to be optimized (reverse PreserveAttribute) ?

like image 670
Philippe Paré Avatar asked Jun 12 '15 18:06

Philippe Paré


People also ask

What is linker in Xamarin iOS?

Linking Xamarin.iOS Apps. When building your application, Visual Studio for Mac or Visual Studio calls a tool called mtouch that includes a linker for managed code. It is used to remove from the class libraries the features that the application is not using.

What is-nolink in Xamarin IDE?

This corresponds to the -nolink option when using the command-line tool mtouch. In this mode, the linker will leave your assemblies untouched, and will reduce the size of the SDK assemblies (i.e. what's shipped with Xamarin.iOS) by removing everything that your application doesn't use. This is the default setting when your IDE targets iOS devices.

Why can't I link to third party libraries in Xamarin?

This also means that any third party libraries that are not part of Xamarin's core SDK will not be linked. This happens typically, because they do not want to manually add [Preserve] attributes to their code.

Where can I find the Xamarin documentation on linking?

You can find the Xamarin documentation on linking at Linking on iOS and Linking on Android.


3 Answers

@SharpMobileCode gave a very good explanation but there are also two other ways to solve this.

  1. Use an XML file and use --xml=your.file in the project's options, under Additional mtouch arguments. That might be useful when you can't change the source code of the assemblies -or- when you want/need to use a tool to generate the list of members to be preserved;

  2. Use the new Preserve(Type) constructor. That allows you to add preserve instructions in a different assembly (e.g. your main .exe) which already has a reference to Xamarin.iOS.dll (so no need to define your own type). E.g.

[assembly: Preserve (typeof (MyType), AllMembers = true)]

like image 195
poupou Avatar answered Nov 06 '22 20:11

poupou


What you can do is create a file called LinkerPleaseInclude.cs. You can actually name it what you want, but this name makes sense. In there you can create dummy objects for the types you need, especially for reflection. That way the link will see that you "need" it and won't strip it out. Here's an example of a LinkerPleaseInclude.cs file. It's useful if the classes you need are defined in a 3rd party DLL.

Update:

Ah, I misread your original post thinking you were talking about 3rd party DLLs you have no control over. So if you have a non-xamarin.iOS library, you can still use the Preserve attribute. However, you will have to define it like so in your library project.:

public sealed class PreserveAttribute : System.Attribute 
{
    public bool AllMembers;
    public bool Conditional;
}

It doesn't which namespace you define the Preserve class in, as the linker only searches for the Preserve name. Then you can just use the Preserve attribute as you normally would. So if you want to Preserve a class and all 50 properties, then you do something like:

[Preserve(AllMembers=true)]
public MyClass
{
   //code here
}
like image 34
SharpMobileCode Avatar answered Nov 06 '22 22:11

SharpMobileCode


For anyone that uses Reflection to set Properties of UIKit Views here is my "Bread and Butter" Preserve Configuration.

Just create a new class in your Xamarin iOS App project and call it something like "LinkerGuard.cs" or "PreserveConfiguration.cs"

Put this in it:

using CoreAnimation;
using CoreGraphics;
using Foundation;
using UIKit;


[assembly: Preserve (typeof (UIResponder), AllMembers = true)]
[assembly: Preserve (typeof (UIControl), AllMembers = true)]
[assembly: Preserve (typeof (UIView), AllMembers = true)]
[assembly: Preserve (typeof (UIButton), AllMembers = true)]
[assembly: Preserve (typeof (UILabel), AllMembers = true)]
[assembly: Preserve (typeof (UIImageView), AllMembers = true)]
[assembly: Preserve (typeof (UITextField), AllMembers = true)]
[assembly: Preserve (typeof (UISwitch), AllMembers = true)]
[assembly: Preserve (typeof (UIActivityIndicatorView), AllMembers = true)]
[assembly: Preserve (typeof (UIDatePicker), AllMembers = true)]
[assembly: Preserve (typeof (UIScrollView), AllMembers = true)]
[assembly: Preserve (typeof (UIWebView), AllMembers = true)]
[assembly: Preserve (typeof (UIPageControl), AllMembers = true)]
[assembly: Preserve (typeof (UIToolbar), AllMembers = true)]
[assembly: Preserve (typeof (UIStepper), AllMembers = true)]
[assembly: Preserve (typeof (UISegmentedControl), AllMembers = true)]
[assembly: Preserve (typeof (UISearchBar), AllMembers = true)]

[assembly: Preserve (typeof (UIScreen), AllMembers = true)]
[assembly: Preserve (typeof (UIWindow), AllMembers = true)]

[assembly: Preserve (typeof (UIBarItem), AllMembers = true)]

[assembly: Preserve (typeof (UINavigationBar), AllMembers = true)]


[assembly: Preserve (typeof (UITabBar), AllMembers = true)]
[assembly: Preserve (typeof (UITabBarItem), AllMembers = true)]
[assembly: Preserve (typeof (UIBarButtonItem), AllMembers = true)]

[assembly: Preserve (typeof (UITextFieldCondition), AllMembers = true)]
[assembly: Preserve (typeof (UIViewContentMode), AllMembers = true)]
[assembly: Preserve (typeof (UITextAlignment), AllMembers = true)]
[assembly: Preserve (typeof (UIControlContentHorizontalAlignment), AllMembers = true)]

[assembly: Preserve (typeof (UIReturnKeyType), AllMembers = true)]
[assembly: Preserve (typeof (UIDataDetectorType), AllMembers = true)]
[assembly: Preserve (typeof (UIKeyboardType), AllMembers = true)]

[assembly: Preserve (typeof (UITextFieldChange), AllMembers = true)]

[assembly: Preserve (typeof (UITableView), AllMembers = true)]
[assembly: Preserve (typeof (UICollectionView), AllMembers = true)]
[assembly: Preserve (typeof (UITableViewCell), AllMembers = true)]
[assembly: Preserve (typeof (UICollectionViewCell), AllMembers = true)]

[assembly: Preserve (typeof (UITableViewDataSource), AllMembers = true)]
[assembly: Preserve (typeof (UICollectionViewDataSource), AllMembers = true)]

[assembly: Preserve (typeof (UIViewController), AllMembers = true)]
[assembly: Preserve (typeof (UITabBarController), AllMembers = true)]
[assembly: Preserve (typeof (UINavigationController), AllMembers = true)]

[assembly: Preserve (typeof (UIDocument), AllMembers = true)]


[assembly: Preserve (typeof (UITapGestureRecognizer), AllMembers = true)]
[assembly: Preserve (typeof (UIGestureRecognizer), AllMembers = true)]

[assembly: Preserve (typeof (CGColor), AllMembers = true)]
[assembly: Preserve (typeof (UIColor), AllMembers = true)]
[assembly: Preserve (typeof (UIFont), AllMembers = true)]
[assembly: Preserve (typeof (UIImage), AllMembers = true)]

[assembly: Preserve (typeof (NSObject), AllMembers = true)]
[assembly: Preserve (typeof (NSAttributedString), AllMembers = true)]
[assembly: Preserve (typeof (NSLayoutConstraint), AllMembers = true)]
[assembly: Preserve (typeof (CAKeyFrameAnimation), AllMembers = true)]
[assembly: Preserve (typeof (NSIndexPath), AllMembers = true)]
like image 20
spaceMonster Avatar answered Nov 06 '22 20:11

spaceMonster