feat(audience): Unity integration, IL2CPP link.xml, and polish (SDK-221)#695
Merged
Conversation
3da9923 to
a8815e6
Compare
a6aa7f8 to
ad93e87
Compare
a8815e6 to
5aefd17
Compare
ad93e87 to
4d9a79d
Compare
f9b8af1 to
f8279bd
Compare
ac288a1 to
e130acf
Compare
e11b0fe to
9dd780b
Compare
7a7dc9e to
f1c32ea
Compare
9dd780b to
c8a7a52
Compare
d96cda9 to
058e4d1
Compare
b0a2c0f to
57a41d9
Compare
f305fc6 to
fe290fc
Compare
69440fe to
bb3d45f
Compare
fe290fc to
9044b9f
Compare
…n (SDK-147) Splits the package into a core asmdef (pure C#, buildable headless under dotnet) and a Unity sub-asmdef (engine-referencing). The Unity layer has always belonged here; the fence on the core was always correct; landing both together makes the partition enforceable at Unity-compile time. - Runtime/Unity/AudienceUnityHooks.cs: RuntimeInitializeOnLoadMethod wiring. Flushes on pause/quit via Application hooks. Installs Debug.Log as Log.Writer for SDK diagnostics. - Runtime/Unity/com.immutable.audience.unity.asmdef: sibling sub-asmdef claiming Runtime/Unity/. Excludes unsupported platforms (mobile, consoles) per v1 scope (Windows / macOS / Linux). Referenced from the core asmdef's sibling - not the other way around, keeping the dependency direction clean. - Runtime/com.immutable.audience.asmdef: noEngineReferences flipped to true. The core has always been pure C# (proven by the sibling Audience.Runtime.csproj that compiles the same tree headless), but the flag shipped as Unity's Editor-default false. Now that the Unity sub-asmdef exists to hold UnityEngine-using code, flipping the fence makes the guarantee enforceable at Unity-compile time - stray `using UnityEngine` in Core/, Events/, Transport/, or Utility/ now breaks the Unity build, matching what dotnet already rejects. - Audience.Runtime.csproj: comment now cross-references the asmdef flag as the primary portability fence and the Compile Remove as the sibling check for the headless dotnet build. Prevents a future cleanup from removing one of the two halves.
Plan §6.6 requires a link.xml at the package root so IL2CPP's managed-code stripping doesn't remove types the SDK reaches only at runtime. Preserves the full Immutable.Audience.Runtime assembly and names the System.Net.Http pipeline pieces used by HttpTransport / SetConsent / DeleteData. System.IO.Compression entries are harmless when the gzip scripting define is off.
Plan §5.1 and the Event Reference require game_launch to ship with platform, version, buildGuid, unityVersion auto-detected. Until the Day 4 DeviceCollector lands, game_launch was shipping with only the studio-supplied distributionPlatform. - ImmutableAudience gets a new internal seam, LaunchContextProvider, that returns a Dictionary<string, object> merged into game_launch properties. Core stays pure C# - the provider is installed from the Unity layer so no UnityEngine import leaks into Runtime/. - FireGameLaunch wraps the provider call in try/catch with a clear warning. A buggy provider must never prevent the launch event from firing - game_launch is the most load-bearing attribution event. - AudienceUnityHooks installs a default provider that reads Application.platform, Application.version, Application.buildGUID, Application.unityVersion. DeviceCollector (Day 4) can replace or extend this without re-wiring. - config.DistributionPlatform keeps winning over any provider value: studios set it explicitly because Unity cannot auto-detect the distribution store, and that value is the one the attribution pipeline expects. Three new tests: provider fields make it onto the event, config overrides provider for distributionPlatform, and a throwing provider doesn't skip the event. 154 passing. Linear: SDK-147 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-147) ConsentStore.Save can fail from transient I/O (disk full, locked file, permissions). Previously the catch handler at ImmutableAudience.cs:318 logged a warning and returned silently - studios had no programmatic signal that their consent change wouldn't survive a restart. Adds AudienceErrorCode.ConsentPersistFailed, distinct from ConsentSyncFailed (backend PUT) so operators can tell the two failure modes apart: a Save failure means local state will revert on relaunch, a Sync failure means the backend audit trail is out of sync but local state is fine. In-memory behaviour is unchanged - the new level still applies to the current session, the purge/downgrade still runs. The callback just gives studios a hook to warn the player, log to their telemetry, or retry the persist at a better moment. New test pre-creates a directory at the consent file path to force File.Move to fail, then asserts ConsentPersistFailed reaches OnError. 155 passing. Linear: SDK-147 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DeleteData fires a backend DELETE for GDPR erasure but returned void, so callers had no handle to know when the erasure completed or failed. FlushAsync returns Task for the same reason - symmetrising the lifecycle methods on the singleton. - Public signature becomes `public static Task DeleteData(string userId = null)`. - All early-return paths (not initialised, no config, no anonymousId) return Task.CompletedTask. - The hot path's Task.Run is just returned directly instead of discarded. No change to the actual HTTP request, error surfacing, or anonymousId-file-side-effect-avoidance contract. - Callers that want fire-and-forget still get it by ignoring the return value; callers that want to gate on completion can now await. Existing void-discarding call sites continue to compile. Two new tests: awaited task completes after DELETE dispatch, and the pre-Init guard returns Task.CompletedTask. 158 passing. Linear: SDK-147 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Runtime/Unity/AudienceUnityHooks.cs: directive only (all references come from UnityEngine which is typed conservatively). - ImmutableAudience.cs: LaunchContextProvider field (added by the auto-context commit on top of the already-annotated file) → nullable; unityContext local in FireGameLaunch → nullable (receives provider result that may be null on exception). Compile-time annotations only; zero runtime behaviour change.
9044b9f to
7067bba
Compare
nattb8
reviewed
Apr 22, 2026
nattb8
previously approved these changes
Apr 22, 2026
ImmutableJeffrey
added a commit
that referenced
this pull request
Apr 22, 2026
- FlushFailed: local storage read error (batch dropped) or non-2xx/non-4xx server response — typically 5xx (batch retained, retried with backoff). - ValidationRejected: server 4xx rejects batch; dropped, not retried. - ConsentSyncFailed: backend PUT /tracking-consent failed; local state already applied. - NetworkError: HTTP exception, timeout, or non-2xx on data deletion. - ConsentPersistFailed: converted from /// <summary> to // to match the rest of the codebase. Wording derived from raise-sites in HttpTransport.cs and ImmutableAudience.cs. Addresses nattb8's review comment on PR #695. Linear: SDK-147 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- FlushFailed: local storage read error (batch dropped), or non-2xx/non-4xx response — typically 5xx (batch retained, retried with backoff). - ValidationRejected: server 4xx rejects batch; dropped, not retried. - ConsentSyncFailed: backend PUT /tracking-consent failed; local state already applied. - NetworkError: HTTP exception, timeout, or non-2xx on data deletion. - ConsentPersistFailed: converted from /// <summary> to // to match the rest of the codebase. Wording derived from raise-sites in HttpTransport.cs and ImmutableAudience.cs. Addresses nattb8's review comment on PR #695. Linear: SDK-147 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
e4b3a68 to
7858115
Compare
nattb8
approved these changes
Apr 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Runtime/Unity/sub-asmdef (com.immutable.audience.unity).noEngineReferencestotrueso the core compiles with zero UnityEngine references at Unity-compile time.link.xmlat the package root for IL2CPP stripping resistance (test-matrix verification tracked separately by SDK-148).Application.quittingtoImmutableAudience.Shutdownand providesDefaultPersistentDataPathProvider+LaunchContextProviderviaAudienceUnityHooksatSubsystemRegistration.platform,version,buildGuid,unityVersionto the auto-firedgame_launchevent (interim; SDK-146 DeviceCollector will replace with full per-event device context).game_launchwhen consent isNoneat the time Init completes.OnErrorcallback asAudienceErrorCode.ConsentPersistFailed.ImmutableAudience.DeleteDatato returnTask(wasasync void) so callers can await errors.AudienceErrorCodevalue with//comments sourced from the raise-sites; converts the existingConsentPersistFailed/// <summary>to//for file consistency.Linear: SDK-221, SDK-216 (the
//-style convention applied toAudienceErrorCodehere is the chosen resolution for SDK-216's "use/// <summary>" ask on the audience surface).