All Articles
CategoryBusiness
Reading Time
14 min read
Published
2025-01-22
Word Count
3,408words

Grab a coffee — this one is a deep dive!

Mobile Attribution: MMP Selection and SKAdNetwork

Summary

From designing SKAdNetwork conversion values to Play Install Referrer, from MMP selection criteria to reading aggregated data correctly: a guide to mobile attribution and SKAdNetwork measurement.

  • In SKAN 4, fine conversion value is a 6-bit integer between 0-63; on low-volume campaigns it falls back to a coarse low/medium/high value.
  • Postbacks are split into 3 windows of 0-2, 3-7, and 8-35 days; only the first window can carry fine value, the second and third are always coarse.
  • Play Install Referrer retains the referrer URL and timestamps for 90 days; it's the primary measurement source on Android today.
  • When choosing an MMP, postback schema management, fraud detection, deep linking, and the data export API are the most decisive criteria.
Mobile Attribution: MMP Selection and SKAdNetwork

In mobile marketing, the question "which channel brought this user" no longer has a simple answer. Since IDFA was largely disabled on iOS after App Tracking Transparency, Apple moved attribution to aggregated frameworks like SKAdNetwork and AdAttributionKit; on Android, Play Install Referrer and Google's privacy-first measurement experiments are going through a parallel transformation. This guide covers three questions a mobile developer or growth team needs to answer: how SKAdNetwork's mechanics work, when a Mobile Measurement Partner (MMP) is actually needed, and what to watch for when reading aggregated data correctly.

💡 Pro Tip: Before you start designing conversion values, write down which 3-5 in-app events will actually change a marketing decision — filling all 64 values with an "as much data as possible" mindset only speeds up your fall to coarse value on low-volume campaigns.

Table of Contents

Why Attribution Broke: ATT and After

With App Tracking Transparency, user-level, deterministic cross-device attribution (click-to-install matching based on IDFA) is no longer possible for users who don't opt in on iOS. The result was the industry splitting into two parallel paths: Apple's own aggregated frameworks (SKAdNetwork, later AdAttributionKit) and MMP layers that keep deterministic data limited to opted-in users and model (probabilistic attribution) the rest.

Deterministic vs. Aggregated Measurement

Deterministic measurement matches a click and an install one-to-one through a shared identifier (IDFA, GAID); it allows user-level LTV calculation but requires consent. Aggregated measurement (like SKAdNetwork), on the other hand, returns no user-level data at all — it only provides a delayed, resolution-reduced (fine→coarse, field-restricted) signal at the campaign/ad-network level. This makes campaign optimization possible but makes user-level cohort analysis impossible.

SKAdNetwork Mechanics and Conversion Value Design

SKAdNetwork is Apple's attribution framework offered inside StoreKit; it tells the ad network which campaign an install came from without sharing any user-level identity. With SKAN 4, two types of conversion value were defined: fine and coarse.

Fine conversion value is a 6-bit integer, meaning it can take 64 possible values from 0 to 63 (Apple Developer, updatePostbackConversionValue). You use these 64 states to encode your app's most critical post-install events (for example onboarding completion, trial start, first purchase) as a bit mask. Coarse conversion value was added with SKAN 4 and has only three levels: low, medium, high. Apple checks whether the volume generated by the app/domain showing the ad, the app being advertised, the country, and the hierarchical source identifier meets a "crowd anonymity" threshold; when the threshold isn't met, it returns coarse value instead of fine value, and the fields carried in the postback are also restricted based on the privacy level (Apple Developer, Receiving postbacks in multiple conversion windows).

swift
1import StoreKit
2 
3// A simple conversion value update: triggered on first purchase
4func reportFirstPurchase() {
5 // choose an integer between 0-63 as the fine value field
6 let conversionValue = 22 // e.g. bit for "onboarding completed + first purchase"
7 
8 if #available(iOS 16.1, *) {
9 SKAdNetwork.updatePostbackConversionValue(
10 conversionValue,
11 coarseValue: .medium
12 ) { error in
13 if let error {
14 print("SKAdNetwork update error: \(error)")
15 }
16 }
17 }
18}

Postback timing was also split into three windows with SKAN 4. By Apple's definition, the first window covers days 0-2 after install, the second window days 3-7, and the third window days 8-35; only the first postback can carry a fine-grained conversion value, the second and third postbacks always return coarse value (Apple Developer, Receiving postbacks in multiple conversion windows). This means it's pointless to try to squeeze a value spread over a long funnel (e.g. a renewal on day 10) into the first postback — that signal already belongs to the third window and will arrive at coarse resolution.

Conversion Value Design Rules

  • Time-sensitive bits: Allocate the bulk of your fine value to events expected to happen within 0-2 days, because only that window can get fine resolution.
  • Locking (lockWindow): Only the updatePostbackConversionValue(_:coarseValue:lockWindow:completionHandler:) overload closes the window early and immediately starts postback preparation, so the signal arrives sooner. The random delay duration doesn't change (24-48 hours for the first postback, 24-144 hours for the second/third), only the counter starts from the lock moment — in exchange, you lose whatever data would have accumulated in the remaining window time (Apple Developer, Receiving postbacks in multiple conversion windows; AdAttributionKit-SKAdNetwork interoperability).
  • Falling back to coarse scenario: Don't build your design on a small-scale campaign based solely on fine value — have a meaningful interpretation plan for coarse (low/medium/high) too.

The Android Side: Play Install Referrer and Privacy Sandbox

While GAID, Android's counterpart to IDFA, is still available on many devices, Google is also pursuing its own aggregated measurement path. There are two separate layers: the Play Install Referrer API you use in production today, and the Privacy Sandbox measurement APIs still in testing.

Play Install Referrer API

The Play Install Referrer API securely returns which referrer information an app was installed with via Google Play. The data it returns: the referrer URL, client- and server-side timestamps of the click, client- and server-side timestamps of the start of install, and the app's version at first install. Referrer information is retained for 90 days and doesn't change unless the app is reinstalled; accessing the API requires the device to have Play Store app version 8.3.73 or higher (Android Developers, Install Referrer API; for the version requirement see Android Developers, Install Referrer API overview).

kotlin
1import com.android.installreferrer.api.InstallReferrerClient
2import com.android.installreferrer.api.InstallReferrerStateListener
3import com.android.installreferrer.api.InstallReferrerClient.InstallReferrerResponse
4 
5fun fetchInstallReferrer(context: Context) {
6 val referrerClient = InstallReferrerClient.newBuilder(context).build()
7 referrerClient.startConnection(object : InstallReferrerStateListener {
8 override fun onInstallReferrerSetupFinished(responseCode: Int) {
9 when (responseCode) {
10 InstallReferrerResponse.OK -> {
11 val response = referrerClient.installReferrer
12 val referrerUrl = response.installReferrer
13 val clickTimestamp = response.referrerClickTimestampSeconds
14 val installTimestamp = response.installBeginTimestampSeconds
15 // forward referrerUrl to your attribution server
16 referrerClient.endConnection()
17 }
18 InstallReferrerResponse.FEATURE_NOT_SUPPORTED -> {
19 // Play Store version is outdated
20 }
21 InstallReferrerResponse.SERVICE_UNAVAILABLE -> {
22 // connection dropped, retry
23 }
24 }
25 }
26 
27 override fun onInstallReferrerServiceDisconnected() {
28 // reconnection logic
29 }
30 })
31}

The Measurement Side of Privacy Sandbox

Under Google's Privacy Sandbox on Android initiative, an Attribution Reporting API was being developed; this API aimed to match source and trigger events on-device and deliver event-level and aggregatable reports to ad tech parties in an aggregated form. As of early 2025, this API was still in Developer Preview/Beta: integration required installing a Privacy Sandbox system image on a supported device or emulator, enabling the API via ADB commands, and comparing raw reports with a debug key (Privacy Sandbox, Attribution Reporting developer guide). In other words, as of when this article was written, it was neither mandatory nor sufficient on its own as a production measurement layer — Play Install Referrer remained the primary source.

Four Frameworks, Quick Comparison

Framework
Platform
Resolution
Timing
SKAdNetwork (SKAN 4)
iOS
Fine (0-63) or coarse (low/medium/high)
3 postback windows: 0-2, 3-7, 8-35 days
AdAttributionKit
iOS / alternative marketplaces
Works alongside SKAdNetwork, similar postback model
Same postback window model as SKAdNetwork; only a single impression wins per conversion
Play Install Referrer
Android
Referrer URL + timestamps (user-level)
At install time; data retained for 90 days
Privacy Sandbox Attribution Reporting
Android
Event-level + aggregatable reports (aggregated)
Beta/testing stage as of when this article was written

MMP Selection Criteria and Decision Table

A Mobile Measurement Partner combines aggregated frameworks like SKAdNetwork/AdAttributionKit, Play Install Referrer, and deterministic signals from opted-in users into a single reporting layer; it also provides attribution window configuration, postback routing, fraud detection, deep linking, and ready-made integrations with numerous ad networks (AppsFlyer, Fraud Protection product page; RudderStack, what is an MMP). Fraud detection matters especially with multi-channel budgets: a single ad network only sees traffic in its own inventory, so it can't catch a fraud pattern spanning channels — only a measurement layer watching all channels at once can.

  • SKAdNetwork/AdAttributionKit postback management: Can it manage the conversion value schema (fine bit mask + coarse mapping) for you and distribute it to ad networks?
  • Fraud detection: Does it offer a dedicated module against attack types like install hijacking and click spamming, or just basic filtering?
  • Deep linking: Is deferred deep linking (routing to the correct screen after install) part of the SDK, or a separate product?
  • Data location and GDPR/local compliance: Which region the raw data is kept in and whether it aligns with your own data retention policy.
  • SDK size and init time: How much it affects app cold-start performance.
  • API/webhook access: Are there ready connectors to move reported data into your own data warehouse (BigQuery, S3, etc.), or does it only offer a dashboard?
Criterion
Why It Matters
How to Test
Postback schema management
Manually updating the fine/coarse design is error-prone
Compare the conversion value log in a test campaign
Fraud module coverage
Cross-channel fraud is invisible to a single network
Sandbox test with a known fraudulent traffic source
Deep link SDK
Correct post-install screen = conversion rate
End-to-end install test with different campaign links
Data export API
Independent access to your own BI layer
Try a sample export/webhook
SDK init time
Affects cold-start metrics
Measure with Xcode Instruments / Android Profiler

The Technical Cost of SDK Integration

Adding an MMP SDK is generally a three-stage job: SDK setup and initialization, implementing the deep link/deferred deep link handler, and validating conversion value/event mapping. The hidden cost is mostly in the third stage — mapping the SDK's own event taxonomy (e.g. "purchase", "subscribe", "tutorial_complete") one-to-one with your own product analytics events requires a separate QA cycle. If you skip this, the "conversion" numbers shown in the dashboard drift away from the event definitions the product team actually uses, and the two teams start arguing over different numbers.

bash
1# iOS: keep SKAdNetwork IDs in sync with the list ad networks share
2# check the SKAdNetworkItems array in Info.plist
3plutil -extract SKAdNetworkItems xml1 -o - Info.plist | grep -c "SKAdNetworkIdentifier"
json
1{
2 "event_mapping": {
3 "product_analytics_event": "app_purchase_completed",
4 "mmp_event_name": "af_purchase",
5 "conversion_value_bit_range": [16, 21],
6 "coarse_fallback": "medium"
7 }
8}

Reading Aggregated Data Correctly

Aggregated postback data is distorted by three things: delay, thresholding, and data-tier narrowing. Delay comes from the random duration Apple applies to every postback (24-48 hours for the first, 24-144 hours for the second/third) — that's why a metric like "today's ROAS" can't be produced from SKAdNetwork data, only a delayed window report can. Thresholding means campaigns below the crowd anonymity threshold get coarse instead of fine value; this seriously lowers data resolution on small-budget test campaigns. Data-tier narrowing is Apple restricting the postback fields (source-identifier, conversion-value, coarse-conversion-value, source-app-id, country-code) based on the privacy level — at the lowest tier only the first postback is sent; rather than treating a single campaign's postback count as absolute truth, tracking the trend over time is a more reliable approach (Apple Developer, Receiving postbacks in multiple conversion windows).

Practical Reading Rules

  • Trend over absolute number: Watch the weekly moving average, not a single day's postback count.
  • Segment by window: Don't mix fine value from the 0-2 day window with coarse value from the 8-35 day window in the same table.
  • Treat coarse as a rough indicator: Don't interpret low/medium/high as a precise number; use it only for relative ranking.

Building Your Own Measurement Layer

Some teams prefer to build their own postback receiver servers to reduce MMP cost or data dependency. This requires a server that hosts an endpoint StoreKit will POST SKAdNetwork postbacks to — either the ad network's or an opted-in developer's own — and a layer that maps the conversion value schema to your product events. Doing this increases data ownership but also means building fraud detection, multi-network integration, and deep linking from scratch — so for most mid-sized teams, an MMP plus export to your own data warehouse is a more balanced starting point.

ts
1// A simple SKAdNetwork postback receiver (Node/Express sketch)
2app.post("/skadnetwork/postback", (req, res) => {
3 const {
4 "ad-network-id": adNetworkId,
5 "source-identifier": sourceIdentifier,
6 "conversion-value": conversionValue,
7 "coarse-conversion-value": coarseConversionValue,
8 "postback-sequence-index": postbackSequenceIndex,
9 "source-app-id": sourceAppId,
10 } = req.body;
11 
12 // 1) verify Apple's signature (separate step, omitted here)
13 // 2) the first postback carries fine, second/third only coarse
14 const mappedEvent =
15 conversionValue !== undefined
16 ? decodeConversionValue(conversionValue)
17 : decodeCoarseConversionValue(coarseConversionValue);
18 
19 saveAttributionRecord({
20 adNetworkId,
21 sourceIdentifier,
22 mappedEvent,
23 sourceAppId,
24 postbackSequenceIndex,
25 });
26 res.sendStatus(200);
27});

Recommendation by App Type

  • Early stage / single platform (iOS only): Set up SKAdNetwork correctly with the right conversion value schema first, add an MMP only once your fraud detection and deep linking needs become clear.
  • Multi-channel, mid-large budget: An MMP is mandatory — postback schema management and cross-channel fraud visibility can't be sustained by hand.
  • Android only: Play Install Referrer is your primary source today; keep watching the Privacy Sandbox measurement APIs without making production dependent on them.
  • Enterprise / data-ownership priority: Export the MMP's raw data into your own data warehouse, use the dashboard reports only for quick checks.

FAQ

How does SKAdNetwork work, what is conversion value?

SKAdNetwork is Apple's framework that runs through StoreKit and delivers a campaign-level conversion signal to the ad network without sharing any user-level identity. Conversion value is what the app reports via updatePostbackConversionValue; in SKAN 4 it's delivered as fine (a 6-bit integer from 0-63) or, at volumes below the crowd anonymity threshold, as coarse (low/medium/high) (Apple Developer, Receiving postbacks in multiple conversion windows).

What should you look at when choosing an MMP (AppsFlyer, Adjust)?

Postback schema management, fraud detection coverage, deep linking support, data location/compliance, the SDK's impact on app launch, and the ability to export to your own data warehouse are the most decisive criteria; these points are summarized in the decision table above.

How do you measure campaign performance after ATT?

Deterministic data is still available for users who opt in; for those who don't, performance is read through SKAdNetwork/AdAttributionKit postbacks, as delayed, window-based (0-2, 3-7, 8-35 days) aggregated signals. Tracking a weekly trend gives more reliable results than a precise daily ROAS.

How does Android's Privacy Sandbox change attribution?

During the period this article covers (January 2025), Privacy Sandbox's Attribution Reporting API was still in developer beta/testing and had not replaced Play Install Referrer; enabling the API required a special system image and ADB command (Privacy Sandbox developer guide). You can find the current status in the "Update" section below.

Update (September 2026)

On October 17, 2025, Google officially announced it was winding down a large portion of the Privacy Sandbox initiative — including the Attribution Reporting API on Android; the status page moved this API on Android to "Deprecate and remove" status (Privacy Sandbox status page). Ecosystem feedback about their expected value and the technologies' low levels of adoption were cited as the reasons (Privacy Sandbox, plan update announcement). The practical consequence: teams that had built a roadmap dependent on Privacy Sandbox on Android need to move their measurement strategy back to Play Install Referrer and MMPs' own modeling layers. On iOS, both AdAttributionKit and SKAdNetwork remain callable; both frameworks' ad network IDs stay valid, but only one impression wins per conversion — the winner can come from either framework (AdAttributionKit-SKAdNetwork interoperability documentation).

Related posts published later:

Conclusion

Mobile attribution is no longer one-to-one matching through a single identity — it's the job of gathering and interpreting multiple aggregated signals (SKAdNetwork/AdAttributionKit postbacks, Play Install Referrer, deterministic data from opted-in users). Before moving to an MMP, verify you can set up SKAdNetwork's conversion value schema correctly on your own; this lets you ask more informed questions when choosing an MMP, and it doesn't leave you empty-handed in the early stage when you need to go without one.

GOLDEN TIP

The most valuable insight in this article

This tip holds the article's most important takeaway.

Easter Egg

You found a hidden gem!

There's a hidden detail in this section. Want to uncover it?

Reader Reward

We've gathered the points that are easy to skip while implementing this guide but get expensive later into a single checklist. Check off the items below one by one before you lock your SKAdNetwork schema or sign an MMP contract.

Sources

Tags

#mobile attribution#SKAdNetwork#MMP#AdAttributionKit#privacy-first measurement#Play Install Referrer#ATT#mobile marketing
Muhittin Çamdalı

Muhittin Çamdalı

Lead Mobile Engineer

Lead Mobile Engineer with 12+ years of experience. Expert in iOS, Android and cross-platform architectures with Swift, SwiftUI, Kotlin and Flutter. I build performant, user-friendly mobile apps.

iOS Development News

Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.

We respect your privacy. You can unsubscribe at any time.

Share