Quick answer: Mark callbacks with [Preserve] or list them in link.xml. Check the IL2CPP StrippingReport.xml for the actual list of removed methods.
A Unity game ships an iOS build. Editor works fine, IL2CPP build crashes when a third-party plugin invokes a managed callback. The callback method was stripped because static analysis couldn’t see who called it.
Preserve Attribute
using UnityEngine.Scripting;
[Preserve]
public class AnalyticsCallback
{
[Preserve]
public static void OnEvent(string name) { ... }
}
Compact for code you control. Apply to both class and method — class-only doesn’t preserve methods inside.
link.xml for Third-Party Assemblies
Add Assets/link.xml:
<linker>
<assembly fullname="MyAnalyticsSDK">
<type fullname="MyAnalyticsSDK.Callback" preserve="all" />
</assembly>
<assembly fullname="Assembly-CSharp">
<type fullname="Game.SaveData" preserve="all" />
</assembly>
</linker>
preserve=“all” keeps fields, methods, and the type itself. preserve=“fields” / “methods” for finer control.
JSON Deserialization Targets
Common offender: Newtonsoft.Json deserialization to a type whose only references are via reflection. Strip removes it. Mark every DTO/model class:
[Preserve]
[JsonObject(MemberSerialization.OptIn)]
public class ApiResponse
{
[JsonProperty][Preserve] public string Id;
}
Diagnose Which Got Stripped
After a build, look in:
Library/il2cpp_cache/<platform>/Bee/StrippingReport.xml
Lists what was removed. Search for your missing type. Confirm absence in the build, then add to link.xml.
Lower Stripping Level
Player Settings → Managed Stripping Level. Options: Disabled, Minimal, Low, High. Default is High; lower for less aggressive stripping at cost of binary size. Use temporarily to confirm strip is the culprit.
Verifying
Build for target platform. Run; callback fires correctly. No InvalidOperationException / MethodNotFound at runtime. Binary size only slightly larger than before fix.
“IL2CPP strips what it can’t see. Anything called via reflection or native code needs explicit Preserve or link.xml.”
Maintain a per-asset link.xml in every plugin folder. Easier to keep in sync with code changes than one global file.