RORK LABJP
BUILD — Rork Max runs real Macs in the cloud loaded with Xcode and the iOS SDK, writing SwiftUI, compiling, reading the errors and building again. That loop, not the code generation, is what lifts the outputNATIVE — What comes out is pure Swift and SwiftUI, not React Native. Reaching AR, Metal graphics and widgets that React Native cannot touch is the real gap between this and other buildersPLATFORMS — Coverage spans iPhone, iPad, Apple Watch, Apple TV and Vision Pro, plus iMessage. Worth a look if you want to start from a watch app or an extension rather than a phone screenCOMPANION — The Rork Companion app lets you check a generated build on a real iPhone without a paid Apple Developer account, lowering the bar for trying a first project end to endPRICING — Free to start, paid plans from $25 a month, and Rork Max on the $200 Max plan. Worth working out up front how many projects it takes to earn that backDEADLINE — From August 31, 2026, Google Play requires target API level 36 or higher for new apps and updates alike. Ten days out, and the targetSdkVersion of what you generate is yours to verifyBUILD — Rork Max runs real Macs in the cloud loaded with Xcode and the iOS SDK, writing SwiftUI, compiling, reading the errors and building again. That loop, not the code generation, is what lifts the outputNATIVE — What comes out is pure Swift and SwiftUI, not React Native. Reaching AR, Metal graphics and widgets that React Native cannot touch is the real gap between this and other buildersPLATFORMS — Coverage spans iPhone, iPad, Apple Watch, Apple TV and Vision Pro, plus iMessage. Worth a look if you want to start from a watch app or an extension rather than a phone screenCOMPANION — The Rork Companion app lets you check a generated build on a real iPhone without a paid Apple Developer account, lowering the bar for trying a first project end to endPRICING — Free to start, paid plans from $25 a month, and Rork Max on the $200 Max plan. Worth working out up front how many projects it takes to earn that backDEADLINE — From August 31, 2026, Google Play requires target API level 36 or higher for new apps and updates alike. Ten days out, and the targetSdkVersion of what you generate is yours to verify
Articles/App Dev
App Dev/2026-06-25Intermediate

The App Privacy Section That Grows the Moment You Add Ads and Subscriptions — Notes on What I Actually Checked

How I filled out App Store Connect's App Privacy section for a Rork (Expo) app with AdMob, RevenueCat, and Crashlytics — including the tracking-to-ATT chain, written up as field notes from running six apps.

App Store87Privacy8AdMob70RevenueCat29Expo175

The other day I tried to submit a new wallpaper app to App Store Connect. The build was fine, yet a red banner — "This app has no App Privacy information" — stopped me from going any further. The code was finished, but I was blocked at the very last step. When you ship apps as a solo developer, this declaration screen always makes you brace yourself a little.

The App Privacy section is where you, the developer, declare what kinds of data your app collects. The tricky part is that you are responsible not only for your own code, but also for whatever the third-party SDKs you embed — ads, billing, crash reporting — quietly collect in the background. For my first few apps I was bounced back here repeatedly, and ended up re-checking what each SDK gathers, one by one.

In this article I'll leave field notes on what I actually checked for a fairly standard setup: AdMob, RevenueCat, and Crashlytics. The exact answers shift with your configuration and SDK versions, so please verify against your own environment in the end.

App Privacy and the privacy manifest are two different things

These are easy to confuse, so let me separate them first. There are two similar-sounding things, and you need both.

NameWhat it isWhere you set it
Privacy manifest (PrivacyInfo.xcprivacy)A file embedded in the app binary. Declares collected data types and the reasons for using "Required Reason APIs" in a machine-readable formThe build (Expo config / bundled by each SDK)
App Privacy (nutrition label)The "data practices" list shown on your store product page. A developer self-declarationThe web form in App Store Connect

The former is a machine-facing declaration read by crawlers; the latter is a human-facing summary that users read on the product page. Even if your manifest is correct, you still have to fill in the web-side declaration yourself. Early on I assumed these two were the same thing, got my manifest in order, still couldn't submit, and spent a while not understanding why.

Adding ads, billing, and crash reporting expands what you must declare

For a wallpaper app that only displays images, the declaration stays minimal. But once you add three SDKs for monetization and operations, it tips firmly toward "data is collected." Here is roughly what I ended up checking for my setup.

SDK (purpose)Main data types collectedLinked to the userUsed for tracking
AdMob (ads)Identifiers (advertising ID / IDFA, etc.), Usage Data, DiagnosticsYes (for ad delivery)Yes (with personalized ads)
RevenueCat (billing)Purchases, Identifiers (user ID), Usage DataYesNo
Crashlytics (crash reporting)Diagnostics, Identifiers (installation ID)No (in most setups)No

The biggest fork in the road here is AdMob's "used for tracking." Once you enable personalized ads, you are using the advertising ID to track users, which puts you under "Used to Track You" in the declaration. In my setup, RevenueCat and Crashlytics collect data to operate my own service but do not track across third parties, so I leave them out of the tracking column.

Each SDK publishes a page summarizing exactly what data it collects. If you fill the form from guesswork without reading these, discrepancies surface later. Before every declaration, I reopen each vendor's latest data-collection document and only then fill in the form.

Checking "used for tracking" makes ATT mandatory

This is the chain that trips people up most at submission time. If you declare "identifiers used for tracking" in App Privacy but your app never shows the App Tracking Transparency (ATT) permission dialog, the review will bounce you — because the declaration and the implementation contradict each other.

With Expo, add expo-tracking-transparency and set the permission string.

{
  "expo": {
    "plugins": [
      [
        "expo-tracking-transparency",
        {
          "userTrackingPermission": "We use your activity to show ads that are relevant to you."
        }
      ]
    ]
  }
}

Then always request permission before loading personalized ads.

import { requestTrackingPermissionsAsync } from "expo-tracking-transparency";
 
const { status } = await requestTrackingPermissionsAsync();
// Only enable personalized ads when granted
const personalized = status === "granted";

If permission is denied, turn personalization off and fall back to non-personalized ads. "Declared it = implemented ATT = stop tracking when the user declines" — unless all three line up, a contradiction will eventually show somewhere. I once set the ATT string but forgot to actually call the dialog, noticed only after submitting, and had to swap it in a hurry.

How to decide the three axes (collected / linked / tracking)

App Privacy is, for each data type, a matter of deciding three things in order. When in doubt, asking yourself in this order keeps it tidy.

First, "is it collected at all?" Take inventory of what your own code collects plus what the embedded SDKs collect. The moment you add an ad SDK, identifiers and usage data fall onto the "collected" side.

Second, "is it linked to the user?" If you can identify an individual through an account or device ID, it is "linked." Purchase history is tied to the account, so I put RevenueCat-related data under "linked."

Third, "is it used to track across third parties?" This is the heaviest call, and it ties directly to ATT. Personalized ads qualify; plain operational analytics usually does not. Leaving this vague and "checking everything just in case" needlessly forces ATT and lowers your opt-in rate, so I find it more practical to narrow it down with a clear rationale.

Revisit the declaration whenever you update an SDK

The hard part of operations is that this isn't fill-once-and-forget. A major SDK update can quietly add new collected data types. Since I run six apps in parallel, I now always set aside time at the once-a-year SDK update to reconcile each vendor's data-collection document against my own declaration.

Lately I keep a single mapping table of my own — "data collected per SDK to App Privacy category" — and when I ship a new app I copy it and only fix the differences. That has cut mistakes far more than thinking it through from scratch every time.

Before you submit your next app, start by listing the SDKs you've embedded and opening each one's latest data-collection page. Those ten-odd minutes turn out to be the shortcut, rather than getting stuck at the declaration after the code is done. I hope this helps anyone stuck at the same spot.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

App Dev2026-07-16
Placing Native Ads in a Masonry Wallpaper Grid: Designing the Lifetime of an Ad Cell
One native ad in a masonry gallery pushed memory from 180 MB to 420 MB over twenty minutes of scrolling. Here is why cell recycling and ad object lifetime never line up, the pool-based implementation that fixed it, and how I picked the insertion interval from measured numbers.
App Dev2026-03-15
Production AI Companion Apps: Streaming, Voice & Monetization
Build a production-grade AI companion with streaming responses, voice I/O, mood tracking, offline support, and subscription monetization. App Store submission guidelines included.
Dev Tools2026-06-16
I Initialized Ads Before Restoring Purchases, and Paying Users Saw a Banner Flash — Cold-Start Ordering for Rork (Expo) Apps
Consent, ATT, ad SDK init, purchase restore, and remote config all try to run in the same few hundred milliseconds at launch. Get the order wrong and a paying user sees a banner flash, or measurement fires before consent in the EEA. Here is how I fold a Rork-generated Expo app's startup into a single orchestrator and kill the races by design.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →