Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Packages/Audience/Editor/AudienceMobileBuildSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public sealed class AudienceMobileBuildSettings : ScriptableObject

[SerializeField]
[Tooltip("Copy shown in the iOS App Tracking Transparency prompt. " +
"Apple rejects empty or generic strings — describe what is " +
"Apple rejects empty or generic strings. Describe what is " +
"collected and why.")]
private string trackingUsageDescription = DefaultTrackingUsageDescription;

Expand Down Expand Up @@ -64,7 +64,7 @@ private static void CreateAsset()
if (guids.Length > 1)
{
Debug.LogWarning(
$"[ImmutableAudience] Multiple AudienceMobileBuildSettings assets found " +
$"[ImmutableAudience] Multiple AudienceMobileBuildSettings assets found - " +
$"using '{path}'. Remove the duplicates to avoid unexpected build behaviour.");
}
return AssetDatabase.LoadAssetAtPath<AudienceMobileBuildSettings>(path);
Expand Down
8 changes: 4 additions & 4 deletions src/Packages/Audience/Editor/iOSFrameworkPostProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ namespace Immutable.Audience.Editor
/// <c>AdSupport.framework</c> hosts <c>ASIdentifierManager</c> (the IDFA
/// accessor). Without it linked, the runtime <c>NSClassFromString</c>
/// lookup returns nil and the IDFA is silently dropped from
/// <c>game_launch</c> — the Info.plist key alone does not load this
/// <c>game_launch</c>. The Info.plist key alone does not load this
/// framework. Linked as a required dep; the framework has shipped on
/// every iOS release since 6.0.
/// </para>
/// <para>
/// <c>AppTrackingTransparency.framework</c> hosts <c>ATTrackingManager</c>.
/// Unity often auto-links this when <c>NSUserTrackingUsageDescription</c>
/// is present, but the auto-link heuristic is undocumented and
/// version-sensitive — an explicit weak link is immune to that drift and
/// version-sensitive. An explicit weak link is immune to that drift and
/// keeps the binary loadable on iOS &lt; 14 (the runtime code already
/// guards via <c>NSClassFromString</c>).
/// </para>
Expand All @@ -40,7 +40,7 @@ namespace Immutable.Audience.Editor
internal static class iOSFrameworkPostProcessor
{
// Runs just after the Info.plist post-processor (9050). Order is
// independent in practice both edit different files but keeping
// independent in practice (both edit different files), but keeping
// them adjacent makes the build log obvious.
internal const int CallbackOrder = 9051;

Expand Down Expand Up @@ -76,7 +76,7 @@ internal static void OnPostProcessBuild(BuildTarget target, string pathToBuiltPr
#endif
}

// Reads the iOS-target define list specifically — the post-processor
// Reads the iOS-target define list specifically. The post-processor
// mutates iOS build output regardless of which target the editor is
// currently focused on.
private static bool AttributionDefineEnabled()
Expand Down
8 changes: 4 additions & 4 deletions src/Packages/Audience/Editor/iOSInfoPlistPostProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace Immutable.Audience.Editor
/// <remarks>
/// Both keys are gated by the <c>AUDIENCE_MOBILE_ATTRIBUTION</c>
/// scripting define so a studio that hasn't opted into attribution
/// ships a clean <c>Info.plist</c> Apple flags apps that include
/// ships a clean <c>Info.plist</c>. Apple flags apps that include
/// either key without the corresponding code paths.
///
/// Values come from the <see cref="AudienceMobileBuildSettings"/>
Expand Down Expand Up @@ -88,11 +88,11 @@ private static void ValidateBuildSettings()
$" NSUserTrackingUsageDescription: {description}\n" +
$" SKAdNetworkItems: {ids.Length} id(s)\n" +
(ids.Length == 0
? " (no SKAdNetwork ids configured set them on the AudienceMobileBuildSettings asset)\n"
? " (no SKAdNetwork ids configured - set them on the AudienceMobileBuildSettings asset)\n"
: string.Concat(System.Array.ConvertAll(ids, id => $" - {id}\n"))));
}

// Reads the iOS-target define list specifically — the post-processor
// Reads the iOS-target define list specifically. The post-processor
// mutates iOS build output regardless of which target the editor is
// currently focused on.
private static bool AttributionDefineEnabled()
Expand All @@ -114,7 +114,7 @@ internal static void ApplyTrackingUsageDescription(
? settings.TrackingUsageDescription
: AudienceMobileBuildSettings.DefaultTrackingUsageDescription;

// Always overwrite — the settings asset is the source of truth,
// Always overwrite. The settings asset is the source of truth,
// beating any placeholder a lower-order post-processor wrote.
root.SetString("NSUserTrackingUsageDescription", description);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ internal static void OnPostProcessBuild(BuildTarget target, string pathToBuiltPr
/// <summary>
/// Adds the attribution-specific privacy declarations to an existing
/// (already Unity-merged) <c>PrivacyInfo.xcprivacy</c> plist root.
/// Idempotent — safe to call on a manifest that already has these entries.
/// Idempotent. Safe to call on a manifest that already has these entries.
/// </summary>
internal static void ApplyAttributionPrivacyEntries(PlistElementDict root)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Packages/Audience/Runtime/AudienceConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public class AudienceConfig
/// collected at runtime. Without the define, this setter is a
/// no-op.
///
/// Studios who set neither ship a clean binary no AD_ID permission,
/// Studios who set neither ship a clean binary: no AD_ID permission,
/// no native attribution code, <c>NSPrivacyTracking = false</c>.
/// </remarks>
public bool EnableMobileAttribution { get; set; } = false;
Expand Down
12 changes: 6 additions & 6 deletions src/Packages/Audience/Runtime/ImmutableAudience.cs
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ private static void SyncConsentToBackend(AudienceConfig config, ConsentLevel lev
/// </summary>
/// <remarks>
/// <para>
/// Studios decide when to show the prompt Apple's HIG requires it
/// Studios decide when to show the prompt. Apple's HIG requires it
/// to fire at a moment that makes the value to the player obvious,
/// not at SDK Init. The IDFA, when authorized, is collected
/// automatically on the next <see cref="ImmutableAudience.Init"/> via
Expand All @@ -751,7 +751,7 @@ private static void SyncConsentToBackend(AudienceConfig config, ConsentLevel lev
/// </para>
/// <para>
/// Resolves to <see cref="TrackingAuthorizationStatus.NotDetermined"/>
/// in three distinct cases — callers who use this signal to decide
/// in three distinct cases. Callers who use this signal to decide
/// whether to retry should consult the SDK log to disambiguate:
/// </para>
/// <list type="bullet">
Expand Down Expand Up @@ -1148,7 +1148,7 @@ private static void FireGameLaunch(
if (skanRegistered == true)
properties["skanRegistered"] = true;

// iOS ATT/IDFA snapshot merged after Unity context so attribution
// iOS ATT/IDFA snapshot, merged after Unity context so attribution
// keys are authoritative if both sources happen to set the same key.
// idfa and gaid are cross-app device identifiers, same privacy class
// as userId; gate them at Full-only. State-class keys (attStatus,
Expand All @@ -1170,7 +1170,7 @@ private static void FireGameLaunch(
}

// Fires install_referrer_received exactly once per install. Cache
// file presence alone isn't enough — on first launch the bridge may
// file presence alone isn't enough. On first launch the bridge may
// write the cache after Init has already run, so the event must be
// dispatched at the next Init that observes a cache hit. The on-disk
// "sent" marker provides idempotency across that boundary.
Expand All @@ -1193,7 +1193,7 @@ private static void FireInstallReferrerReceivedOnce(AudienceConfig config, strin
}
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
{
// Marker write failed — the event will re-fire on the next
// Marker write failed. The event will re-fire on the next
// launch. Pipeline-side dedup or the cost of one duplicate is
// less bad than never sending the event at all.
Log.Warn(AudienceLogs.InstallReferrerSentMarkerWriteFailed(ex));
Expand All @@ -1220,7 +1220,7 @@ private static string AttStatusToString(int status)
// RequestTrackingAuthorizationAsync resolves).
//
// First observation (no file): persists the baseline and returns without
// firing game_launch already captures the initial state on that Init.
// firing. game_launch already captures the initial state on that Init.
private static void CheckAndFireAttStatusChanged(
AudienceConfig config,
ConsentLevel consent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# Keep Google Play Services AdvertisingIdClient symbols (GAID).
#
# AdvertisingIdClient and its inner Info class are reflected via JNI from
# GAIDBridge they have no managed-side reference for R8 to follow, so
# GAIDBridge - they have no managed-side reference for R8 to follow, so
# fullMode in studio projects can drop them. Defensive rules ensure the
# getAdvertisingIdInfo / getId / isLimitAdTrackingEnabled surface survives.
-keep class com.google.android.gms.ads.identifier.** { *; }
Expand Down
4 changes: 2 additions & 2 deletions src/Packages/Audience/Runtime/Unity/Mobile/ATTBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ internal static class ATTBridge
// Hold a single delegate instance for the lifetime of the process. If
// we passed a fresh delegate to each P/Invoke call, IL2CPP could GC
// the wrapper before Apple's completion handler fires (which can be
// seconds or minutes later — the user may swipe the prompt away or
// seconds or minutes later. The user may swipe the prompt away or
// background the app).
private static readonly NativeStatusCallback _staticCallback = OnATTStatus;

Expand All @@ -60,7 +60,7 @@ internal static class ATTBridge
// - MonoPInvokeCallback so IL2CPP emits the trampoline that lets
// native code call back into managed code.
// - Preserve so managed-code stripping at High doesn't drop this
// method (it has no managed callers only the function pointer).
// method (no managed callers; only the function pointer).
// Without both, the await in RequestAsync never completes on a
// shipping build with stripping enabled.
[MonoPInvokeCallback(typeof(NativeStatusCallback))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Immutable.Audience.Unity.Mobile
// Android: gaid + gaidLimitAdTracking are read from the GAIDBridge disk
// cache populated by the previous launch's background fetch (Google's
// AdvertisingIdClient is sync + must run off main thread, so first launch
// ships nothing gaidLimitAdTracking shows up on launch #2 onwards).
// ships nothing; gaidLimitAdTracking shows up on launch #2 onwards).
internal static class AttributionContext
{
// Maps Apple's ATTrackingManagerAuthorizationStatus to the wire
Expand All @@ -33,7 +33,7 @@ internal static string AttStatusToString(int status)
}

// persistentDataPath is required for Android (GAID disk cache); iOS
// ignores it. Returns a possibly-empty dict never null so callers
// ignores it. Returns a possibly-empty dict (never null) so callers
// can merge unconditionally.
internal static IReadOnlyDictionary<string, object> Capture(string? persistentDataPath = null)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Packages/Audience/Runtime/Unity/Mobile/GAIDBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Immutable.Audience.Unity.Mobile
/// Reads gaid + limitAdTracking via AdvertisingIdClient. Google requires
/// the call run off the main thread, so we dispatch on a dedicated worker
/// and cache the result to disk for the next launch. GAID can change
/// (user reset), so we refresh every launch — first launch ships nothing,
/// (user reset), so we refresh every launch. First launch ships nothing;
/// launch #2+ ships the previously-cached value.
/// </summary>
internal static class GAIDBridge
Expand Down Expand Up @@ -101,7 +101,7 @@ internal static void WriteCacheEntry(string persistentDataPath, string gaidOrEmp
private static void StartFetchNative(string persistentDataPath)
{
// Dedicated Thread (not ThreadPool) so Attach/Detach pair on the
// same one-shot worker ThreadPool reuse strands JVM state.
// same one-shot worker. ThreadPool reuse strands JVM state.
var thread = new Thread(() => FetchOnWorkerThread(persistentDataPath))
{
IsBackground = true,
Expand Down
2 changes: 1 addition & 1 deletion src/Packages/Audience/Runtime/Unity/Mobile/IDFVBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Immutable.Audience.Unity.Mobile
{
internal static class IDFVBridge
{
// Replaceable in tests captures NativeImpl by default.
// Replaceable in tests; captures NativeImpl by default.
internal static Func<string?> Impl = NativeImpl;

internal static string? GetIDFV() => Impl();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private static void StartFetchNative(string persistentDataPath)
//
// Each AndroidJavaClass / AndroidJavaObject holds a JNI global
// reference; leaking them stranded a JNI handle every Init.
// `client` is the exception ownership transfers to the
// `client` is the exception: ownership transfers to the
// listener which disposes it in its endConnection finally.
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void ApplyTrackingUsageDescription_WithCustomCopy_WritesIt()
[Test]
public void ApplyTrackingUsageDescription_WithEmptyCopy_FallsBackToDefault()
{
// Whitespace falls through to the default Apple rejects empty.
// Whitespace falls through to the default. Apple rejects empty.
var settings = ScriptableObject.CreateInstance<AudienceMobileBuildSettings>();
SetPrivate(settings, "trackingUsageDescription", " ");

Expand All @@ -61,7 +61,7 @@ public void ApplyTrackingUsageDescription_WithEmptyCopy_FallsBackToDefault()
[Test]
public void ApplyTrackingUsageDescription_OverwritesExistingValue()
{
// Settings asset is the source of truth — beat any placeholder
// Settings asset is the source of truth. Beat any placeholder
// a lower-order post-processor wrote.
var root = new PlistDocument().root;
root.SetString("NSUserTrackingUsageDescription", "stale placeholder");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1524,7 +1524,7 @@
//
// Dedicated event (not a game_launch property): install attribution
// is install-level state, decoupled from launch / session lifecycles.
// Fires once per install — cache landing late on first launch is
// Fires once per install. Cache landing late on first launch is
// recovered by the next Init via the on-disk "sent" marker.
// -----------------------------------------------------------------

Expand Down Expand Up @@ -1561,7 +1561,7 @@
.Select(File.ReadAllText)
.First(c => c.Contains("\"game_launch\""));
Assert.IsFalse(launchFile.Contains("installReferrer"),
"game_launch must never carry installReferrer it ships on its own event");
"game_launch must never carry installReferrer; it ships on its own event");
}

[Test]
Expand All @@ -1585,7 +1585,7 @@
[Test]
public void Init_DoesNotFireInstallReferrerReceived_WhenProviderReturnsEmpty()
{
// Empty cache file marks "fetched, no referrer" — bridge maps
// Empty cache file marks "fetched, no referrer". Bridge maps
// that to null on read, but defend the contract here too.
ImmutableAudience.MobileInstallReferrerProvider = () => string.Empty;
var config = MakeConfig();
Expand Down Expand Up @@ -1652,8 +1652,8 @@
// First launch: bridge fetch in flight, provider returns null.
// Second launch: cache populated, provider returns the referrer.
// Event must fire on the second Init even though it missed the first.
string? firstCallReturn = null;

Check warning on line 1655 in src/Packages/Audience/Tests/Runtime/ImmutableAudienceTests.cs

View workflow job for this annotation

GitHub Actions / Unit Tests (.NET)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 1655 in src/Packages/Audience/Tests/Runtime/ImmutableAudienceTests.cs

View workflow job for this annotation

GitHub Actions / Unit Tests (.NET)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
string? secondCallReturn = "utm_source=second_launch";

Check warning on line 1656 in src/Packages/Audience/Tests/Runtime/ImmutableAudienceTests.cs

View workflow job for this annotation

GitHub Actions / Unit Tests (.NET)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 1656 in src/Packages/Audience/Tests/Runtime/ImmutableAudienceTests.cs

View workflow job for this annotation

GitHub Actions / Unit Tests (.NET)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
var callCount = 0;
ImmutableAudience.MobileInstallReferrerProvider = () =>
++callCount == 1 ? firstCallReturn : secondCallReturn;
Expand Down Expand Up @@ -1766,7 +1766,7 @@
[Test]
public async Task RequestTrackingAuthorization_NoProvider_ReturnsNotDetermined()
{
// No provider set non-iOS environment.
// No provider set (non-iOS environment).
var status = await ImmutableAudience.RequestTrackingAuthorizationAsync();
Assert.AreEqual(TrackingAuthorizationStatus.NotDetermined, status);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public void EnsureFetchStarted_CalledTwiceInSameProcess_FetchesOnce()
public void EnsureFetchStarted_TerminalCacheExists_StillFetches()
{
// Unlike the install referrer (terminal once written), GAID can
// change (user reset) we always refresh the cache for the next
// change (user reset), so we always refresh the cache for the next
// launch even when a value already exists.
GAIDBridge.WriteCacheEntry(_testDir, "stale-gaid", limitAdTracking: false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void GetCachedInstallReferrer_NonEmptyFile_ReturnsContent()
[Test]
public void GetCachedInstallReferrer_EmptyFile_ReturnsNull()
{
// Empty file marks "fetched, no referrer" — caller treats it as
// Empty file marks "fetched, no referrer". Caller treats it as
// "nothing to emit" but EnsureFetchStarted treats it as terminal.
InstallReferrerBridge.WriteCacheEntry(_testDir, string.Empty);

Expand Down Expand Up @@ -104,7 +104,7 @@ public void EnsureFetchStarted_CalledTwiceInSameProcess_FetchesOnce()
public void EnsureFetchStarted_TerminalCacheExists_SkipsFetch()
{
// Simulate a previous launch that wrote a cache entry. The fetch
// must NOT run again Google's referrer is stable per install.
// must NOT run again. Google's referrer is stable per install.
InstallReferrerBridge.WriteCacheEntry(_testDir, "utm_source=test");

var fetchCalls = 0;
Expand All @@ -118,7 +118,7 @@ public void EnsureFetchStarted_TerminalCacheExists_SkipsFetch()
[Test]
public void EnsureFetchStarted_EmptyCacheExists_SkipsFetch()
{
// Empty cache = "fetched, no referrer" terminal state, no retry.
// Empty cache = "fetched, no referrer": terminal state, no retry.
InstallReferrerBridge.WriteCacheEntry(_testDir, string.Empty);

var fetchCalls = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void DisposeAndWait_LongCallback_ReturnsFalseAndLeaksHandle()
// Mono player builds (Unity Standalone Mono targets) signal
// Timer.Dispose's wait handle ahead of in-flight callbacks
// completing, which breaks this unit test's premise. Production
// code is unaffected DrainHeartbeatTimer is exercised
// code is unaffected. DrainHeartbeatTimer is exercised
// end-to-end by the SampleApp PlayMode tests on the same Mono
// builds and works correctly. This unit test asserts a
// lower-level WaitHandle invariant that doesn't hold under Mono.
Expand Down
Loading