MCPInstall

adattributionkit

Measure 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 SKAdNetwork with AdAttributionKit for ad measurement.

Installs
3.1K
Last 24h
+15
Trending rank
#7076
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/dpearson2699/swift-ios-skills --skill adattributionkit
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads adattributionkit automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

AdAttributionKit

Privacy-preserving ad attribution for iOS 17.4+. AdAttributionKit lets ad networks measure conversions (installs and re-engagements) without exposing user-level data. It supports the App Store and alternative marketplaces, and interoperates with SKAdNetwork.

Three roles exist in the attribution flow: the ad network (signs impressions, receives postbacks), the publisher app (displays ads), and the advertised app (the app being promoted).

Contents

Overview and Privacy Model

AdAttributionKit preserves user privacy through several mechanisms:

  • Crowd anonymity tiers -- the device limits postback data granularity based on the crowd size associated with the ad, ranging from Tier 0 (minimal data) to Tier 3 (most data including publisher ID and country code).
  • Time-delayed postbacks -- postbacks are sent 24-48 hours after conversion window close (first window) or 24-144 hours (second/third windows).
  • No user-level identifiers -- postbacks contain aggregate source identifiers and conversion values, not device or user IDs.
  • Hierarchical source identifiers -- 2, 3, or 4-digit source IDs where the number of digits returned depends on the crowd anonymity tier.

In migration and interoperability reviews, explicitly state that the system evaluates AdAttributionKit and SKAdNetwork impressions together, only one impression wins per conversion, click-through beats view-through, and recency breaks ties within click-through impressions before falling back to the most recent view-through impression.

Publisher App Setup

A publisher app displays ads from registered ad networks. Add each ad network's ID to the app's Info.plist so its impressions qualify for install validation.

Add ad network identifiers

<key>AdNetworkIdentifiers</key>
<array>
    <string>example123.adattributionkit</string>
    <string>another456.adattributionkit</string>
</array>

Ad network IDs must be lowercase. SKAdNetwork IDs (ending in .skadnetwork) are also accepted -- the frameworks share IDs.

Display a UIEventAttributionView

For click-through custom-rendered ads, place one UIEventAttributionView over each tappable ad/control. It must cover the tappable area and stay above views that would intercept touches before handleTap() succeeds.

import UIKit

let attributionView = UIEventAttributionView()
attributionView.frame = adContentView.bounds
attributionView.isUserInteractionEnabled = true
adContentView.addSubview(attributionView)

Advertiser App Setup

The advertised app is the app someone installs or re-engages with after seeing an ad. It must call a conversion value update at least once to begin the postback conversion window.

Opt in to receive winning postback copies

Add AttributionCopyEndpoint under the top-level AdAttributionKit Info.plist dictionary so the device sends a copy of the winning postback to your server:

<key>AdAttributionKit</key>
<dict>
    <key>AttributionCopyEndpoint</key>
    <string>https://example.com</string>
</dict>

The system derives the well-known endpoint from the registrable domain in the URL, ignoring subdomains:

https://example.com/.well-known/appattribution/report-attribution/

Configure your server to accept HTTPS POST requests at that path. The domain must have a valid SSL certificate.

Opt in for re-engagement postback copies

Add a second key in the same AdAttributionKit dictionary to also receive copies of winning re-engagement postbacks:

<key>AdAttributionKit</key>
<dict>
    <key>AttributionCopyEndpoint</key>
    <string>https://example.com</string>
    <key>OptInForReengagementPostbackCopies</key>
    <true/>
</dict>

Update conversion value on first launch

Call a conversion value update as early as possible after first launch to begin the conversion window:

import AdAttributionKit

func applicationDidFinishLaunching() async {
    do {
        try await Postback.updateConversionValue(0, lockPostback: false)
    } catch {
        print("Failed to set initial conversion value: \(error)")
    }
}

Impressions

Ad networks create signed impressions using JWS (JSON Web Signature). The publisher app uses AppImpression to register and handle those impressions.

Create an impression from a JWS

import AdAttributionKit

let impression = try await AppImpression(compactJWS: signedJWSString)

The JWS contains the ad network ID, advertised item ID, publisher item ID, source identifier, timestamp, and optional re-engagement eligibility flag. See references/adattributionkit-patterns.md for JWS generation details.

Check device support

guard AppImpression.isSupported else {
    // Fall back to alternative ad display
    return
}

View-through impressions

Record a view impression when the ad content has been displayed and dismissed:

func handleAdViewed(impression: AppImpression) async {
    do {
        try await impression.handleView()
    } catch {
        print("Failed to record view-through impression: \(error)")
    }
}

For long-lived ad views, use beginView() and endView() to track view duration:

try await impression.beginView()
// ... ad remains visible ...
try await impression.endView()

Click-through impressions

Respond to ad taps by calling handleTap() within 15 minutes of creating the AppImpression; otherwise request a fresh impression. If the advertised app is not installed, the system opens its App Store or marketplace page. If installed, the system launches it directly.

func handleAdTapped(impression: AppImpression) async {
    do {
        try await impression.handleTap()
    } catch {
        print("Failed to record click-through impression: \(error)")
    }
}

A UIEventAttributionView must overlay the ad for handleTap() to succeed.

StoreKit-rendered ads

Pass the impression to StoreKit overlay or product view controller APIs. StoreKit automatically records view-through impressions after 2 seconds of display and click-through impressions on tap.

import StoreKit

let config = SKOverlay.AppConfiguration(appIdentifier: "1234567890",
                                         position: .bottom)
config.appImpression = impression

Postbacks

Postbacks are attribution reports the device sends to ad networks (and optionally to the advertised app developer) after a conversion event.

Conversion windows

Winning attributions can produce multiple postbacks across conversion windows; lower data tiers and nonwinning attributions disclose less. Load references/adattributionkit-patterns.md for the current window and delay matrix.

Time windows for events

Attribution eligibility windows are distinct from conversion/postback windows. Configure and verify view-through, click-through, install-update, and re-engagement limits from the current documentation and the reference; do not merge the two concepts.

Lock conversion values early

Lock the postback to finalize a conversion value before the window ends and receive the postback sooner:

try await Postback.updateConversionValue(
    42,
    coarseConversionValue: .high,
    lockPostback: true
)

After locking, the system ignores further updates in that conversion window.

Postback data by tier

Disclosure grows with the system-assigned data tier. Code and analytics must tolerate absent source digits, fine/coarse conversion values, publisher item ID, and country. The reference owns the detailed tier matrix.

Conversion Values

Fine-grained values

Fine values are integers from 0...63 (6 bits). They are available only in the first postback and only at Tier 2 or higher:

try await Postback.updateConversionValue(
    35,
    coarseConversionValue: .medium,
    lockPostback: false
)

Coarse values

Three levels for lower tiers and second/third postbacks:

// CoarseConversionValue cases: .low, .medium, .high
try await Postback.updateConversionValue(
    10,
    coarseConversionValue: .high,
    lockPostback: false
)

Update by conversion type (iOS 18+)

Separate conversion values for install vs. re-engagement postbacks. In server JSON, use "conversion-type": "re-engagement" with the hyphen; Swift APIs use .reengagement without it.

let installUpdate = PostbackUpdate(
    fineConversionValue: 20,
    lockPostback: false,
    conversionTypes: [.install]
)
try await Postback.updateConversionValue(installUpdate)

let reengagementUpdate = PostbackUpdate(
    fineConversionValue: 12,
    lockPostback: false,
    conversionTypes: [.reengagement]
)
try await Postback.updateConversionValue(reengagementUpdate)

Conversion tags (iOS 18.4+)

Use conversion tags to selectively update specific postbacks when overlapping conversion windows exist:

let update = PostbackUpdate(
    fineConversionValue: 15,
    lockPostback: false,
    conversionTag: savedConversionTag,
    conversionTypes: [.reengagement]
)
try await Postback.updateConversionValue(update)

The system delivers the conversion tag through the re-engagement URL's AdAttributionKitReengagementOpen query parameter.

Re-engagement

Re-engagement tracks users who already have the advertised app installed and interact with an ad to return to it.

Mark impressions as re-engagement eligible

Set eligible-for-re-engagement to true in the JWS payload when generating the impression.

Handle re-engagement taps with a URL

Pass a universal link that the system opens in the advertised app:

let reengagementURL = URL(string: "https://example.com/promo/summer")!
try await impression.handleTap(reengagementURL: reengagementURL)

The system appends AdAttributionKitReengagementOpen as a query parameter. The advertised app checks for this parameter to detect AdAttributionKit-driven opens:

func handleUniversalLink(_ url: URL) {
    let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
    let isReengagement = components?.queryItems?.contains(where: {
        $0.name == Postback.reengagementOpenURLParameter
    }) ?? false

    if isReengagement {
        // AdAttributionKit opened this app via a re-engagement ad
    }
}

Re-engagement limits

  • Only click-through interactions create re-engagement postbacks (not view-through).
  • The device enforces monthly per-app and yearly per-device re-engagement limits.
  • The AdAttributionKitReengagementOpen parameter is always present on the URL, even when the system does not create a postback.

Common Mistakes

MistakeFix
First launch never updates conversion valueCall the canonical first-launch update before the intended window elapses.
Ad network ID contains uppercase charactersUse the exact lowercase network identifier.
handleTap() uses a stale impression or lacks the current attribution view tapCover the ad with UIEventAttributionView, keep the impression fresh, and call from the validated tap flow.
Tap errors are discardedHandle expired-impression and missing-view cases explicitly.
Postback endpoint delays or drops the responseAccept, persist/queue processing, and return the expected success promptly.

Review Checklist

  • Publisher app includes all ad network IDs in AdNetworkIdentifiers (lowercase)
  • Ad network IDs match between publisher app's Info.plist and JWS kid
  • UIEventAttributionView overlays each tappable click-through ad/control
  • Click-through AppImpression is no older than 15 minutes at handleTap()
  • Advertised app calls updateConversionValue on first launch
  • Server endpoint at well-known path accepts HTTPS POST with valid SSL
  • Postback verification uses correct Apple public key for environment
  • Duplicate postbacks filtered by postback-identifier
  • Server responds with HTTP 200 to postback requests
  • Re-engagement URL is a registered universal link for the advertised app
  • Conversion value strategy accounts for all three conversion windows
  • AppImpression.isSupported checked before attempting impression APIs

References

FAQ

What does the adattributionkit skill do?

Measure 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 SKAdNetwork with AdAttributionKit for ad measurement.

How do I install adattributionkit?

Run npx skills add https://github.com/dpearson2699/swift-ios-skills --skill adattributionkit in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/adattributionkit/ (personal) or .claude/skills/adattributionkit/ (project).

Which AI agents can use adattributionkit?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is adattributionkit safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains adattributionkit?

It is published by dpearson2699 in dpearson2699/swift-ios-skills. It has 3,068 recorded installs.

More from dpearson2699/swift-ios-skills

View all →

swiftui-animation

dpearson2699/swift-ios-skills

Implement, diagnose, or review SwiftUI motion using explicit and scoped implicit animations, springs, transitions, PhaseAnimator, KeyframeAnimator, matched geometry or navigation zoom, SF Symbol effects, and custom Animation types. Use when views should animate on state changes, insertion, removal, navigation, or multi-step choreography, or when motion must respect Reduce Motion and Swift concurrency.

Mobile5.5K installs

ios-accessibility

dpearson2699/swift-ios-skills

Build and audit SwiftUI, UIKit, and AppKit accessibility for VoiceOver, Voice Control, Switch Control, Full Keyboard Access, Dynamic Type, focus restoration, labels/traits/actions, traversal, custom rotors, NSAccessibility, XCTest checks, adaptive system preferences, and App Store accessibility declarations. Use when implementing accessible UI, fixing an accessibility audit, testing assistive-technology behavior, or substantiating App Store Accessibility Nutrition Labels.

Mobile5K installs

swiftui-performance

dpearson2699/swift-ios-skills

Profile, diagnose, and remediate SwiftUI runtime performance using code review, Instruments, and repeatable measurements. Use when a SwiftUI screen renders slowly, scrolling or animations hitch, view bodies update excessively, list identity churns, layout work spikes, or broad Observation dependencies raise CPU cost. Covers evidence-based triage, SwiftUI Instruments lanes, lazy-container guardrails, state lifetime, and before/after verification.

Mobile4.6K installs

swiftui-patterns

dpearson2699/swift-ios-skills

Builds and reviews SwiftUI views with modern MV architecture, state, composition, isolated previews, and migration guidance. Covers @Observable ownership, @State/@Bindable/@Environment wiring, view decomposition, ViewModifiers, environment values, .task loading, iOS 26+ handoffs, Writing Tools, clipboard availability, and performance. Use when structuring SwiftUI state, managing @Observable, composing views, previewing meaningful UI states, or correcting SwiftUI patterns.

Mobile4.6K installs

app-store-review

dpearson2699/swift-ios-skills

Audits App Store submission readiness and rejection risk across current review guidelines, PrivacyInfo.xcprivacy and required-reason APIs, privacy labels, ATT, StoreKit payments, metadata, entitlements, widgets, and Live Activities. Use when preparing a submission, responding to rejection, reconciling privacy evidence, or separating upload blockers from cleanup.

Mobile4.6K installs

ios-networking

dpearson2699/swift-ios-skills

Build, review, or improve networking code in iOS/macOS apps using URLSession with async/await, structured concurrency, and modern Swift patterns. Use when working with REST APIs, downloading files, uploading data, WebSocket connections, pagination, retry logic, request middleware, caching, background transfers, or network reachability monitoring. Also use when handling HTTP requests, API clients, network error handling, or data fetching in Swift apps.

Mobile4.4K installs

Popular mobile skills

View category →

design-mobile-apps

designed-by-ai/skills

Design mobile app screens with Sleek, edit Sleek projects, and implement their designs in React Native or HTML.

Mobile1.1M installs

ios-design

uizze.sh

Design and review iOS UI/UX for iPhone apps in SwiftUI, UIKit, or React Native. Use for mobile app design, screen redesigns, navigation flows, typography, forms, keyboard and safe-area layout, and accessibility checks. Apply Apple Human Interface Guidelines and consult real product references through the optional Uizze connection.

Mobile416.5K installs

vercel-react-native-skills

vercel-labs/agent-skills

React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.

OfficialMobile256.7K installs

animate-expo

emilkowalski/skills

Build animations in React Native and Expo, making the decisions in the order that determines whether they feel right — should it animate, which thread it runs on, which properties, spring or timing, how the gesture hands off, how it degrades. Writes the implementation with Reanimated, Gesture Handler, Expo Router and expo-haptics. Use when animating anything in an Expo app, adding gestures, sheets, screen transitions, press feedback or haptics, or fixing motion that stutters on device. For web animation use animate.

Mobile140.1K installs

xcode-project-setup

firebase/agent-skills

Safely modifies Xcode projects (.pbxproj) to add Swift Packages and link files. Use this skill whenever an iOS project needs dependencies installed (e.g. Firebase, Alamofire).

OfficialMobile131.2K installs

firebase-remote-config-basics

firebase/agent-skills

Manages Firebase Remote Config templates, feature flags, loading strategies, and SDKs (Android, iOS). Use when downloading/deploying remoteconfig JSON templates, managing version history/feature flags, setting in-app defaults, fetchAndActivate(), real-time listeners, or SDK setup. Don't use for Firebase Hosting, Auth, Firestore, Data Connect, Crashlytics, or App Hosting.

OfficialMobile121.7K installs