Production-grade Claude Code skill for debugging, integrating, and auditing iOS marketing channel attribution under Apple's App Tracking Transparency (ATT) framework. Covers AppsFlyer, Adjust, Branch, Singular, Meta App Events, Apple Ads, Google App Campaigns, and TikTok — with SKAdNetwork 4 + AdAttributionKit dual attribution, Conversions API, privacy manifests, and the kind of production gotchas that don't make it into vendor docs.
If you've ever shipped an iOS app with paid user acquisition, you've seen at least one of these:
- 📉 "Most of our installs show as Organic in the MMP dashboard" — even though you're spending six figures on Meta and Apple Ads
- 🪤 MMP ATT wait is configured, but some events still leave before ATT resolves — usually because they are outside that SDK's queue
- 🤐 Meta says AEM is configured, but Ads Manager won't let you select it — and the docs don't explain why
- 💀 Apple Ads campaigns running, but 80% of installs are Organic — because the AdServices API token isn't being forwarded
- 🎯 SKAN postbacks arrive empty after migrating to AdAttributionKit — turns out one of two API calls got removed
- 🚫 App Review rejection under Guideline 5.1.2 — the ATT prompt was reachable on yesterday's build but not today's
This skill is a structured, codified playbook for all of the above — pulled from production iOS apps, cross-referenced against Apple, AppsFlyer, Adjust, and Meta documentation, and battle-tested against real incident reports.
When invoked inside Claude Code, the skill activates automatically on keywords like ATT, IDFA, SKAdNetwork, AppsFlyer, Adjust, Meta AEM, Apple Ads, attConsentWaiting, organic install surge, installs missing, events going organic, and 25+ other triggers.
Most marketing-attribution skills focus on either strategy (campaign structure, bid optimization) or single-vendor docs (just AppsFlyer, just Adjust). This skill focuses specifically on the plumbing layer — the place where bugs actually live in production:
- ✅ The ATT wait/delay scope gap across MMP queues, direct SDK calls, and backend/CAPI event pipes
- ✅ The capture-protection silent-defer bug that prevents the ATT prompt from showing for affected users
- ✅ The Facebook AppEvents silent-drop when ATT is denied
- ✅ The
AAAttribution.attributionToken()integration that fixes 80%-Organic-on-Apple-Ads - ✅ The dual-API-call requirement during SKAN→AAK migration
- ✅ React Native + Expo specifics that vendor docs gloss over
- ✅ A diagnostic script that grep's your iOS project for 12 known anti-patterns
It also routes properly: when a question is about strategy or campaign structure rather than plumbing, the skill explicitly delegates to the right upstream skill instead of pretending to be everything.
git clone https://github.com/Rylaa/ios-marketing-att-skill \
~/.claude/skills/marketing-att-pipelineRestart Claude Code. The skill registers itself and auto-activates on relevant keywords.
cd /path/to/your/ios/project
git clone https://github.com/Rylaa/ios-marketing-att-skill \
.claude/skills/marketing-att-pipelineIn Claude Code, type:
What does the marketing-att-pipeline skill cover?
Or trigger it organically:
We're seeing 60% Organic installs in AppsFlyer after migrating to Adjust v5.
What's wrong?
The skill should fire and route to references/att-timing-and-events.md.
marketing-att-pipeline/
├── SKILL.md Skill entry point, decision tree, channel ref
├── WORKFLOW.md 5-stage operating sequence + 5 ready-made playbooks
├── README.md (this file)
├── references/
│ ├── att-timing-and-events.md MMP wait/delay scope, unmanaged event pipes, RN/Expo
│ ├── consent-gating.md ATT 4-state matrix × 6 SDK init pattern
│ ├── adattributionkit-and-skan.md AAK + SKAN 4 + iOS 18 PostbackUpdate + re-engagement
│ ├── meta-aem-troubleshoot.md Prerequisites + ATE error + extinfo 16-pos
│ ├── apple-ads-audit.md ASA Health Score + AdServices API + Maximize Conv
│ ├── pre-launch-checklist.md 10-phase pre-paid-UA checklist
│ ├── tiktok-integration.md TikTok Business SDK + SKAN ownership matrix
│ └── symptom-to-cause.md Vague-symptom triage table
└── scripts/
└── diagnose.sh 12-check static analysis pass
| File | Lines | Purpose |
|---|---|---|
SKILL.md |
192 | Skill registration + decision tree |
WORKFLOW.md |
275 | 5-stage operating sequence |
references/adattributionkit-and-skan.md |
356 | Postback layer reference |
references/pre-launch-checklist.md |
253 | New-app launch playbook |
references/meta-aem-troubleshoot.md |
251 | Meta-specific AEM issues |
references/att-timing-and-events.md |
235 | ATT timing + event gap |
references/consent-gating.md |
208 | ATT 4-state per-SDK init |
references/apple-ads-audit.md |
204 | Apple Ads audit framework |
references/symptom-to-cause.md |
71 | Triage table |
scripts/diagnose.sh |
249 | Static analysis script |
| Total | 2,193 lines |
User: We migrated from Adjust v4 to v5 last week. Now 65% of installs in
the AppsFlyer dashboard show as Organic. We didn't change anything else.
Skill response:
1. Routes to references/att-timing-and-events.md
2. Identifies likely cause: Adjust v5 init API rename (appDidLaunch → initSdk)
3. Greps your codebase for `Adjust.appDidLaunch` (still v4 syntax → no-op in v5)
4. Patches with `Adjust.initSdk(adjustConfig)`
5. Verifies `enableFirstSessionDelay()` (method, not property)
6. Suggests verification: fresh install + 48h dashboard check
User: We're launching our new subscription app next month. AppsFlyer + Meta
+ Apple Ads. What do we need before flipping on paid UA?
Skill response:
1. Routes to references/pre-launch-checklist.md
2. Walks 10 phases: Info.plist, PrivacyInfo, App Store Connect, MMP SDK init,
ATT prompt, conversion value mapping, per-channel setup, deep linking,
verification, day-1 monitoring
3. Generates per-channel SDK init code (Swift + RN/Expo)
4. Produces Conversion Value encoding strategy for subscription apps (multi-window)
5. Runs scripts/diagnose.sh on your project to verify
User: We have $40K/month on Apple Search Ads. The dashboard shows 12K installs.
AppsFlyer shows 1.8K Apple Ads installs and 11K Organic. Help.
Skill response:
1. Routes to references/apple-ads-audit.md → AdServices API section
2. Diagnoses: AAAttribution.attributionToken() not implemented or not forwarded
3. Provides Swift integration code and MMP-specific AdServices checks
4. Verifies in MMP raw data: `media_source: Apple Search Ads` (not Organic)
5. Suggests waiting 24h to validate
User: Meta says our AEM is set up, but in Ads Manager when I create an app
campaign, the AEM event option is greyed out.
Skill response:
1. Routes to references/meta-aem-troubleshoot.md → Prerequisites section
2. Walks 7-step prerequisite checklist (app published, BM association, domain
verified, AEM/event sharing, app status, install volume, event sending mech)
3. Identifies likely missing prereq: domain verification or app-to-ad-account
association
4. Provides specific Meta UI navigation paths
5. Suggests waiting 24-72h after fix → verify in Test Events tab
User: We migrated to AdAttributionKit on iOS 17.4+. Postbacks arrive but
conversion-value field is always 0 or null.
Skill response:
1. Routes to references/adattributionkit-and-skan.md → Failure Mode 6
2. Greps codebase for both Postback.updateConversionValue and
SKAdNetwork.updatePostbackConversionValue — both must fire
3. Identifies missing dual-call (common migration regression)
4. Patches the missing path
5. Verifies with simctl log filter on both subsystems
User: Our marketing isn't working anymore. Help.
Skill response:
1. Routes to references/symptom-to-cause.md (triage)
2. Asks Q1-Q4: what specifically broken? when started? which channel? what
does MMP raw data show?
3. Routes from triage answers to specific reference file
4. Optionally runs scripts/diagnose.sh as cross-check
User: Our ATT opt-in rate is at 12%. Industry says 35-50%. Why so low?
Skill response:
1. Routes to references/consent-gating.md → Pre-permission section
2. Reviews your current ATT prompt UX
3. Provides Apple-approved pre-permission screen Swift code (15-25% lift)
4. Reviews NSUserTrackingUsageDescription wording
5. Lists App Review rejection patterns to avoid (incentive ban, two-button
effective-deny, vague third-party language, modal stacking)
User: First time integrating Adjust in a React Native + Expo app. What's
the order of operations?
Skill response:
1. Routes to references/att-timing-and-events.md → React Native + Expo section
2. Provides JS bridge code: AdjustConfig + setAttConsentWaitingInterval +
enableFirstSessionDelay
3. Adds expo-tracking-transparency for the ATT prompt
4. Calls out Expo Go limitation (must use dev client / standalone)
5. Notes plugin-version-must-match-native-SDK-version constraint
bash ~/.claude/skills/marketing-att-pipeline/scripts/diagnose.sh /path/to/ios/projectPerforms 12 static checks on your iOS project source:
| # | Check | What it catches |
|---|---|---|
| 1 | NSUserTrackingUsageDescription |
ATT prompt would crash without it |
| 2 | SKAdNetworkItems |
Source-app SKAN IDs missing, or partner-required ID list absent |
| 3 | PrivacyInfo.xcprivacy |
App Review rejection (iOS 17+) |
| 4 | AttributionCopyEndpoint plist key |
AAK postback copy not enabled |
| 5 | AppsFlyer waitForATTUserAuthorization |
Install ships before ATT resolves |
| 6 | Adjust attConsentWaitingInterval |
Same problem on Adjust |
| 7 | ATTrackingManager request call site |
ATT never actually requested |
| 8 | unmanaged event + ATT/privacy-gating heuristic | Direct SDK/backend event pipes that need gating |
| 8b | Capture-protection silent-defer | isCaptured near requestTrackingAuthorization |
| 8c | SDK init order | Wait config before start() / initSdk() |
| 9 | SKAN/AAK conversion value updates | Postbacks won't carry CV data |
| 10 | Marketing SDK inventory | Auto-detect AppsFlyer/Adjust/Branch/Singular/Meta/Firebase/AdMob |
Output:
=== iOS Marketing + ATT Pipeline Diagnosis ===
Root: /Users/me/Projects/MyApp
[1/10] NSUserTrackingUsageDescription
✓ found in Info.plist
[2/10] SKAdNetworkItems
✓ SKAdNetworkItems present
[3/10] PrivacyInfo.xcprivacy
✓ PrivacyInfo.xcprivacy exists
[4/10] AdAttributionKit
⚠ no AttributionCopyEndpoint — server-side postback mirror not enabled
[5/10] AppsFlyer ATT wait config
✓ AppsFlyer wait config found
[6/10] Adjust ATT wait / first-session delay config
⚠ Adjust SDK not detected — skip if not using
[7/10] ATT prompt invocation
✓ ATT request found
[8/10] unmanaged event call sites + ATT/privacy-gating
⚠ 14 marketing event call sites; ~3 file(s) lack obvious ATT/privacy-gating reference
[8b] Capture-protection ATT defer
(no findings)
[8c] SDK init order (waitFor* config BEFORE start)
(passing)
[9/10] SKAN/AAK conversion value updates
✓ conversion value update calls found
[10/10] Marketing SDK inventory
✓ marketing SDKs detected:
→ AppsFlyer
→ Meta SDK
→ Firebase
PASS / WARN / FAIL color-coded, with action items per finding.
The skill activates automatically when Claude Code detects any of these in user input:
Core ATT: ATT, IDFA, ATTrackingManager, NSUserTrackingUsageDescription, App Tracking Transparency, attConsentWaiting, "tracking authorization"
MMP SDKs: AppsFlyer, AppsFlyerLib, Adjust, Branch, Singular, MMP, "mobile measurement partner"
Postback layer: SKAdNetwork, SKAN, AdAttributionKit, AAK, conversion value, postback, conversion window, dual attribution
Channels: Apple Ads, Apple Search Ads, ASA, Google App Campaigns, GAC, UAC, Meta App Install, Meta AEM, ATE, Aggregated Event Measurement, TikTok App Install
Symptoms: organic install surge, "installs missing", "events going organic", "attribution broken", "spend not converting"
Privacy: privacy manifest, PrivacyInfo.xcprivacy, required reason API, App Privacy
Setup keywords: install attribution, conversion value mapping, install order, SDK init order, Firebase iOS attribution
For any task, the skill follows a 5-stage flow defined in WORKFLOW.md:
┌──────────────┐ ┌──────────┐ ┌──────────────┐ ┌──────┐ ┌─────────┐
│ 1. CONTEXT │ → │ 2. ROUTE │ → │ 3. DIAGNOSE │ → │ 4. ACT│ → │ 5. VERIFY│
└──────────────┘ └──────────┘ └──────────────┘ └──────┘ └─────────┘
1. CONTEXT — Gather: iOS-only or RN/Expo, MMP choice, channels active, iOS min target, app type (sub/IAP/utility), build environment (App Store/TestFlight/local), symptom or goal.
2. ROUTE — Map user's stated problem to ONE primary reference file via decision tree.
3. DIAGNOSE — Read the reference fully, grep for relevant symbols in user's project, run diagnose.sh if appropriate.
4. ACT — Three modes: direct fix (Mode A), recommendation only (Mode B), or audit with score (Mode C).
5. VERIFY — Compile-time + static (re-run diagnose) + runtime (real device fresh install + 24-48h dashboard check).
WORKFLOW.md includes 5 ready-made multi-stage playbooks: post-ship attribution drop, pre-launch new app, ASA + AppsFlyer audit, Meta AEM not selectable, SKAN→AAK migration regression.
| iOS minimum target | Skill features available |
|---|---|
| iOS 14.5+ | Core ATT + SKAN 3 |
| iOS 16.1+ | + SKAN 4 (3 windows, fine + coarse, lockWindow) |
| iOS 17.4+ | + AdAttributionKit (single-value API) |
| iOS 18.0+ | + AAK PostbackUpdate type, conversion types |
| iOS 18.4+ | + AAK in-Settings developer mode (5-10 min postbacks) |
| MMP / SDK | Recommended version |
|---|---|
| Adjust iOS SDK | v5.x current line; v4.34.0 minimum |
| AppsFlyer iOS SDK | 6.14+ (full SKAN 4 + AAK support) |
| Meta SDK (iOS) | 17+ (privacy manifest); 18+ for iOS 17 ATT auto-detection |
| Google Mobile Ads | 11.x+ |
| Firebase iOS SDK | 10.21+ |
| TikTok Business iOS SDK | 1.4+ for privacy manifest |
To keep the scope manageable, the following are out of scope:
- ❌ Web / SPA attribution — use a web analytics skill
- ❌ Apple Search Ads keyword strategy / bid tuning — delegate to upstream
ads-appleskill - ❌ ASO / store listing optimization — separate domain
- ❌ Android Privacy Sandbox — Android-specific, separate skill needed
- ❌ Push notification attribution — different SDK domain
- ❌ OAuth / authentication flows —
auth-specialistskill - ❌ GA4 / web analytics deep dive — use a web analytics skill
When the skill detects a question outside its scope, it explicitly delegates rather than guessing.
This skill merges three excellent upstream community skills:
| Upstream | Used For |
|---|---|
AgriciDaniel/claude-ads → ads-apple |
Apple Ads audit framework, MMP/AAK/ATT integration checklist, ASA Health Score scoring |
dpearson2699/swift-ios-skills → adattributionkit |
AdAttributionKit role model, conversion windows, postback tiers, code patterns |
mukul975/Privacy-Data-Protection-Skills → managing-mobile-app-consent |
ATT 4-state matrix, pre-permission prompt design, per-SDK consent propagation patterns |
Plus production lessons from real iOS app incidents that surfaced:
- The ATT wait/delay scope gap (MMP queues, direct SDK calls, and backend pipes have different behavior)
- The capture-protection silent-defer bug (ATT prompt never shows for affected users)
- The Facebook AppEvents silent-drop on ATT-denied users
- The dual-API-call requirement for SKAN ↔ AAK interop
These production incidents are what differentiate this skill from a "merge of three skills" — they're patterns that show up in real apps and aren't documented in vendor docs.
[1/10] NSUserTrackingUsageDescription ✓
[2/10] SKAdNetworkItems ✓
[3/10] PrivacyInfo.xcprivacy ✓
[4/10] AdAttributionKit ✓
[5/10] AppsFlyer ATT wait config ✓
[6/10] Adjust ATT wait / first-session delay (skipped — Adjust not used)
[7/10] ATT prompt invocation ✓
[8/10] unmanaged event call sites ⚠ 14 sites — manual review
[8b] Capture-protection ATT defer (no findings)
[8c] SDK init order ✓
[9/10] SKAN/AAK conversion value updates ✓
[10/10] Marketing SDK inventory ✓ AppsFlyer, Meta, Firebase
[8/10] unmanaged event call sites + ATT/privacy-gating
⚠ 17 marketing event call sites; ~5 file(s) lack obvious ATT/privacy-gating reference
/Features/Splash/SplashView.swift:39
/Features/Onboarding/HeroPage.swift:250
/Features/Onboarding/EffectPickerPage.swift:388
/AppCore/ApplicationDelegate.swift:486
/Features/Onboarding/LoadingPage.swift:179
[8b] Capture-protection ATT defer
✗ /AppCore/ATTController.swift uses isCaptured near requestTrackingAuthorization
→ Add a hard timeout fallback so prompt fires regardless of capture state
This skill is at v1.2.0. Recent work:
- ✅ Meta AEM legacy event-priority setup — replaced with current guidance covering event sharing, mapping, and eligibility
- ✅
Settings.shared.isAdvertiserTrackingEnablediOS 17+ deprecation — wrapped inif #unavailable(iOS 17) { ... }blocks - ✅
NSAdvertisingAttributionReportEndpointplist key — added to pre-launch checklist + AAK reference with provider URL table (Adjust, AppsFlyer, Branch, Singular) - ✅ TikTok integration — new dedicated
references/tiktok-integration.mdwith full init, SKAN ownership decision matrix, hybrid MMP+SDK setup
- Branch.io specific gotchas (
branch_referrableflag, deferred deep link sandbox) - Singular SKAN Decoder pattern
- Conversions API server-side flow for Meta/TikTok
- Firebase + GA4 attribution consolidated reference
- iOS 26 ATT persistence change documentation (when Apple confirms)
PRs welcome — see "Contributing" below.
Contributions are welcome. Especially valued:
- Production incident write-ups — real bugs from real apps with real symptoms and the actual fix
- Vendor doc updates — when AppsFlyer, Adjust, Meta, Apple change their APIs (this happens often)
- iOS version updates — when Apple releases new ATT/AAK behavior
diagnose.shimprovements — additional checks, better heuristics, false-positive reduction
- Fork + branch
- Make changes; if you're adding a reference file, link it from
SKILL.mddecision tree ANDWORKFLOW.mdrouting matrix - Run
bash scripts/diagnose.sh /tmp(sanity check the script still parses) - Update
CHANGELOG.mdwith what changed - Bump
metadata.versioninSKILL.mdfrontmatter - Submit PR with: what changed, why, link to incident report if applicable
- Markdown: GitHub-flavored, no HTML except in tables
- Code samples: Swift 5.9+ for native iOS, TypeScript for RN/Expo
- No emoji in skill body files (only in this README)
- Keep references short and dense — readers will scan, not read
If you write about, fork, or reference this skill, the following citation is appreciated but not required:
Marketing + ATT Pipeline (iOS) Claude Code Skill
https://github.com/Rylaa/ios-marketing-att-skill
The skill itself credits its three upstream sources in SKILL.md's metadata.sources field.
MIT License — see LICENSE file.
This means you can:
- Use it commercially
- Modify it
- Distribute it
- Use it privately
You must:
- Include the copyright notice and license in copies/substantial portions
The three upstream sources are also MIT or Apache-2.0 licensed, so this aggregated work has no licensing conflicts.
| Field | Value |
|---|---|
| Skill version | 1.2.0 |
| Last verified | 2026-05-07 |
| iOS baseline | 17.4+ for AAK; 18+ for PostbackUpdate API |
| Status | Active — used in production by maintainer |
When iOS major versions change (annual September) or any of the credited MMP SDKs ship a major version, expect updates within 2-4 weeks. File an issue if something looks stale.
If your question is broader than iOS marketing attribution, these adjacent skills are worth installing:
ads-apple(AgriciDaniel/claude-ads) — Apple Ads campaign strategy, bid optimization, CPP testing, keyword researchadattributionkit(dpearson2699/swift-ios-skills) — Even deeper AAK-only reference; this skill borrows from itapp-store-review(dpearson2699/swift-ios-skills) — App Store rejection prevention, privacy manifest deep dive, IAP review rulesmobile-deep-linking-specialist(curiositech/windags-skills) — Universal Links, App Links, deferred deep linking, attributionmanaging-mobile-app-consent(mukul975/Privacy-Data-Protection-Skills) — GDPR / EU consent flows, Android Privacy Sandbox, regulatory compliance
The marketing-att-pipeline skill explicitly delegates to these when the user's question falls outside its scope.
Q: Does this skill work with non-Claude AI assistants?
A: It's structured as a Claude Code skill with auto-discovery via the Skill tool. Other assistants that support skill-style markdown registration (Cursor's rules, GitHub Copilot's instructions) can use the content but won't get auto-routing — you'd need to manually load the relevant reference file.
Q: Why merge upstream skills instead of just installing them all? A: Skill auto-discovery picks the first-matching skill on a keyword. Three overlapping skills means unpredictable routing and missed coverage. A merged skill with explicit decision tree gives consistent behavior. Plus the merge adds production incident knowledge that none of the upstreams have.
Q: Can I use this in a non-iOS context? A: No — the entire skill assumes iOS. Cross-platform (RN/Expo) is partially covered for the JS bridge layer, but the Apple ATT/AAK/SKAN frameworks are iOS-only. Android marketing attribution is a different beast.
Q: How is this different from just reading the AppsFlyer / Adjust / Apple docs? A: Vendor docs tell you the API surface. This skill tells you what goes wrong in production, where the docs are misleading, and which specific code paths trigger which specific symptoms. It's the difference between a reference manual and a debugging playbook.
Q: Will this skill be maintained? A: Yes — the maintainer uses it in production iOS apps, so it gets updated whenever a new iOS release or MMP SDK version breaks something. Expect 2-4 update cycles per year.
Q: Can I integrate this into a CI pipeline?
A: Yes — scripts/diagnose.sh is designed to be runnable headless. Add it to your iOS CI as a non-blocking warning step:
- name: Marketing-ATT Diagnostics
run: bash .claude/skills/marketing-att-pipeline/scripts/diagnose.sh .
continue-on-error: true- Apple's ATT, SKAdNetwork, and AdAttributionKit teams for documentation that, while sometimes confusing, is at least available
- AppsFlyer, Adjust, and Meta engineering teams whose support tickets, changelog notes, and developer forum answers built the institutional knowledge this skill captures
- The community skill authors (AgriciDaniel, dpearson2699, mukul975) whose work is the foundation
- Anthropic for Claude Code and the skill system that makes this kind of structured, discoverable expertise possible
Built for iOS engineers who'd rather fix the bug than read the docs.