$npx -y skills add dpearson2699/swift-ios-skills --skill adattributionkitMeasure ad effectiveness with privacy-preserving attribution using AdAttributionKit. Use when registering ad impressions, handling attribution postbacks, updating conversion values, implementing re-engagement attribution, configuring publisher or advertiser apps, or replacing SKA
| 1 | # AdAttributionKit |
| 2 | |
| 3 | Privacy-preserving ad attribution for iOS 17.4+. AdAttributionKit |
| 4 | lets ad networks measure conversions (installs and re-engagements) without |
| 5 | exposing user-level data. It supports the App Store and alternative |
| 6 | marketplaces, and interoperates with SKAdNetwork. |
| 7 | |
| 8 | Three roles exist in the attribution flow: the **ad network** (signs |
| 9 | impressions, receives postbacks), the **publisher app** (displays ads), and the |
| 10 | **advertised app** (the app being promoted). |
| 11 | |
| 12 | ## Contents |
| 13 | |
| 14 | - [Overview and Privacy Model](#overview-and-privacy-model) |
| 15 | - [Publisher App Setup](#publisher-app-setup) |
| 16 | - [Advertiser App Setup](#advertiser-app-setup) |
| 17 | - [Impressions](#impressions) |
| 18 | - [Postbacks](#postbacks) |
| 19 | - [Conversion Values](#conversion-values) |
| 20 | - [Re-engagement](#re-engagement) |
| 21 | - [Common Mistakes](#common-mistakes) |
| 22 | - [Review Checklist](#review-checklist) |
| 23 | - [References](#references) |
| 24 | |
| 25 | ## Overview and Privacy Model |
| 26 | |
| 27 | AdAttributionKit preserves user privacy through several mechanisms: |
| 28 | |
| 29 | - **Crowd anonymity tiers** -- the device limits postback data granularity based |
| 30 | on the crowd size associated with the ad, ranging from Tier 0 (minimal data) |
| 31 | to Tier 3 (most data including publisher ID and country code). |
| 32 | - **Time-delayed postbacks** -- postbacks are sent 24-48 hours after conversion |
| 33 | window close (first window) or 24-144 hours (second/third windows). |
| 34 | - **No user-level identifiers** -- postbacks contain aggregate source |
| 35 | identifiers and conversion values, not device or user IDs. |
| 36 | - **Hierarchical source identifiers** -- 2, 3, or 4-digit source IDs where the |
| 37 | number of digits returned depends on the crowd anonymity tier. |
| 38 | |
| 39 | In migration and interoperability reviews, explicitly state that the system |
| 40 | evaluates AdAttributionKit and SKAdNetwork impressions together, only one |
| 41 | impression wins per conversion, click-through beats view-through, and recency |
| 42 | breaks ties within click-through impressions before falling back to the most |
| 43 | recent view-through impression. |
| 44 | |
| 45 | ## Publisher App Setup |
| 46 | |
| 47 | A publisher app displays ads from registered ad networks. Add each ad network's |
| 48 | ID to the app's Info.plist so its impressions qualify for install validation. |
| 49 | |
| 50 | ### Add ad network identifiers |
| 51 | |
| 52 | ```xml |
| 53 | <key>AdNetworkIdentifiers</key> |
| 54 | <array> |
| 55 | <string>example123.adattributionkit</string> |
| 56 | <string>another456.adattributionkit</string> |
| 57 | </array> |
| 58 | ``` |
| 59 | |
| 60 | Ad network IDs must be lowercase. SKAdNetwork IDs (ending in `.skadnetwork`) |
| 61 | are also accepted -- the frameworks share IDs. |
| 62 | |
| 63 | ### Display a UIEventAttributionView |
| 64 | |
| 65 | For click-through custom-rendered ads, place one `UIEventAttributionView` over |
| 66 | each tappable ad/control. It must cover the tappable area and stay above views |
| 67 | that would intercept touches before `handleTap()` succeeds. |
| 68 | |
| 69 | ```swift |
| 70 | import UIKit |
| 71 | |
| 72 | let attributionView = UIEventAttributionView() |
| 73 | attributionView.frame = adContentView.bounds |
| 74 | attributionView.isUserInteractionEnabled = true |
| 75 | adContentView.addSubview(attributionView) |
| 76 | ``` |
| 77 | |
| 78 | ## Advertiser App Setup |
| 79 | |
| 80 | The advertised app is the app someone installs or re-engages with after seeing |
| 81 | an ad. It must call a conversion value update at least once to begin the |
| 82 | postback conversion window. |
| 83 | |
| 84 | ### Opt in to receive winning postback copies |
| 85 | |
| 86 | Add `AttributionCopyEndpoint` under the top-level `AdAttributionKit` Info.plist |
| 87 | dictionary so the device sends a copy of the winning postback to your server: |
| 88 | |
| 89 | ```xml |
| 90 | <key>AdAttributionKit</key> |
| 91 | <dict> |
| 92 | <key>AttributionCopyEndpoint</key> |
| 93 | <string>https://example.com</string> |
| 94 | </dict> |
| 95 | ``` |
| 96 | |
| 97 | The system derives the well-known endpoint from the registrable domain in the |
| 98 | URL, ignoring subdomains: |
| 99 | |
| 100 | ``` |
| 101 | https://example.com/.well-known/appattribution/report-attribution/ |
| 102 | ``` |
| 103 | |
| 104 | Configure your server to accept HTTPS POST requests at that path. The domain |
| 105 | must have a valid SSL certificate. |
| 106 | |
| 107 | ### Opt in for re-engagement postback copies |
| 108 | |
| 109 | Add a second key in the same `AdAttributionKit` dictionary to also receive |
| 110 | copies of winning re-engagement postbacks: |
| 111 | |
| 112 | ```xml |
| 113 | <key>AdAttributionKit</key> |
| 114 | <dict> |
| 115 | <key>AttributionCopyEndpoint</key> |
| 116 | <string>https://example.com</string> |
| 117 | <key>OptInForReengagementPostbackCopies</key> |
| 118 | <true/> |
| 119 | </dict> |
| 120 | ``` |
| 121 | |
| 122 | ### Update conversion value on first launch |
| 123 | |
| 124 | Call a conversion value update as early as possible after first launch to begin |
| 125 | the conversion window: |
| 126 | |
| 127 | ```swift |
| 128 | import AdAttributionKit |
| 129 | |
| 130 | func applicationDidFinishLaunching() async { |
| 131 | do { |
| 132 | try await Postback.updateConversionValue(0, lockPostback: false) |
| 133 | } catch { |
| 134 | print("Failed to set initial conversion value: \(error)") |
| 135 | } |
| 136 | } |
| 137 | ``` |
| 138 | |
| 139 | ## Impressions |
| 140 | |
| 141 | Ad networks create signed impressions using JWS (JSON Web Signat |