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/Business
Business/2026-06-30Intermediate

Holding App Store Messages Until the Right Moment

Price-increase consent, billing-issue, and win-back messages from the App Store appear right at launch by default. Here is how to take control of StoreKit 2 Messages and defer them until a moment that does not interrupt onboarding or checkout, with working Swift.

StoreKit8In-App Purchase7Rork Max233Monetization37UX5

Premium Article

A price-increase consent sheet once slid up on the third onboarding screen for a brand-new user. As an indie developer running a few apps with in-app purchases on the App Store, I see onboarding completion drop noticeably whenever that interruption fires. We are asking someone to agree to a price before they have felt any value — so the drop makes sense.

App Store system messages (price-increase consent, billing issues, win-back offers) are displayed by StoreKit automatically when your app returns to the foreground. You cannot avoid showing them, but you fully control when. By subscribing to StoreKit 2's Message, you move the trigger into your own hands. This assumes the native Swift that Rork Max generates, but the same approach applies if you bridge something like react-native-iap.

The default behavior, and the one lever you hold

The key fact is that messages can be held. When you subscribe to the StoreKit.Message.messages async sequence, any message the system wants to present flows to your app instead. Once you are inside that for await loop, nothing displays until you call message.display(in:) yourself.

So "subscribing" effectively means "suppress auto-presentation and take over." A received message is retained until you display it; if you never do, it arrives again next launch. You will not lose messages, but never showing them hurts both compliance and revenue, so always design a path that eventually flushes everything.

Open exactly one subscription at launch

Keep the subscription to a single loop for the whole app. Running for await in several places risks processing the same message twice. Create one @MainActor coordinator and start the loop right at launch.

import StoreKit
import SwiftUI
 
@MainActor
final class StoreMessageCoordinator: ObservableObject {
    private(set) var pending: [Message] = []
    /// Whether it is OK to present right now (driven by screen state)
    var canPresent: Bool = false { didSet { flushIfPossible() } }
 
    private var listenTask: Task<Void, Never>?
 
    func start() {
        guard listenTask == nil else { return }
        listenTask = Task { [weak self] in
            for await message in Message.messages {
                // The system will not auto-present unless we call display()
                self?.enqueue(message)
            }
        }
    }
 
    private func enqueue(_ message: Message) {
        // Consent and billing tie directly to revenue/compliance — keep them at the front
        switch message.reason {
        case .priceIncreaseConsent, .billingIssue:
            pending.insert(message, at: 0)
        default:
            pending.append(message)
        }
        flushIfPossible()
    }
 
    private func flushIfPossible() {
        guard canPresent, let scene = Self.activeScene() else { return }
        let next = pending
        pending.removeAll()
        for message in next {
            do { try message.display(in: scene) }
            catch { pending.append(message) } // put it back if we cannot present
        }
    }
 
    private static func activeScene() -> UIWindowScene? {
        UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .first { $0.activationState == .foregroundActive }
    }
}

Flip canPresent based on screen state and you control timing completely. Note that display(in:) requires a UIWindowScene; calling it before a scene is frontmost fails, so only present when a foregroundActive scene is available.

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
Subscribe to StoreKit 2 Message.messages and choose exactly when to call message.display(in:) — with working Swift
A per-reason policy table for priceIncreaseConsent / billingIssue / generic deciding show-now vs. defer
A pending queue that holds messages during onboarding, checkout, and full-screen playback, then flushes on return to home
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.

or
Unlock all articles with Membership →
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 →

Related Articles

Business2026-07-02
Protecting Ad eCPM in Your Rork Max App: Designing ATT Pre-Permission Priming
For iOS apps built with Rork Max, ad revenue swings heavily on your ATT opt-in rate. Here is how to design a pre-permission priming screen, implement it in SwiftUI, measure the opt-in rate, and order AdMob init correctly.
Business2026-07-01
Before You Reach for Tap to Pay on iPhone in a Rork Max App — Eligibility, Choosing a Payment Provider, and a Realistic Design
Implementation notes for judging, from a business angle, whether you can put Tap to Pay — accepting in-person payments on an iPhone alone — into a Rork Max app. Covers the organization-account and entitlement requirements, why you need a payment provider (PSP), the split between ProximityReader and the PSP, and a realistic path for an indie developer, told candidly.
Business2026-06-14
Will Rork Max's $200 a Month Pay for Itself? Decide It With a Formula
When you are torn over Rork Max at $200 a month, here is a break-even formula, a script that runs a sensitivity analysis, and a way to judge the timing by three months of buildup rather than a single month—with notes from indie development.
📚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 →