Quick answer: Add Assets/link.xml with explicit <type fullname="MyType" preserve="all"/> entries for any class accessed only via reflection.

Editor: Type.GetType("MyMod.Plugin") returns the type. iOS IL2CPP build: returns null. Stripper saw no static reference and removed the type.

The Symptom

Reflection-based serialization, plugin systems, or scripting layers (MoonSharp, Roslyn) fail in builds with NullReferenceException or "Type not found". Editor works.

The Fix

<!-- Assets/link.xml -->
<linker>
  <assembly fullname="Assembly-CSharp">
    <type fullname="MyMod.Plugin" preserve="all"/>
    <type fullname="MyMod.Hook" preserve="all"/>
  </assembly>
  <assembly fullname="Newtonsoft.Json" preserve="all"/>
</linker>

Unity merges all link.xml files in the project. Per-package authors typically ship their own; for your code, write one yourself.

Targeted Preserve

Preserve only what reflection touches. preserve="methods" keeps method bodies, preserve="fields" keeps fields, preserve="all" keeps everything. Type-level wildcards work via fullname="MyMod.*".

Per-Type Attribute

For one-off types embedded in mixed assemblies: [Preserve] attribute on the class or method achieves the same as link.xml without the file. Useful when the type is in a generated assembly.

Verifying

Build IL2CPP player. Run reflection lookups. Types resolve. Drop link.xml entry: lookups fail again. Confirms link.xml is doing the work.

“Reflection needs preservation. link.xml whitelist. Builds match editor.”

Related Issues

For shader keyword stripping, see keyword strip. For Addressables content update, see content update.

Whitelist the type. IL2CPP keeps it.