Bug 28763 - [preserve] is ignored
Summary: [preserve] is ignored
Alias: None
Product: Forms
Classification: Xamarin
Component: Forms ()
Version: 1.4.1
Hardware: PC Mac OS
: --- normal
Target Milestone: ---
Assignee: Bugzilla
Depends on:
Reported: 2015-04-04 01:08 UTC by George Cook
Modified: 2015-04-30 03:09 UTC (History)
7 users (show)

Is this bug a regression?: ---
Last known good build:

the exception from building ios project (39.16 KB, image/png)
2015-04-04 01:08 UTC, George Cook
Test project (4.12 MB, application/zip)
2015-04-04 23:29 UTC, Adam Kemp
project with tiered sub-projects. (7.31 MB, application/zip)
2015-04-06 20:19 UTC, George Cook

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description George Cook 2015-04-04 01:08:12 UTC
Created attachment 10633 [details]
the exception from building ios project

I need to link assemblies to keep my code size down. 

in build options for my iOS project I select Link SDK assemblies only,

however, when I do that I get excpetions when I start the app saying that various classes could not be loaded. 

these are all classes that are referenced by [attributes] (like json converters).

I tried doing this in my main iOS project :

		//types we need to force to compile
		FixBadEmptyJsonArrayConverter<object> _fixBadEmptyJsonArrayConverter = new FixBadEmptyJsonArrayConverter<object> ();
		SpaceDefinitionConverter _SpaceDefinitionConverter;
		MongoDateTimeConverter _MongoDateTimeConverter;
		MongoIDConverter _MongoIDConverter;
		VideoConverter _videoconverter;
		PanelConverter _panelConverter;

but it still crashes saying the class FixBadEmptyJsonArrayConverter was not loaded.

I created a [Preserve] attribute, which the docs say would mark the class as being included; but it does nothing.

	public class FixBadEmptyJsonArrayConverter<T> : JsonCreationConverter<T>

my preserve code

using System;

namespace Giganet.App

	[System.AttributeUsage (System.AttributeTargets.Class | System.AttributeTargets.Struct)]
	public class Preserve: System.Attribute
		public Preserve ()

Is this a bug? am I doing it wrong? 

it needs to be fixed, or the docs need to be updated.
Comment 1 George Cook 2015-04-04 01:30:10 UTC
ok. I realzie dit should be PreserveAttribute, and did that - but it idn't work either.

so the problem is that this code will not execute if I set stripping to strip SDKs

    //TODO - server sends {} when there's no items
    [JsonProperty ("favouriteMedia")]
    [JsonConverter (typeof(FixBadEmptyJsonArrayConverter<string[]>))]
    public string[] FavouriteIds;
the FixBadEmptyJsonArrayConverter class is in my binary and can be instantiated in my iOS project's app delegate.

this has to be a bug, right?

what else can I do to reduce filesize while I Wait for xamarin to fix this?

This code here is the one that causes the issue; but only when stripping is not set to off.
    [JsonConverter (typeof(FixBadEmptyJsonArrayConverter<string[]>))]

This is such a huge issue. My app is >120 mb in size without stripping (it's a clone of an iOS app which is only 20mb in size)...
Comment 2 George Cook 2015-04-04 01:31:21 UTC
here's the actual exception

Could not load type Giganet.Core.Model.Converters.FixBadEmptyJsonArrayConverter`1[[System.String[], System.Runtime, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]], GiganetMobile.SharedModels, Version=1.0.5572.786, Culture=neutral, PublicKeyToken=null while decoding custom attribute
Comment 3 Adam Kemp 2015-04-04 23:29:11 UTC
Created attachment 10637 [details]
Test project

I've attached another test project that demonstrates that custom Preserve attributes are ignored, despite the documentation found here: http://developer.xamarin.com/guides/ios/advanced_topics/linker/

If you run the project on iOS with the linker turned on for all assemblies then the button does nothing. When you link SDK only or none then the button increments the number. What's happening is that the ButtonPageViewModel.IncrementCommand property's getter is being stripped out. This is despite the fact that I have a Preserve(AllMembers = true) attribute on the class.

The documentation explicitly says that you can use any namespace you want, but in fact it only works in an iOS app if you use the namespace "Foundation".

On Android I figured only MonoDroid.Foundation would be recognized, but I couldn't get any custom Preserve attribute to be recognized on Android regardless of namespace.

So the story seems to be that you can make it work on iOS (not Android), but you have to use the Foundation namespace.

This really needs to be fixed. Xamarin.Forms relies heavily on reflection, and it doesn't make sense to have an iOS-specific namespace in your PCL. If anything Xamarin.Forms should provide its own PCL Preserve attribute, but ideally the linker would allow any Preserve attribute like the documentation says.

Also, the documentation doesn't even mention the AllMembers property of the preserve attribute, but that's important. Without it you would have to decorate every memory of a class that might get stripped. The attached example doesn't work unless you set AllMembers to true.
Comment 4 George Cook 2015-04-06 13:53:17 UTC
further add to this: I can't  get this to work on my project as it stands.

Can we get some feedback from someboady at Xamarin please? this is urgent - the native app is 18mb. I can't possibly justify shipping a 120mb app to my client. Even when stripped, the 40-50mb is big; but without stripping it just takes the biscuit.
Comment 5 George Cook 2015-04-06 15:58:52 UTC
even with the change that AdamKemp recommends, I get the exact same error as before.

Also, I use multiple iOS projects (we have 3 apps based on the similar code base, so we have a core iOS project for shared code), and I'm unable to use the PreserveAttribute in the core IOS project.

I don't know how to escalate this on Xamarin's side; but it's a HUGE problem.

Does Xamarin have paid for support? I think we will likely need to pay for a support ticket, as we can't release an app that's 6 times bigger in size than the native version, which is currently the case.

We can possibly arrange for you to have access to the entire project, to see what's going on. Can someone from Xamarin sign an NDA, if that's the case?
Comment 6 Kent Green [MSFT] 2015-04-06 18:21:47 UTC
Taking a look at the sample in comment 3, I observed the following:

I was able to reproduce the issue when building the app with the setting "Link All Assemblies." Specifically the increment button fails to work. 

If I skipped linking the PCL assembly with the following mtouch argument, the sample again works as expected, with the increment working:

> --linkskip=FormsTestApp1

===Size considerations with different linker settings===
I also compared the sizes of the app packaged as shown in the bin -> iPhone -> release folder; when using the Release | iPhone deployment and the ARMv7 architecture. (Multiple architectures will increase total package size as noted in the arch. tooltip.):

> Link All ----------------------------- 14.5 MB .app package (30.2 MB entire release folder)
> Link All Skipping PCL assembly --- 14.5 MB .app package (30.3 MB entire release folder)
> Link SDK --------------------------- 14.5 MB .app package (30.3 MB entire release folder)
> Don’t Link -------------------------- 56.8 MB .app package (113 MB entire release folder)

These numbers may also vary a bit depending on other specific settings; but seem to have similar variances looking at *just* the linker settings. (i.e. the primary size difference is between "Don't Link" vs. any of the other options.)
Comment 7 George Cook 2015-04-06 20:19:31 UTC
Created attachment 10648 [details]
project with tiered sub-projects.

Can you guys please take a look at this example. the work-around that Adam provided does not work for this one, and it's more presentative of my real project, and I beleive that the tiered PCL's (for abstracting shared models across clients and servers) is more indicative of the kinds of architectures that are going to be face more scrutiny in larger organizations.
Comment 8 Kent Green [MSFT] 2015-04-07 21:32:03 UTC
Using the sample provided in comment 7, I was able to reproduce the exception described in comment 2. However, I also noted that this exception occurs even if the linker is set to Don't Link and there are no other mtouch arguments. Based on this, I suspect the issue isn't being caused by the linker at all. I also noticed that the exception only occurs at runtime, the build completes successfully.

> System.TypeLoadException: Could not load type SharedClasses.FixBadEmptyJsonArrayConverter`1[[System.String[], System.Runtime, Version=, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]], SharedClasses, Version=1.0.5575.32824, Culture=neutral, PublicKeyToken=null while decoding custom attribute

Full stacktrace of that issue: 
(it's gigantic, not sure which bits are significant. I also noticed a lot of references to Newtonsoft.Json.)
Comment 9 Adam Kemp 2015-04-21 15:10:02 UTC
Since the issue described in Comment 3 was confirmed can we update the status of this bug to CONFIRMED? I'm afraid of it getting lost.
Comment 10 Brendan Zagaeski (Xamarin Team, assistant) 2015-04-30 03:09:00 UTC
It turned out the original issue from comment 0 on this bug report was unrelated to the problems with the `[Preserve]` attribute as described in comment 3. In particular, in the scenario described in the original comment, the linker was only linking the SDK assemblies, so the `[Preserve]` attributes were technically all redundant.

The underlying problem from comment 0 was eventually resolved on bug 28823.

## For comment 3

- On iOS the problem is now solved as of Xamarin.iOS 8.10 (the latest stable version), thanks to Bug 28389.

- On Android the problem is _not_ yet solved, so I filed a new bug for that specific issue: Bug 29571.

- I also submitted documentation bugs for both iOS and Android to make sure we update the sample `PreserveAttribute` code to exactly match the original `Foundation.PreserveAttribute` type.

Since all the issues discussed on this bug are now being tracked elsewhere, I will mark this bug as RESOLVED.