●RORKMAX — Rork Max builds native Swift apps instead of React Native, targeting iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessage●NATIVE — Rork Max unlocks capabilities React Native can't reach: AR/LiDAR, Metal 3D, widgets, Dynamic Island, Live Activities, HealthKit, HomeKit, NFC, and Core ML●PUBLISH — Two-click App Store publishing cuts the steps between generating an app and shipping it●SIM — A browser-based streaming iOS simulator lets you test in a real Apple environment without Xcode or a Mac●STANDARD — Standard Rork turns a plain-English description into working React Native (Expo) code●PRICING — It's free to start, paid plans begin at $25/month, and Rork Max is $200/month●RORKMAX — Rork Max builds native Swift apps instead of React Native, targeting iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessage●NATIVE — Rork Max unlocks capabilities React Native can't reach: AR/LiDAR, Metal 3D, widgets, Dynamic Island, Live Activities, HealthKit, HomeKit, NFC, and Core ML●PUBLISH — Two-click App Store publishing cuts the steps between generating an app and shipping it●SIM — A browser-based streaming iOS simulator lets you test in a real Apple environment without Xcode or a Mac●STANDARD — Standard Rork turns a plain-English description into working React Native (Expo) code●PRICING — It's free to start, paid plans begin at $25/month, and Rork Max is $200/month
When Rork Max's Two-Click Submit Stalls, Tell Which Layer Broke
Rork Max's one-click install and two-click submit fold the complexity of iOS shipping into four hidden layers. When the abstraction leaks and your submission stalls, this map helps you tell which layer failed and fix it yourself.
The other day I built an app that compiled cleanly in the cloud and hit Rork Max's submit button. The screen said "sent," yet a few minutes later a rejection email arrived from App Store Connect. Rork never showed that error. The email carried a single token: ITMS-90683.
What had happened was that the word "two clicks" had folded away several steps, and the failure came from the deepest of them. The number of buttons and the number of steps behind them do not match. When you ship apps on your own, you will always hit a moment where that gap is invisible and everything stops. This article draws a map across that gap.
"One Click" and "Two Clicks" Fold Four Layers
Rork Max's promise, "one click to install on device, two clicks to submit to the App Store," takes the work you used to do by hand across Xcode and the Apple Developer dashboard and pushes it behind a button. That folded work splits roughly into four layers. The buttons are few not because the steps are few, but because the steps stay invisible as long as everything works.
Layer
What it handles behind the scenes
Typical symptom when it leaks
Layer 1 Signing
Matching the distribution certificate to a provisioning profile
Won't install on device, untrusted developer
Layer 2 Info.plist
Declaring bundle ID, version, purpose strings
Tokens like ITMS-90683, ITMS-91053
Layer 3 Upload
Packaging the archive into an IPA and sending it to App Store Connect
Stuck in processing, build never appears
Layer 4 Pre-review check
Validating export compliance and privacy declarations
An automated rejection email right after submit
When an error flashes red, just being able to name which row you are in changes how fast you recover. Below, layer by layer, I walk through what each one handles and how to read it when it leaks.
Layer 1: Signing Guarantees "Who, and on Which Device"
Every iOS app is signed with a distribution certificate, and a provisioning profile ties together the promise that "this bundle ID, with these entitlements, may run on this set of devices." Rork Max's one-click device install quietly issues and links that certificate and profile for you.
Leaks here come from certificate or profile expiry, or from hitting the device registration limit. If you install on device with free provisioning, you can run into an "untrusted developer" message. That is not a submission failure; it is a wall on the device-testing side. The ceiling and timing of on-device testing are covered in the free device-testing limit with Companion and when to pay for a developer account.
The thing to remember about Layer 1 is that signing problems happen before anything reaches App Store Connect. So if a post-submission email rejected you, it is not Layer 1. That is the first fork in your diagnosis.
✦
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
✦When a two-click submit dies with a red error, you can pinpoint whether signing, Info.plist, upload, or the pre-review check is where it broke
✦You will have plutil and App Store Connect API checks you can run locally before submitting, cutting the round-trips of rejection
✦You will be able to decide, as an indie developer budgeting your time, where to fix things yourself and where to trust Rork
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.
Layer 2: Info.plist Is the Language of Your Submission
Layer 2 is the document in which your app declares itself to Apple. Bundle ID, version and build number, purpose strings for the permissions it needs, the encryption declaration. All of these live in Info.plist, a set of keys and values, and the review system reads that document mechanically.
The ITMS-90683 I ran into at the top of this article is the classic case for this layer. Your app starts using something privacy-sensitive, like the camera or photo library, but the string explaining why (such as NSCameraUsageDescription) is missing from the plist. It happens when the generated app begins using a capability and the matching description never followed along.
<!-- Part of Info.plist. Each privacy-touching capability needs a purpose string --><key>NSCameraUsageDescription</key><string>Used to take your profile photo</string><key>NSPhotoLibraryUsageDescription</key><string>Used to load images you have already saved</string><!-- Encryption declaration. Often false if you only use standard HTTPS --><key>ITSAppUsesNonExemptEncryption</key><false/>
The nice thing about this layer is that you can lint it before you ever submit. If you have macOS, plutil tells you in one shot whether the plist is broken.
# Check that the plist isn't broken at the XML syntax levelplutil -lint Info.plist# Expected output: Info.plist: OK# Confirm a specific key is presentplutil -extract NSCameraUsageDescription raw Info.plist
If the syntax is broken it dies at the Layer 3 upload; if a key is missing it dies at Layer 4. The same plist can stall at different layers. The specific fix for ITMS-90683 lives in fixing Info.plist when a purpose string is missing, and the privacy-manifest-driven ITMS-91053 is in the pre-submission audit for privacy manifests. This article stays focused not on the individual fixes but on the skill of reading which layer a token came from.
Layer 3: Archiving and Upload Are Hitting an API
In Layer 3, the signed build is packaged into a distributable box called an .ipa and transferred to App Store Connect. In the Xcode world this is what Transporter or xcrun altool handled, and Rork Max's cloud compile runs it server-side. The reason you reach submission without owning a Mac is that this transfer runs in the cloud rather than locally. The role of cloud compilation is laid out in how Mac-free native builds work.
This layer's symptom shows up less as a red error and more as silent stagnation. The upload succeeded, yet the build never appears in TestFlight on App Store Connect, or it sits in "processing" for hours. That is separate from a review rejection; it is stuck in Apple's processing queue or in build metadata validation.
What you can check on your side is whether the build actually landed. With an App Store Connect API token, you can query mechanically whether the build you sent shows up in the list.
# Fetch the app's build list via App Store Connect API (JWT generated ahead of time)# Purpose: confirm the build you thought you sent actually reached Applecurl -s -H "Authorization: Bearer ${ASC_JWT}" \ "https://api.appstoreconnect.apple.com/v1/builds?filter[app]=${APP_ID}&limit=5" \ | python3 -c "import sys,json; d=json.load(sys.stdin); [print(b['attributes']['version'], b['attributes']['processingState']) for b in d['data']]"# Example output: 42 PROCESSING / 41 VALID
Once a build flips from PROCESSING to VALID, you have cleared Layer 3. If it never appears here, it has not reached Apple yet, so this tells you whether to wait or resend. Misreading stagnation as an "error" and resubmitting repeatedly will jam you further on duplicate build numbers.
Layer 4: The Pre-Review Check Runs Automatically Right After Submit
Once the build is VALID and you submit, an automated check runs before any reviewer looks. That is where my opening email came from. Export compliance declaration, privacy manifest, required purpose strings. If these are not in place, a machine rejects you before a human ever sees the app.
The one I trip on most in indie development is a missing export compliance declaration. Even when you use nothing but standard HTTPS, if you do not explicitly declare whether you use encryption (ITSAppUsesNonExemptEncryption), you get asked at every submission. Write it into the plist once and the recurring question disappears. The reasoning behind this declaration is written up in a permanent fix for export compliance.
What matters at Layer 4 is that a rejection here is about your app's declaration, not its contents. Fixing code will not solve it. This is the layer where you fix the plist and App Store Connect settings. Before you rush to change the internals, remember that distinction.
Three Checks to Run Locally Before You Submit
Each rejection round-trip costs anywhere from a few minutes to, counting Apple's processing wait, tens of minutes. In indie development, cutting those round-trips translates directly into protecting your building time. Before I press submit, I run these three locally.
Check
How
Layer it prevents
The plist syntax isn't broken
plutil -lint Info.plist
Layer 3 upload stagnation
Purpose strings exist for the features you use
Check privacy keys with plutil -extract
Layer 2 and Layer 4 rejections
The encryption declaration is present
Confirm ITSAppUsesNonExemptEncryption exists
Layer 4 export compliance
All three complete on your machine, before you send a build to Apple. A three-minute check has erased tens of minutes of round-trips for me more than once. The full checklist across the submission flow is gathered in the pre-submission checklist, worth a glance before your first submission.
Where to Fix It Yourself, and Where to Trust Rork
I have separated the layers, but the goal is not to manage all of them yourself. It is the opposite. Rork Max's value is that, as long as things work, you never have to think about these four layers. Only when the abstraction leaks do you need to name the layer and reach in. That is enough.
My own line is this. Layers 2 and 4, the rejections tied to the plist and the declaration, I fix myself, because they only add a line of text and never touch code. Layer 1 signing and Layer 3 cloud processing I leave to Rork's machinery. Trying to touch those by hand means throwing away the very advantage of the abstraction.
The person using the tool knows just a little of its insides. You do not need to know all of it; enough to know which door to open when you get stuck. Continuing to build apps on my own, I have found that this "right distance" is what protects my time the most.
Wrapping Up
The next time your submission stops on a red error, before you open your code, try to name one thing: which of the four layers the error came from. Signing means before submission, a token-bearing rejection means the plist, silent stagnation means the upload, an email right after submit means the declaration. That one sentence reliably cuts your round-trips.
I am still looking things up myself every time I see a new token. If we keep our hands moving together, I would be glad to make the map of where things get stuck a little finer, one stall at a time.
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.