diff --git a/src/Packages/Audience/Editor/AudienceMobileBuildSettings.cs b/src/Packages/Audience/Editor/AudienceMobileBuildSettings.cs
index 7049b346..13e42267 100644
--- a/src/Packages/Audience/Editor/AudienceMobileBuildSettings.cs
+++ b/src/Packages/Audience/Editor/AudienceMobileBuildSettings.cs
@@ -18,17 +18,23 @@ namespace Immutable.Audience.Editor
///
public sealed class AudienceMobileBuildSettings : ScriptableObject
{
- // Fallback so a build never ships with the key missing (which would
- // block App Store submission). Studios should override on the asset
- // with copy describing what is collected and why.
+ // Fallback used when no asset exists or the field is left blank.
+ // Apple reviewers reject generic strings — studios MUST replace this
+ // with copy that names their app and explains the specific use case
+ // (e.g. "Game Name uses your advertising identifier to attribute app
+ // installs and measure ad campaign performance."). Submitting this
+ // default will likely result in an App Store rejection.
internal const string DefaultTrackingUsageDescription =
- "Your data may be used to deliver personalised ads to you. " +
- "You can change this preference at any time in Settings.";
+ "This app uses your device's advertising identifier to attribute " +
+ "app installs and measure ad campaign performance. You can change " +
+ "this preference at any time in Settings > Privacy & Security > Tracking.";
[SerializeField]
- [Tooltip("Copy shown in the iOS App Tracking Transparency prompt. " +
- "Apple rejects empty or generic strings. Describe what is " +
- "collected and why.")]
+ [Tooltip("REQUIRED: Customise this before submitting to the App Store. " +
+ "Apple rejects generic strings — describe what YOUR app collects " +
+ "and why (e.g. 'Game Name uses your advertising identifier to " +
+ "attribute installs and measure ad performance.'). " +
+ "Left blank, the SDK default is used but will likely be rejected.")]
private string trackingUsageDescription = DefaultTrackingUsageDescription;
[SerializeField]
diff --git a/src/Packages/Audience/Editor/iOSPrivacyManifestPostProcessor.cs b/src/Packages/Audience/Editor/iOSPrivacyManifestPostProcessor.cs
index e8a12821..16542cf8 100644
--- a/src/Packages/Audience/Editor/iOSPrivacyManifestPostProcessor.cs
+++ b/src/Packages/Audience/Editor/iOSPrivacyManifestPostProcessor.cs
@@ -69,6 +69,26 @@ internal static void ApplyAttributionPrivacyEntries(PlistElementDict root)
// IDFA collection constitutes tracking under Apple's definition.
root.SetBoolean("NSPrivacyTracking", true);
+ // Apple requires every domain contacted for tracking to be listed.
+ const string trackingDomain = "api.immutable.com";
+ PlistElementArray trackingDomains;
+ if (root.values.TryGetValue("NSPrivacyTrackingDomains", out var existingDomains) &&
+ existingDomains is PlistElementArray existingDomainsArray)
+ {
+ trackingDomains = existingDomainsArray;
+ }
+ else
+ {
+ trackingDomains = root.CreateArray("NSPrivacyTrackingDomains");
+ }
+
+ var hasDomain = false;
+ foreach (var item in trackingDomains.values)
+ {
+ if (item is PlistElementString ds && ds.value == trackingDomain) { hasDomain = true; break; }
+ }
+ if (!hasDomain) trackingDomains.AddString(trackingDomain);
+
PlistElementArray dataTypes;
if (root.values.TryGetValue("NSPrivacyCollectedDataTypes", out var existing) &&
existing is PlistElementArray existingArray)
diff --git a/src/Packages/Audience/Runtime/Plugins/iOS/PrivacyInfo.attribution.xcprivacy b/src/Packages/Audience/Runtime/Plugins/iOS/PrivacyInfo.attribution.xcprivacy
index 72d54c0c..2e5cb56c 100644
--- a/src/Packages/Audience/Runtime/Plugins/iOS/PrivacyInfo.attribution.xcprivacy
+++ b/src/Packages/Audience/Runtime/Plugins/iOS/PrivacyInfo.attribution.xcprivacy
@@ -5,7 +5,9 @@
NSPrivacyTracking
NSPrivacyTrackingDomains
-
+
+ api.immutable.com
+
NSPrivacyCollectedDataTypes