The constraint, plainly

On iOS you do not get an event stream. You get a small number, once per install, within a measurement window, and only if the campaign passed Apple's anonymity threshold. Everything you will ever know about an iOS cohort has to fit inside that.

Which makes schema design a budgeting exercise. Every bucket you spend on something you will not act on is a bucket unavailable to the thing you will.

Start from the decision, not from the events

The wrong process is to list your in-app events and map them to values. The right one is to write down the decision the data has to support, then encode only what distinguishes the options.

For most subscription apps that decision is: which campaigns produce users who start a trial and stay long enough to convert. Which means the schema needs to separate no-trial from trial-started from trial-with-engagement, and almost nothing else.

A workable subscription schema – six values instead of sixty-four
ValueMeaningWhy it earns a slot
0Install, no meaningful activityThe baseline you are trying to buy less of
1Onboarding completedSeparates real users from accidental installs
2Reached the paywallDistinguishes acquisition problems from paywall problems
3Trial startedThe primary optimisation event
4Trial started, engaged in first 48hPredicts trial-to-paid better than trial start alone
5Paid within the windowRare inside SKAN timing, but unambiguous when it happens

Source: Illustrative structure, not a standard. The right schema depends on which decision your team actually makes with the data.

Why fewer buckets beat more

Apple withholds the conversion value entirely when a campaign has not cleared its privacy threshold. Fine-grained schemas split volume across many buckets, and thin buckets are exactly the ones most likely to come back null. You end up with an elaborate schema returning nothing.

The same logic pushes towards fewer, larger campaigns on iOS. This runs directly against good Android practice, where granularity helps, and it is the most common structural mistake we find in cross-platform accounts.

Timing beats richness

The measurement window is short relative to a subscription funnel. A schema that encodes day-14 retention will mostly encode nothing, because the postback fires first. Encode signals that occur inside the window and correlate with what you care about, rather than the thing you care about arriving too late to be seen.

Early engagement is the usual answer: whatever a user does in the first twenty-four to forty-eight hours that predicts trial-to-paid in your own historical data. That is a question your existing cohorts can answer before you write a single line of the schema.

Once it is live

Treat the schema as versioned infrastructure. Changing it invalidates comparisons with everything measured before, so change it deliberately, note the date, and expect a gap in your trend lines. Teams that quietly adjust it three times a quarter have no usable iOS history at all.

Questions people actually ask

Do I still need SKAdNetwork if I have an MMP?
Yes. Your MMP does not replace SKAdNetwork on iOS – it receives, decodes and organises the postbacks. The schema still has to be designed, and the MMP defaults are a starting point rather than an answer.
How many conversion values should I use?
As few as answer your question. The instinct is to encode everything the API allows; the result is a schema where every bucket has too little volume to be reliable. Six well-chosen values beat sixty-four thin ones.
Revenue buckets or event sequence?
Events for subscription apps, revenue for one-off purchases. Subscriptions produce almost no revenue inside the measurement window, so a revenue schema on a subscription app mostly encodes zero. Encode trial start and early retention instead.
What changed with SKAN 4?
Multiple postback windows rather than one, coarse-grained values as a lower-signal fallback, and hierarchical source identifiers that give more campaign granularity when volume permits. In practice it means you can measure a little further out, but the crowd-anonymity thresholds still decide how much you actually see.
Why do postbacks arrive empty?
Almost always privacy thresholds. Apple withholds the conversion value when a campaign has not passed its anonymity threshold, so low-volume campaigns return nulls. Fewer, larger campaigns produce more usable data than many small ones – which is the opposite of good Android practice.

We audit these for free.

Send us your schema and your campaign structure and we will tell you what your current setup can and cannot answer, and what we would change.