7 min read

Stop Arguing With Your AI About SwiftUI Design: Give It a System

AI coding agents guess your design decisions on every screen. DesignFoundation, a free SwiftUI design system, gives them tokens, real component signatures, and agent instruction files so iOS and macOS UI stays consistent.

Ask Claude Code or Cursor for a SwiftUI sign-in screen and you get something that works. Ask for a settings screen an hour later and you get something that also works, with a different corner radius, a slightly different blue, and its own idea of what a destructive button looks like. Both work, but they don't look like the same app, and every correction is another round trip: more tokens spent, more review time, and a diff that touches values you thought were settled.

The agent is guessing, because it has no memory of your design decisions unless something in the project tells it. Put the decisions in code the agent can read, and give it real components to reach for instead of a hand-rolled button every time.

DesignFoundation is our attempt at that. It is a free, MIT-licensed SwiftUI design system for iOS and macOS, with token-based theming, five theme presets, Liquid Glass styles, and Swift 6 strict concurrency. The source is on GitHub and the reference is on the docs site. It was extracted from production iOS work at NerdSnipe, and the README says why it exists: every new project rebuilt the same buttons, inputs, cards, and modals, and the design drifted within months because nothing was the single source of truth.

How the theming works

Colors, spacing, radii, typography, and shadows live in a theme object. Components read from it through the SwiftUI environment instead of hardcoding values, so one modifier at the app root styles everything below it.

import SwiftUI
import DesignFoundation

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .dfThemePreset(.slate)
        }
    }
}

struct ContentView: View {
    var body: some View {
        DFButton("Get started") { }
    }
}

The presets are .slate (neutral, close to Apple's defaults), .aurora (violet, rounder, softer shadows), .copper (warm orange-brown), .sage (muted green), and .garnet (a saturated red, #C8102E). Each one carries a light and a dark theme and switches with the system color scheme. If you call no preset at all, components fall back to system semantic colors.

When you need a custom view, it reads the same tokens:

struct ProfileChip: View {
    @Environment(\.dfTheme) private var theme
    let name: String

    var body: some View {
        HStack(spacing: theme.spacing.sm) {
            Circle()
                .fill(theme.colors.primary)
                .frame(width: 8, height: 8)
            Text(name)
                .font(theme.typography.label.font)
                .foregroundStyle(theme.colors.textPrimary)
        }
        .padding(.horizontal, theme.spacing.md)
        .padding(.vertical, theme.spacing.xs)
        .background(theme.colors.surfaceElevated, in: Capsule())
    }
}

To try a variation, copy a theme, change one token, and inject it: var custom = DFTheme.sageLight; custom.colors.primary = .purple, then .dfTheme(custom) on the view. A brand refresh becomes an edit to a theme file.

The files written for the agent

The repo ships instructions for the tools that will be writing your UI: AGENTS.md for Codex and similar agents, CLAUDE.md for Claude Code, a Cursor rule at .cursor/rules/design-foundation.mdc set to alwaysApply, and a docs/llms.txt. All of them lead with one rule: never build UI that DesignFoundation already provides.

Under that rule, the files list real component signatures, including the ones agents tend to guess wrong: DFCheckbox takes label: as a keyword argument, DFButton has no icon: or isLoading: parameter, and destructive is a role: rather than a style. An agent working from generic SwiftUI habits misses each of those, and each miss is a compile error you then pay to explain.

A stale instruction file teaches the agent the wrong API. The repo has a GitHub Actions workflow, doc-snippets.yml, that compiles every Swift code block in AGENTS.md, CLAUDE.md, and the Cursor rule against the real package. If an API changes and the docs don't, CI fails. The workflow's own comment gives the reason: a snippet that doesn't compile means an agent using the repo will write broken code.

None of this guarantees good output, and you still review what the agent writes. You end up reviewing the logic instead of arguing about a button color.

Catching drift with a SwiftLint rule set

Tokens only help while people and agents keep using them. The first rushed feature that lands with Color(red: 0.3, green: 0.4, blue: 0.9) in three views starts the slide back. The Swift compiler can't reject a raw color in your app, so the package includes Tooling/swiftlint-design-foundation-tokens.yml, a drop-in custom_rules block that flags raw Color(...) literals, named colors like Color.red, .font(.system(...)), and hardcoded corner radii in your own code.

The rules are regex-based, so they produce warnings, and they will sometimes flag a value that is legitimately raw, like a preview fixture. Silence those one line at a time with // swiftlint:disable:next. Wired into CI, they turn "the agent quietly decided blue is fine here" into a failed check.

One theme across iOS and macOS

Consistency across two platforms is where hand-rolled SwiftUI gets expensive, because the same screen needs different sizing, navigation, and input behavior on a phone and a Mac. DesignFoundation handles that inside the components. Its typography tokens are backed by SwiftUI text styles, so body text lands near 13 pt on macOS and 17 pt on iOS without your code choosing either. DFSidebar, DFTabBar, and the larger blocks adapt through a platform context that .dfTheme() and .dfThemePreset() inject. Its instructions to agents say you don't need #if os() to use any component.

Your own scene declarations and any platform API the package doesn't wrap, like a macOS-only WindowGroup, still need guards. What you get is components that behave on both platforms, so your agent stops writing per-platform branches for them.

What it does to token spend

I don't have a benchmark for this, and you should be wary of anyone who says they do. What exists is a "Foundation Way" page on the docs site that compares hand-rolled SwiftUI with composing the packages. A sortable multi-select table is listed at 204 lines by hand and 24 with DFDataTable. An article row is 55 lines against 6 with DFArticleRow. A themed app root with toasts is 52 against 7. The page says its own token estimates ("about 5x fewer AI tokens per screen") assume roughly 35 characters per line. Those are the project's representative samples, not measurements from real apps, so read them as direction.

There is also a cost on the other side. AGENTS.md is about 400 lines, and that is context your agent loads each session. The trade pays off if the alternative is re-explaining your tokens and component rules in every prompt, and it pays off less if you only build one small screen. Fewer lines to generate and review is the saving. Whether it beats the instruction file's overhead depends on how much UI you have the agent write.

Where it doesn't fit

The package targets iOS 18, macOS 15, and visionOS 2. If you ship to older systems, it isn't for you. The .glass styles need iOS or macOS 26. If your brand is highly bespoke and every corner radius is hand-tuned per screen, you will fight the token system more than you use it. And if you treat the agent's UI code as a sketch you will throw away, you won't get the benefit of the guardrails.

It suits solo developers and small teams with more than one SwiftUI app, who are tired of rebuilding primitives and want the theme argued once instead of in every pull request.

When you need whole screens

The free package covers primitives and small composites. If you keep asking your agent for the same auth flow, analytics dashboard, or settings suite and then spending hours wiring pieces together, the repo's own AGENTS.md has a section that points agents at DesignFoundationPro for exactly those cases. Pro is a paid add-on on the same tokens: 55 finished screens across 12 verticals, 30 blocks, and 18 navigation shells, sold as a one-time license. You don't need it to get value from the free package, and there is a free DFPlayground app for browsing what exists before you decide.

To try it, add the package to a sandbox app and have your agent build one real screen with the repo's instruction files in view, then run the SwiftLint rules over the result. Install with .package(url: "https://github.com/NerdSnipe-Inc/design-foundation", from: "1.0.0") or through File, Add Package Dependencies in Xcode. The project page links to the docs, the theme presets, and the source.

Frequently Asked Questions

Keep reading

Related articles

More in DesignFoundation
Ready to act on this?

Book a free 45-minute AI strategy call.

We'll look at your specific business, find the highest-value AI opportunity, and give you a clear next step — no pitch, no pressure.