●FUNDING — Rork raises a $15M seed led by Left Lane Capital●RORK MAX — Rork Max generates native Swift apps instead of React Native●PLATFORM — It targets iPhone, iPad, Watch, and Vision Pro, reaching Live Activities and Core ML●GROWTH — Traffic keeps climbing at 743K monthly visits and 85% growth●TEST — The Companion app lets you test on a real device without a paid Apple Developer account●STACK — Built on React Native and Expo for true native experiences, not web wrappers●FUNDING — Rork raises a $15M seed led by Left Lane Capital●RORK MAX — Rork Max generates native Swift apps instead of React Native●PLATFORM — It targets iPhone, iPad, Watch, and Vision Pro, reaching Live Activities and Core ML●GROWTH — Traffic keeps climbing at 743K monthly visits and 85% growth●TEST — The Companion app lets you test on a real device without a paid Apple Developer account●STACK — Built on React Native and Expo for true native experiences, not web wrappers
Adding App Store Promoted In-App Purchases to a Rork App
How to list purchases on your App Store product page and carry a buyer straight into your in-app purchase flow. Covers receiving deferred purchases that arrive before the app is launched or the user is signed in, with implementation for both react-native-iap and StoreKit 2.
Scroll down an app's App Store product page and you'll sometimes see an "In-App Purchases" list near the screenshots. Promoted In-App Purchases let you place your own purchase items there and send anyone who taps one directly into your in-app purchase flow.
As an indie developer running wallpaper and healing-themed apps, I had leaned entirely on the post-launch paywall for monetization. But the promoted slot on the product page is one of the few touchpoints you get before someone installs the app. The thing that tripped me up when designing it was simple: the buy button is pressed outside the app.
In other words, the intent to purchase happens while the app isn't running. Sometimes the app isn't even installed yet. How you receive that "purchase arriving from outside" is the heart of designing a promoted IAP.
What happens between the tap and the completed purchase
When a user taps a promoted purchase item on your App Store product page, the flow runs in this order.
If the app isn't installed, the App Store prompts to install it first
After install (or if it already exists), the app launches
StoreKit hands the app an event saying "this product is about to be purchased"
The app decides whether to continue the purchase or hold it
The crucial part is that step 4 belongs to your app. StoreKit doesn't force the purchase — it asks the app whether it's okay to proceed right now. When initialization isn't finished or the user hasn't signed in, you can hold here and resume once you're ready. If you don't build this hold-and-resume mechanism correctly, a purchase sheet can pop up the instant the app launches and confuse the user, or you can drop the event entirely and leave them with "I tapped but nothing happened."
Setting up App Store Connect
Before any code, you need to register the purchase as promotable.
Open the app in App Store Connect and select the individual item under In-App Purchases
Enable "Promote in the App Store"
Upload a 1024×1024 promotional image (this is what appears on the product page)
Display order can be overridden from the API, but the order in Connect is the initial default
You can register up to 20 promoted IAPs. In my experience, rather than surfacing all of them, narrowing to one or two clear items — your main subscription or an "unlock everything" — keeps the product page uncluttered and makes the effect easier to measure.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦You'll be able to catch a buyer who started a purchase from the App Store product page and carry them through to a completed purchase after launch
✦You'll understand, with working code, how to defer a purchase that arrives at first launch or signed-out by returning false from shouldAddStorePayment and resuming once ready
✦You'll learn the App Store Connect setup and the points that tend to get flagged in review when you build this as a revenue path
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Receiving the deferred purchase with react-native-iap
A standard Rork app is generated as React Native (Expo), so the practical approach is to bring a native module like react-native-iap into a development build. With promoted IAPs, the key is registering the purchase listener as early as possible during startup. If listener registration is late, you'll drop the event that arrives on a cold launch.
// promotedPurchase.ts — call this at the very start of app launchimport { initConnection, getPromotedProductIOS, requestPurchaseOnPromotedProductIOS, promotedProductListenerIOS,} from 'react-native-iap';// Let the readiness check be injected from outsidetype ReadyCheck = () => boolean;let pendingPromoted = false;export async function setupPromotedPurchase(isReady: ReadyCheck) { await initConnection(); // Fires when a promoted purchase arrives from the App Store promotedProductListenerIOS(async () => { pendingPromoted = true; await tryConsumePromoted(isReady); }); // Guard against an event that already arrived on cold launch const promoted = await getPromotedProductIOS(); if (promoted) { pendingPromoted = true; await tryConsumePromoted(isReady); }}// Run the purchase if ready; otherwise keep it held and returnexport async function tryConsumePromoted(isReady: ReadyCheck) { if (!pendingPromoted) return; if (!isReady()) return; // keep holding if login/init is incomplete pendingPromoted = false; await requestPurchaseOnPromotedProductIOS();}
The point is to make tryConsumePromoted callable from two places: once when the event arrives, and once "the moment you become ready." Calling the same function after login or after onboarding lets a held purchase resume naturally at that point.
// Resume a held purchase when login completesasync function onLoginSuccess() { await tryConsumePromoted(() => true);}
The "ready" definition that holds it all together
This is the core of the design. How you define "ready" shapes the user's experience. What I settled on in practice was proceeding only when all three of these conditions are met.
Condition
Behavior if unmet
StoreKit initialization (initConnection) complete
The purchase request is ignored, so always wait
User account resolved (signed in or guest confirmed)
Hold the purchase and resume after login
A screen that reflects the result can render
Hold and resume after navigation is ready
Keeping the event in pendingPromoted until all three are met was the most reliable way to avoid drops. That said, holding state only in memory means it's gone if the app crashes. Calling getPromotedProductIOS once at startup as a second line of defense covers the cold-launch drop.
With Rork Max (native Swift), receive it via StoreKit 2
Because Rork Max generates native Swift apps, you can receive these through StoreKit 2's PurchaseIntent — a more direct async sequence that waits for purchase intents.
import StoreKit// Start the observation task right after launchfunc observePromotedPurchases() { Task.detached { for await intent in PurchaseIntent.intents { await handlePromoted(intent.product) } }}func handlePromoted(_ product: Product) async { // Wait until ready (e.g., login resolved) await AppReadiness.shared.waitUntilReady() do { let result = try await product.purchase() await fulfill(result) // grant entitlement } catch { // Don't swallow network errors or cancellations — record them Telemetry.log("promoted_purchase_failed", error) }}
The StoreKit 1 "return false to defer" behavior of shouldAddStorePayment is replaced in StoreKit 2 by a straightforward wait: receive the intent, then wait until ready. Rork Max generates the scaffolding of this observation task, but what goes inside waitUntilReady and how you record failures is the part you finish yourself.
Points that tend to get flagged in review and measurement
Promoted IAPs are inspected during App Store review too. From what I've hit or heard, these are the easy places to stumble.
Avoid inserting your own paywall after the tap. Apple expects the buyer to purchase the item they chose on the product page. Wedging a custom upsell screen in between can mismatch the intended product and become a rejection reason
Don't fully block purchases for signed-out users. During review that can read as "can't purchase." Letting a guest buy and binding it to an account later is the safer escape hatch
Measurement rides the same path as a normal in-app purchase, but tagging whether the entry point was the promoted slot lets you evaluate the product-page conversion later. I attach source: "promoted" to the purchase-complete event and view it separately from regular paywall purchases
As a revenue path, the promoted IAP occupies a unique spot: a purchase touchpoint before the app is opened. You can't pitch as freely as an in-app paywall, but its value is carrying the buying intent of the moment someone lands on your product page from search all the way through to after install. As long as you build the hold-and-resume design carefully, it becomes a quiet revenue route with few drops. I hope it helps with your own implementation.
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.