โ† All articles

AGENTS.md for iOS: Keeping Claude Code and Cursor Consistent in SwiftUI

Use AGENTS.md to keep Claude Code, Cursor, and Codex writing consistent SwiftUI in your iOS project. What to put in it, how each reads it, and a starter.

AGENTS.md for iOS: Keeping Claude Code and Cursor Consistent in SwiftUI

AGENTS.md is a plain markdown file at the root of your repository that AI coding agents read before they write code, so they follow your project's conventions instead of their own generic defaults. For a SwiftUI app that is the difference between an agent reaching for @Observable and your networking client, or reaching for ObservableObject and a raw URLSession because that is what most of its training data looked like. One file, read by Claude Code, Cursor, and Codex alike, is the cheapest way to keep AI-written SwiftUI consistent with the rest of your codebase.

This guide covers why agents drift from your conventions, what AGENTS.md is and what belongs in an iOS one, how each of the major tools discovers it, how to reinforce it with per-tool rules and file headers, and a starter file you can paste in today.

On this page

Why AI agents write inconsistent SwiftUI

An AI agent writes the average of what it has seen, and SwiftUI has changed a lot since 2019. Its training data is full of ObservableObject and @Published from the early years, @StateObject from the middle years, and the @Observable macro from iOS 17 onward, all mixed together. Ask three times and you can get three different state patterns, none of them wrong in isolation, all of them inconsistent with each other.

The same drift shows up everywhere the framework offers more than one way to do a thing: networking written with a bare URLSession in one view and a shared client in another, colors and strings sometimes typed and sometimes hardcoded, async work started in .onAppear here and .task there. The agent does not know which your project prefers, so it guesses, and the guesses do not agree.

AGENTS.md fixes this by writing the preferences down once, in the place the agent already looks. It does not make the agent smarter; it makes your conventions the default instead of the coin toss.

What AGENTS.md is

AGENTS.md is an emerging, tool-agnostic convention: a markdown file at the repository root that describes how agents should work in this codebase. There is no schema to learn. It is prose and lists, written for a capable developer who just joined the project and needs to know the house rules before touching anything.

Its value is that it is not tied to one vendor. The same file is picked up by multiple agents, so you maintain one source of truth instead of a separate rules file per tool. You still add small tool-specific shims (covered below), but they point back at AGENTS.md rather than duplicating it.

Keep it short and specific. An AGENTS.md that runs to ten pages of philosophy gets skimmed the same way a human would skim it. The useful ones are a page or two of concrete, checkable rules.

What to put in an iOS AGENTS.md

Cover the decisions your project has already made that an agent would otherwise re-decide at random. For a SwiftUI app that usually means these areas.

Architecture. State the pattern in one line so the agent places new code correctly. For example: views are structs, view models are @Observable classes, one feature per folder.

State management. This is the highest-value rule because it is where agents drift most. Name the current pattern and explicitly rule out the old ones:

## State
- Use the `@Observable` macro (iOS 17+) for view models, `@State` to own them.
- Do NOT use `ObservableObject`, `@Published`, or `@StateObject`. They are the old pattern.

Networking. If you have a client, make it mandatory so the agent stops hand-rolling requests:

## Networking
- All requests go through `APIClient.shared.send(_:)`. Endpoints are `enum` cases.
- Never call `URLSession` directly in a view or view model.

Assets and strings. If you generate typed constants (with SwiftGen or similar), an agent will happily ignore them and type string literals unless told not to:

## Assets and strings
- Images and colors: `Asset.Images.icon.image`, never string literals.
- User-facing text: `L10n.Onboarding.title`, never hardcoded strings.

Subscriptions and other wrapped systems. If a singleton wraps a third-party SDK, point the agent at the wrapper, not the SDK:

## Subscriptions
- Check entitlements with `SubscriptionManager.shared.hasEntitlement("pro")`.
- Never read `Purchases.shared` or `purchases.products` directly.

Lifecycle and testing. A couple of one-liners close the remaining common gaps: load async work in .task { } rather than .onAppear { Task { } } so it cancels correctly, and use the Swift Testing framework (@Test, #expect) rather than XCTest for new tests.

The pattern across all of these is the same: name the one blessed way, and name the tempting wrong ways to rule them out. Agents follow a "do X, not Y" rule far more reliably than a vague "prefer modern patterns."

Wiring it to each tool

Most agents read AGENTS.md directly, but each has its own primary entry point, and a two-line shim makes sure nothing is missed.

Claude Code reads a CLAUDE.md at the repository root. Rather than duplicate your rules, make CLAUDE.md a thin pointer so there is one source of truth:

# CLAUDE.md
 
See [AGENTS.md](AGENTS.md) for all conventions in this project.

Cursor uses rule files under .cursor/rules/ with an .mdc extension and a small frontmatter block. globs scopes a rule to matching files, and alwaysApply keeps it in context:

---
description: SwiftUI conventions for this project
globs: ["**/*.swift"]
alwaysApply: true
---
 
Follow AGENTS.md at the repo root. Key rules:
- @Observable + @State, never ObservableObject / @Published.
- Networking through APIClient.shared.send(_:).
- Entitlements via SubscriptionManager.shared.hasEntitlement(_:).

Codex reads AGENTS.md natively, so it needs no shim; the root file is enough.

The point of the shims is coverage, not duplication. Each one is a few lines that defer to AGENTS.md, so when you change a convention you edit one file and every tool follows.

Beyond one file: skills and file headers

A root file gets an agent started correctly, but two extra habits keep it correct as the project grows past what any single file can hold in context.

Skill packs. Some agents support reusable "skills," focused rule sets an agent can pull in on demand for a specific task. Claude Code, for example, reads skills from a .claude/skills/ folder. Dropping in well-made packs (there are strong open-source ones for reviewing SwiftUI, for Swift Testing, and for SwiftData) gives the agent deeper, topic-specific guidance than a general AGENTS.md has room for, without bloating the root file.

File headers. As a codebase grows, an agent editing a single file deep in the tree may never re-read AGENTS.md. A short header comment on each file pulls the conventions back into view right where the work happens:

//
//  HomeView.swift
//  Conventions: see AGENTS.md. State: @Observable + @State.
//  Networking: APIClient.shared. Entitlements: SubscriptionManager.shared.
//

You can automate this in Xcode with a file-template macro (an IDETemplateMacros.plist) so every new file is created with the header already in place. It is a small thing that quietly keeps every file pointing back at the same rules.

A starter AGENTS.md

Paste this at your repository root and adjust the specifics to your project. It is deliberately short; that is the point.

# AGENTS.md
 
Conventions for AI agents working in this SwiftUI app. Follow these over your
own defaults. When a rule says "not X", do not use X even if it would work.
 
## Architecture
- SwiftUI throughout. Views are structs; view models are `@Observable` classes.
- One feature per folder under `Sources/Views/`.
 
## State
- Use the `@Observable` macro (iOS 17+) for view models, `@State` to own them.
- Do NOT use `ObservableObject`, `@Published`, or `@StateObject`.
 
## Networking
- All requests go through `APIClient.shared.send(_:)`. Endpoints are `enum` cases.
- Never call `URLSession` directly in a view or view model.
 
## Assets and strings
- Images and colors: `Asset.Images.icon.image`, never string literals.
- User-facing text: `L10n.Onboarding.title`, never hardcoded strings.
 
## Subscriptions
- Entitlements: `SubscriptionManager.shared.hasEntitlement("pro")`.
- Never read `Purchases.shared` or `purchases.products` directly.
 
## Lifecycle and testing
- Load async work in `.task { }`, not `.onAppear { Task { } }`.
- New tests use Swift Testing (`@Test`, `#expect`), not XCTest.

Keeping it honest

An AGENTS.md is only useful while it matches reality. A few habits keep it that way.

  • Update it when a convention changes. The moment the file disagrees with the code, the agent gets conflicting signals and picks one at random. A stale rules file is worse than none.
  • Keep it short. Every rule you add dilutes the others' weight in the agent's context. Cut anything that is not a real, recurring decision.
  • Do not contradict yourself across files. If AGENTS.md says one thing and a Cursor rule says another, you are back to the coin toss. The shims should defer, not restate.
  • Still review the output. AGENTS.md raises the floor on consistency; it does not remove the need to read what the agent wrote. Treat it as onboarding docs for a fast junior developer, not a guarantee.

FAQ

What is AGENTS.md?

It is a markdown file at your repository root that tells AI coding agents how to work in your project: the architecture, the patterns to use, and the ones to avoid. Agents read it before writing code, so their output matches your conventions instead of their generic defaults.

Do Claude Code and Cursor both read AGENTS.md?

Codex reads AGENTS.md directly. Claude Code's primary file is CLAUDE.md and Cursor's is .cursor/rules/*.mdc, so the clean setup is a root AGENTS.md plus a two-line shim for each that points back at it, giving you one source of truth with full coverage.

Why does AI-generated SwiftUI come out inconsistent?

SwiftUI has had several state-management patterns since 2019, and an agent's training data mixes all of them, so it guesses differently each time. The same happens with networking, assets, and lifecycle. Writing your chosen patterns down in AGENTS.md replaces the guessing with a default.

What is the difference between AGENTS.md and Cursor rules?

AGENTS.md is a single, tool-agnostic file that any agent can read. Cursor rules (.cursor/rules/*.mdc) are Cursor-specific and support scoping to file globs. Use AGENTS.md as the source of truth and keep the Cursor rule as a thin shim that defers to it.

How long should an AGENTS.md be?

A page or two. Every rule competes for the agent's attention, so include only real, recurring decisions and rule out the tempting wrong options explicitly. A long, philosophical file gets skimmed and loses its effect.

Does AGENTS.md replace code review?

No. It raises the baseline consistency of what an agent produces, which removes a lot of nitpicking, but you still read the output. Think of it as onboarding documentation for a fast junior developer, not a substitute for review.


Spaceport ships all of this in every generated project. The Xcode project comes with an AGENTS.md, a thin CLAUDE.md that points at it, Cursor rules under .cursor/rules/, and a set of open-source Swift skill packs under .claude/skills/, plus typed Asset and L10n constants and wrapper singletons like SubscriptionManager.shared already in place, so your agent writes consistent SwiftUI from the first prompt instead of learning your conventions the hard way. From an indie iOS dev, for indie iOS devs.

Read more at spaceport.build

Community appsJoin Discord