Flutter, React Native & Unity

Unity Managed Stripping: Keep Reflective Entry Points Without Disabling Everything

Unity managed stripping removes code the linker considers unreachable. Reflection, serialization and runtime-selected types can require explicit preservation because their use may not be visible through ordinary static call analysis.

By , FounderUpdated 3 min read

Find the dynamic contract

Start from the missing type or method and identify how it is reached. The managed-stripping guide describes supported preservation through attributes and link.xml.

Do not preserve every assembly before checking whether the library already provides the required annotations.

Preserve only the necessary surface

Runtime behavior Preservation question
Type loaded by name Must the type and its name remain?
Reflective constructor Which constructor is required?
Serialized member Which fields/properties are accessed?
Generic AOT path Is the needed instantiation available?

The IL2CPP documentation provides context for AOT constraints. A rule fixing one reflective path does not establish compatibility for every generic or runtime-generated path.

Test the stripped release

Development or editor execution may not exercise the same stripping pipeline. Build the intended release backend and test the exact feature that failed.

Include less frequent content paths, downloaded content where supported and error recovery. A type used only in a later level can remain invisible to a short smoke test.

Keep preservation maintainable

Document why each attribute or XML entry exists and which test protects it. Review old broad rules during dependency upgrades rather than accumulating permanent keep-all exceptions.

A smaller output is not a success if the game fails to deserialize a saved session.

Inspect with appropriate limits

APKLint's Unity Analyzer can help identify packaged assemblies/native structures, but it cannot simulate the linker's complete reachability model or run reflective code. Retain the build's stripping settings, preservation file and release test evidence. The desired result is a narrow, explainable rule that preserves functionality while allowing unrelated unused code to be removed.

Investigate a type reached only from saved data

Suppose a release loads ordinary inventory items but fails when an older save contains a rare item class. In the editor, the same save works. Identify how the save library resolves that class and constructs it. If the type name comes from serialized data, a static reference search may not represent the runtime contract that the linker must preserve.

The managed stripping documentation describes supported preservation mechanisms. Consult the library's supplied annotations before adding your own rule. Record whether the missing surface is a type, a constructor, a field or a method; these require different preservation decisions and should not be replaced automatically with an assembly-wide exception.

Validate the rule against both missing and unused code

Build the original release profile with the smallest rule that covers the identified contract. Load the failing synthetic save, create another instance through the same dynamic path and save it again. Then load a save without that item to check the ordinary path. Test any supported migration from an older save format, since migration code can refer to types absent from newly created data.

If preservation does not solve the failure, inspect the actual exception and backend limitation. A required generic instantiation or unsupported runtime code generation can be a different problem from a stripped member. Repeatedly broadening a keep rule without confirming the failure mechanism can conceal the real compatibility issue and retain unnecessary code.

Keep each rule close to an explanation naming the feature and its regression fixture. When a dependency changes, repeat that fixture and review whether the library now supplies its own preservation metadata. A previously necessary rule can become redundant, but remove it only after testing the release pipeline that performs stripping.

Finally, compare output size and inspect build diagnostics where available. Treat size reduction as a secondary result after behavior is preserved. A short startup test cannot cover dynamically loaded content or every saved-data subtype, so document the fixture population rather than claiming exhaustive reachability proof. The strongest evidence is a previously failing release case that now works through a narrowly explained preservation change, alongside ordinary cases that remain functional under the intended backend and stripping level.

Sources and further reading

  1. Unity: Managed code stripping
  2. Unity: IL2CPP

Reference review: 29 September 2026. Examples illustrate the workflow; check your installed versions, release artifact and account-specific Console requirements before applying them. This guide is not a claim that APKLint executed your project or verified your private account.

APKLint

Android inspection tools and practical release guides. About APKLint · Report a correction