◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 27 days left. If your app calls it, check the migration target●SDK 58 — Moves to React Native 0.88. R8 is now on by default in Android release builds, and iOS prebuild generates a SceneDelegate●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 27 days left. If your app calls it, check the migration target●SDK 58 — Moves to React Native 0.88. R8 is now on by default in Android release builds, and iOS prebuild generates a SceneDelegate●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-06-16Advanced

Adding SwiftData to a SwiftUI App Generated by Rork Max

Rork Max can produce polished SwiftUI screens, but persistence often stops at @State. Here is how I layer SwiftData onto generated code: model design, wiring the container to views, and a schema migration pattern that survives shipping.

Rork Max235SwiftDataSwiftUI66PersistenceMigration3

✦ Premium Article

One evening I was looking at the SwiftUI code Rork Max gave me after I asked it to "build a habit tracker," and my hand froze on the trackpad. The screens were more thoughtful than I expected — the list, the checkboxes, all neatly laid out. But every time I relaunched the app, every habit I had entered was gone.

The reason was obvious the moment I opened the source. The data lived only in @State private var habits: [Habit] = [], never written to disk at all. Rork Max is excellent at producing a screen that looks like it works as fast as possible, but it does not design the persistence layer for you. That was the lesson of the night.

In this article I want to record the full path of taking that generated code and growing it into an app that survives a relaunch, with the actual code I wrote. I am assuming iOS 17 or later and that you can open the SwiftUI Rork Max emits in Xcode or a cloud build.

Why staying on @State eventually breaks

Freshly generated code usually looks like this.

struct Habit: Identifiable {
    let id = UUID()
    var name: String
    var doneToday: Bool
}
 
struct ContentView: View {
    @State private var habits: [Habit] = [
        Habit(name: "Morning walk", doneToday: false),
        Habit(name: "Reading", doneToday: false)
    ]
 
    var body: some View {
        List($habits) { $habit in
            Toggle(habit.name, isOn: $habit.doneToday)
        }
    }
}

As a screen, this is finished. But @State is in-memory state tied to the lifetime of the view, so it disappears when the process ends. Good enough for a demo, not viable for an app people open every day.

Many of us reach for serializing a Codable into UserDefaults as JSON next. I did exactly that in my early apps. The trouble is that once items grow and you need relationships or query conditions, reading and writing the whole JSON blob gets painful fast. SwiftData is designed for data that grows, so as an indie developer I have found that spending a little effort up front to move onto it pays off later.

Step 1: Replace the struct with @Model

The first move is turning the struct into an @Model class. The catch is that it becomes a final class rather than a struct — miss that and it will not compile.

import SwiftData
 
@Model
final class Habit {
    var name: String
    var doneToday: Bool
    var createdAt: Date
 
    init(name: String, doneToday: Bool = false, createdAt: Date = .now) {
        self.name = name
        self.doneToday = doneToday
        self.createdAt = createdAt
    }
}

Once @Model is applied, SwiftData tracks property changes automatically and treats the object as something to persist. You do not need to hold an id by hand; SwiftData manages an internal persistent identifier.

There is a judgment call here. If the generated code still has let id = UUID(), you can drop it as long as your views and animations do not depend on it. But if a ForEach references it via id:, I prefer to keep an explicit UUID property as a stable identifier. Rork Max output tends to be ambiguous here, so it is safest to check how ForEach is used before deciding.

✦

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
✦A step-by-step path from @State-only generated code to SwiftData @Model storage
✦VersionedSchema and MigrationPlan code that lets your schema grow without breaking the app
✦A ModelContainer fallback design that keeps the app launching even when migration fails
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

⬡ Dev Tools2026-07-18
The Generated Screen That Quietly Jams at AX5 and in German — Putting Layout Resilience Checks on Rork Max SwiftUI
A record of running every Rork Max generated SwiftUI screen through pseudolocalization and the largest text size to find exactly where it jams. Covers when to reach for ViewThatFits, ScaledMetric and layoutPriority, plus the snapshot checks that catch regressions every time you regenerate.
⬡ Dev Tools2026-05-17
Testing Rork Max SwiftUI Features on a Real Wallpaper App — What Worked, What Needed Fixes
I benchmarked Rork Max's SwiftUI generation against the production code of a wallpaper app I actually run. Here is what shipped as-is, what needed fixing, and what I ended up writing by hand — with working code and on-device measurements.
⬡ Dev Tools2026-05-12
Building a Shared UI Component Library with Swift Package Manager in Rork Max
A practical guide to building a shared SwiftUI component library with Swift Package Manager for indie developers running multiple apps — eliminate duplicate code across wallpaper, healing, and similar app families.