In mid-June I read that Rork Max, powered by Claude Code and Opus 4.6, had started generating native Swift rather than React Native. The same week, Rork closed a $15M round. Watching both at once, I had the quiet sense that AI-assisted app building had finally stepped past the "lowest common denominator of cross-platform" and into the deeper parts of native.
If I'm honest, my first reaction was half excitement, half caution. The excitement is easy to understand, so I'll set it aside. The caution comes from years of maintaining iOS apps. With native Swift, the moment of generation is not the finish line. It's where the long part begins — review, updates, billing, measurement, everything that lives after release. So I wanted to put into my own words which layers I can cheerfully delegate to AI, and which ones I should deliberately keep in my own hands.
Why "native Swift generation" differs in kind from React Native generation
Generating React Native (Expo) was, in a sense, easy to live with. What came out was JavaScript/TypeScript, and as long as it stayed on the Expo rails, its behavior was fairly predictable. Native Swift widens the surface dramatically: Live Activities, Dynamic Island, HealthKit, Core ML, NFC, App Clips. What Rork Max says it unlocks is exactly the territory React Native struggled to reach.
A wider reach also means a wider area to investigate when something breaks. Across the wallpaper and relaxation apps I maintain as an indie developer, widgets and notifications were the parts that looked "obviously working" yet quietly fell apart across OS versions and screen densities. Having AI write all of it at once is genuinely helpful — but I want to avoid a state where I can't explain it once it breaks. That tension is what makes me split the work into layers.
Layer 1: Hand over the UI skeleton, gladly
Start with what I'm happy to delegate. The screen scaffold — the SwiftUI view hierarchy, the rough layout — is fastest when AI drafts it. This is a region where "there's no single correct answer," but also "getting it slightly wrong does little damage."
struct WallpaperGridView: View {
let items: [Wallpaper]
private let columns = [GridItem(.adaptive(minimum: 110), spacing: 8)]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 8) {
ForEach(items) { item in
WallpaperCell(wallpaper: item)
.aspectRatio(9/16, contentMode: .fit)
}
}
.padding(8)
}
}
}Code like this is easy to re-read and adjust even if I take it as-is. When reviewing, I check only two things: is it using LazyVGrid (naively stacking a VStack for a long list makes scrolling heavy), and does adaptive follow the device width. The fine padding and animation I tune by hand while running it. Skeleton delegated, texture kept — that's the split.
Layer 2: Keep the "boundaries" — permissions, billing, measurement — in your own hands
This is the part I most wanted to write. The boundary where the app meets the outside world — billing state, ad gating, analytics, and the OS permission layer — is where I refuse to hand things over wholesale. I always insert one slice of my own judgment. The reason is simple: when this drifts, it breaks "quietly but fatally." A bug that hides the free article is something you notice; a bug that shows ads to a paying user can go unnoticed for a long time.
For example, the decision of "may we hide ads" must collapse several inputs (a lifetime purchase, a temporary reward-based ad-free window, a subscription) into a single place. If the generated code writes separate if statements on each screen, I always pull them back into this shape.
struct AdFreeState {
let hasLifetimePurchase: Bool
let hasActiveSubscription: Bool
let rewardGrantedUntil: Date?
// Only this one place ever decides whether to show ads.
func isAdFree(now: Date = .now) -> Bool {
if hasLifetimePurchase || hasActiveSubscription { return true }
if let until = rewardGrantedUntil, until > now { return true }
return false
}
}Once the decision is sealed inside a single function, adding a new monetization path means touching only this spot. The more screens I let AI mass-produce, the more valuable this "single source of truth" becomes. I've laid out the billing side in more depth in a separate piece on StoreKit 2 subscription architecture, if you want to go deeper.
The same holds for on-device Core ML inference. Rork Max advertises Core ML support, but "where the inference runs and how the result is handled" is a design decision in itself.
func classify(_ image: CGImage) throws -> String? {
let model = try WallpaperTagger(configuration: .init())
let input = try WallpaperTaggerInput(imageWith: image)
let output = try model.prediction(input: input)
// Below the threshold, return "unknown" — don't let it assert blindly.
guard let top = output.classLabelProbs.max(by: { $0.value < $1.value }),
top.value >= 0.6 else { return nil }
return top.key
}A small judgment like this 0.6 threshold almost never appears in AI's first draft. Yet that very habit of "staying silent when unsure" is what reduces complaints after release. I wrote about how far you can take native Swift without Xcode in shipping native Swift without a Mac; however far the tooling advances, this boundary judgment is the part people should keep holding.
Layer 3: Get generated code into a "maintainable" state
The third is the work of leaving AI's code in a state my future self can read. This is closer to a habit than a layer, but the more native Swift generation becomes routine, the more it pays off.
The minimum I do is align naming and the lines of responsibility right after generation. AI tends to name things slightly differently per context, so if Manager, Store, and Service are mixed together, I pull them toward my project's vocabulary. Then, for the boundaries from Layer 2 (billing, measurement, permissions), I place at least one small test each. I'm not chasing coverage — just a minimal sentry that screams the moment it breaks. Balancing generation with maintenance is continuous with my notes on exit paths from Expo to native.
A separate question: do you bet on the tool's longevity?
Finally, a word on the $15M raise. Funding news doesn't, by itself, guarantee quality. But when I choose a tool as an indie developer, the one thing I care about is: "if this tool disappears tomorrow, do my assets stay in my hands?" If the generated native Swift source lives in my own repository, opens in Xcode, and I hold the billing and measurement boundaries myself — then I don't have to bet everything on the tool's lifespan. I welcome the funding while keeping a stance that doesn't over-depend on it. That's my honest position right now.
I've written about the judgment axis for that bet from another angle in how to think about Rork Max's pricing.
If you're about to try Rork Max, generate just one small screen first, then pull its billing check back into something like the AdFreeState above by hand. In that single pass, the feel of "what to delegate, what to keep" becomes surprisingly clear. I'd be glad to keep searching for the right line together, hands on the keyboard.