AI Swift iOS Claude Code CLAUDE.md Xcode SwiftUI

Claude Code for Swift and iOS Development: CLAUDE.md Templates for Xcode Projects (2026)

The Prompt Shelf ·

iOS development and AI coding tools have an awkward relationship. Claude Code operates entirely from the terminal. Xcode is resolutely GUI-first. The gap between them is real, and most guides about “AI coding for iOS” skip over the inconvenient parts.

This guide doesn’t. We’ll cover exactly what Claude Code does well in Swift and iOS projects, where it falls flat, and — most importantly — what to put in your CLAUDE.md to make it genuinely useful for the parts where it excels.

Claude Code + iOS: Where It Shines (And Where It Doesn’t)

Let’s start with the honest version.

Claude Code is effective for:

  • Swift Package Manager (SPM) projects — pure terminal workflow, no Xcode required
  • Business logic in ViewModels, Services, and Repositories
  • Unit test generation (XCTest) from existing model and service code
  • Refactoring Swift files with clear architecture rules
  • Enforcing Swift 6 concurrency patterns across a codebase
  • swift build and swift test for SPM packages

Claude Code struggles with:

  • Xcode GUI operations — it can’t open .xcworkspace files meaningfully
  • SwiftUI Previews — these require Xcode’s canvas, which is inaccessible from the terminal
  • Device deployment — code signing and provisioning profiles require Apple’s toolchain via Xcode
  • Simulator GUI interaction — you can launch simulators via xcrun simctl, but Claude Code can’t tap buttons or verify visual output
  • .xcodeproj project file modifications — adding new files to Xcode targets requires Xcode GUI or third-party tools like xcodegen

The practical split: use Claude Code for logic and tests, switch to Xcode for UI work and deployment.

Complete CLAUDE.md for Xcode Projects

Here’s the full production-ready template. Adapt the project name, Swift version, and architecture decisions to your codebase.

# iOS App: [AppName]

## Build & Test Commands
- Build (simulator): `xcodebuild -project [App].xcodeproj -scheme [App] -sdk iphonesimulator -configuration Debug build`
- Build (SPM package): `swift build`
- Test: `xcodebuild test -project [App].xcodeproj -scheme [App] -destination 'platform=iOS Simulator,name=iPhone 16'`
- Lint: `swiftlint --strict`
- Format: `swiftformat .`
- SPM update: `swift package update`

## Swift Version and Target
- Swift version: 6.0 (check Package.swift or project settings)
- iOS deployment target: 17.0+
- Xcode version: 16.x

## Language Conventions
- Use Swift concurrency (async/await, Actor) — avoid DispatchQueue for new code
- SwiftUI for new views; UIKit only for legacy compatibility layers
- MVVM architecture: View → ViewModel (ObservableObject) → Service → Repository
- Naming: PascalCase for types, camelCase for variables/functions
- Error handling: Swift typed throws (Swift 6) or Result<T, Error> for older targets
- No force unwrap (`!`) — use guard let, if let, or nil coalescing

## Architecture Rules
- ViewModels must not import SwiftUI (business logic only)
- Services handle networking and databases — inject via protocol
- No business logic in View files
- Separate files for each View, ViewModel, Service (1 type per file rule)
- Repositories abstract data sources from Services

## Testing
- XCTest for unit tests (ViewModels, Services, Repositories)
- XCUITest for UI testing — kept in a separate test target
- Mock with protocols: define ServiceProtocol, create MockService for tests
- Use `@MainActor` correctly — test ViewModel methods that update UI on main thread
- All new public functions require at least one unit test

## Dependency Management
- Prefer Swift Package Manager for all new dependencies
- CocoaPods only if required by an existing third-party SDK (avoid new Pods)
- Document each dependency with a comment in Package.swift explaining why it's included
- No new dependencies without discussion

## SwiftUI Patterns
- `@State` for local UI state only (never business state)
- `@StateObject` for ViewModels owned by the view
- `@ObservedObject` for ViewModels passed in from a parent
- `@EnvironmentObject` for app-wide state (inject at root)
- Avoid large view bodies — extract subviews when a body exceeds ~50 lines
- Previews: same file as the View, use mock data or preview providers

## Swift Concurrency Rules
- All async operations use async/await — no completion handlers in new code
- `@MainActor` annotation required on ViewModels and any class that updates UI
- Use `Task { }` to bridge sync to async contexts, not `DispatchQueue.main.async`
- Actor isolation: define custom Actors for shared mutable state accessed from multiple tasks
- Structured concurrency: prefer `async let` over `Task.detached` for parallel work

## Memory Management
- No retain cycles — use `[weak self]` in closures that capture self
- Services must not hold strong references to ViewControllers or Views
- Avoid `@objc` wrappers unless UIKit interop requires it

## Known Claude Code Limitations for This Project
- Cannot run Xcode previews — verify UI changes manually in Xcode
- Cannot sign or deploy to physical device
- Cannot modify .xcodeproj target membership — add new files to targets in Xcode manually
- For simulator operations, use `xcrun simctl` from the command line
- Focus on: logic code, SPM packages, unit tests, file-level Swift changes

Build Commands: xcodebuild + swift build

The build command section of your CLAUDE.md carries more weight than it looks like. Claude Code uses these commands to verify that changes compile before considering a task complete.

For SPM-only packages, swift build and swift test are clean and fast:

swift build                    # build the package
swift test                     # run all tests
swift test --filter MyTests    # run a specific test class

For Xcode projects, xcodebuild is the right tool — but it’s verbose and requires knowing your scheme name:

# list available schemes
xcodebuild -list -project MyApp.xcodeproj

# build for simulator
xcodebuild \
  -project MyApp.xcodeproj \
  -scheme MyApp \
  -sdk iphonesimulator \
  -configuration Debug \
  build

# run tests
xcodebuild test \
  -project MyApp.xcodeproj \
  -scheme MyApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.0'

Put the exact commands with your project name and scheme filled in. Claude Code will use them verbatim.

SwiftUI Architecture Rules in CLAUDE.md

SwiftUI’s property wrappers look similar on the surface but have meaningfully different semantics. Without explicit rules, AI agents mix them up — especially @StateObject vs @ObservedObject, which is a common source of bugs where ViewModels get recreated on every re-render.

The rules that matter most to encode:

## SwiftUI Property Wrapper Rules
- `@State`: primitive local UI state (Bool, String, Int) — never for business data
- `@StateObject`: ViewModel that this View owns and creates — use when the View creates the instance
- `@ObservedObject`: ViewModel passed in as a parameter — use when the parent creates the instance
- `@EnvironmentObject`: global app state passed via .environmentObject() at the app root
- `@Binding`: pass-through for two-way binding to a parent's @State
- NEVER use @ObservedObject for a ViewModel the View creates itself — it will be recreated on every render

This single rule prevents a whole class of subtle SwiftUI bugs where a ViewModel loses its state mid-session.

Swift Concurrency Rules (async/await, Actor)

Swift 6 introduced strict concurrency checking — code that compiled under Swift 5 with warnings now fails to compile. If your project targets Swift 6, encoding the concurrency rules in CLAUDE.md prevents Claude Code from generating code that won’t compile.

## Concurrency Rules (Swift 6)
- `@MainActor` is required on all ViewModel classes (they update `@Published` properties observed by Views)
- Use `await` for all async function calls — no callbacks or DispatchQueue in new code
- `async let` for parallel tasks: `async let a = fetchA(); async let b = fetchB(); let (x, y) = await (a, b)`
- Actor for shared mutable state: define `actor CacheActor` rather than using a serial DispatchQueue
- Sendable conformance: types passed across actor boundaries must be Sendable
- `nonisolated` only when a property is truly read-only after initialization

The Actor pattern deserves special attention. Before Swift 6, developers used serial DispatchQueue to protect shared state. Claude Code may still generate queue-based patterns — encoding the Actor preference prevents this.

Testing Strategy: XCTest + Protocol Mocking

Claude Code generates XCTest boilerplate well, but it needs to know your mocking strategy. iOS projects commonly use protocols to define service interfaces so they can be swapped for test doubles.

## Testing Patterns
- Every Service has a Protocol: `NetworkServiceProtocol`, `DatabaseServiceProtocol`
- Tests inject mock implementations: `MockNetworkService: NetworkServiceProtocol`
- ViewModels take protocols in init, not concrete types
- Test file location: `Tests/<FeatureName>Tests/<Feature>ViewModelTests.swift`
- Fixture data lives in `Tests/Fixtures/` as JSON files or static Swift structs
- XCTAssert over XCTAssertEqual when testing Equatable conformance not yet implemented
- `@MainActor` on test class or test method when testing MainActor-isolated ViewModels

Adding the @MainActor note matters more than it looks. Without it, Claude Code will generate tests that call @MainActor-isolated methods from non-isolated contexts, producing Swift 6 compilation errors that are annoying to debug.

SwiftLint + SwiftFormat Integration

SwiftLint is the standard iOS linter. If it’s in your project, encoding its location in CLAUDE.md lets Claude Code self-lint after generating code.

## Linting and Formatting
- Lint: `swiftlint --strict` (configuration in `.swiftlint.yml` at project root)
- Format: `swiftformat .` (configuration in `.swiftformat` at project root)
- Run lint after any Swift file changes
- SwiftLint violations block PR merges — fix all warnings, not just errors
- Disabled rules (project-specific): `line_length` (we allow 140 chars), `file_length`

You can also automate this with a hook so Claude Code runs SwiftLint after every file edit:

{
  "PostToolUse": [
    {
      "matcher": "Edit",
      "hooks": [
        {
          "type": "command",
          "command": "swiftlint --strict"
        }
      ]
    }
  ]
}

Place this in .claude/settings.json at your project root. Claude Code will run SwiftLint automatically after each file edit and surface any violations.

SPM-Only Projects: The Best Claude Code Setup

If you’re building a Swift package (a library, a CLI tool, or a framework that gets imported into an Xcode project), you’re in the best-case scenario for Claude Code. Everything works from the terminal with no Xcode dependency.

# [PackageName] Swift Package

## Build & Test
- Build: `swift build`
- Test: `swift test`
- Test with verbose output: `swift test --verbose`
- Generate Xcode project (optional): `swift package generate-xcodeproj`
- Clean: `swift package clean`

## Swift Rules
- Swift 6.0 strict concurrency enabled (`-strict-concurrency=complete` in Package.swift)
- All public API documented with DocC comments (`///`)
- No force unwrap (`!`) — use guard let, if let, or throw an error
- Public types must conform to Sendable where appropriate
- No Foundation imports in core logic — use Swift standard library where possible

## Package Structure
- `Sources/<TargetName>/` — production code
- `Tests/<TargetName>Tests/` — test files, mirroring Sources structure
- One type per file
- Public API surface documented before implementation

For SPM packages, Claude Code can write code, run tests, and verify compilation without any toolchain gaps. This is the workflow where it delivers the most value.

What Claude Code Cannot Do in iOS Development

We want to be explicit about this, because most AI coding guides aren’t.

Cannot run SwiftUI Previews. SwiftUI Previews require Xcode’s canvas. Claude Code has no mechanism to render or verify them. Always open Xcode to verify UI changes.

Cannot deploy to a physical device. Code signing, provisioning profiles, and device registration all require Xcode and an Apple Developer account. Claude Code cannot touch any of this.

Cannot modify Xcode project targets. Adding a new Swift file to your Xcode project means adding it to the .xcodeproj target membership. Claude Code can create the file on disk but cannot wire it into the Xcode project — you’ll need to drag it in manually or use xcodegen with a project.yml to automate this.

Cannot interact with the iOS Simulator GUI. You can launch simulators and install apps via xcrun simctl, but tapping buttons, verifying layout, and checking visual output require a human at Xcode.

Cannot access Keychain or entitlements. Capabilities like push notifications, Sign in with Apple, and iCloud require entitlement files and provisioning profile configuration in Apple’s Developer Portal. Claude Code has no path to this.

What this means in practice: Claude Code is a strong pair programmer for your Swift logic and test layers. It’s not a replacement for Xcode. The workflow that works is: write logic with Claude Code → verify UI in Xcode → ship.

AGENTS.md Version for Cross-Tool Teams

If your team uses multiple AI coding tools (Claude Code, Cursor, Copilot), an AGENTS.md file at the project root gives you a single source of truth that all tools read.

# [AppName] — AI Agent Context

## Overview
iOS app built with Swift 6, SwiftUI, and MVVM architecture. Uses Swift Package Manager.

## Build & Test
- Build: `xcodebuild -project App.xcodeproj -scheme App -sdk iphonesimulator build`
- Test: `xcodebuild test -project App.xcodeproj -scheme App -destination 'platform=iOS Simulator,name=iPhone 16'`
- SPM only: `swift build && swift test`

## Architecture
- MVVM: Views → ViewModels (@MainActor, ObservableObject) → Services (Protocol-based) → Repositories
- No business logic in View files
- No SwiftUI imports in ViewModel files

## Swift Conventions
- Swift 6 concurrency: async/await required, no DispatchQueue in new code
- Actor for shared mutable state
- Protocol-based dependency injection for testability
- One type per file

## Linting
- Run `swiftlint --strict` after Swift file changes
- Zero warnings policy — fix all SwiftLint output

## Known Tool Limitations
- AI agents cannot run SwiftUI Previews — verify UI in Xcode
- AI agents cannot modify .xcodeproj target membership — use xcodegen or add files manually

Keep AGENTS.md focused on what’s universally relevant, and move Claude-specific preferences (asking behavior, commit message style) to CLAUDE.md.


More Swift and iOS CLAUDE.md patterns are available in our rules gallery. If you’re working on a multi-module iOS architecture or a Swift package with DocC documentation, there are templates for those setups as well.

Frequently Asked Questions

Can Claude Code work with CocoaPods projects?

Yes, but with limits. Claude Code can edit Swift files in a CocoaPods project and run xcodebuild commands. It cannot run pod install reliably across environments, and it cannot modify the Podfile sensibly without knowing your target iOS version and existing pod versions. Document your pod update commands in CLAUDE.md and treat them as manual steps.

Does Claude Code understand Swift 6 concurrency errors?

Yes, this is actually one of its strengths. If you run swift build and get a Swift 6 concurrency error, Claude Code is good at reading the error and suggesting the correct fix — whether that’s adding @MainActor, conforming a type to Sendable, or restructuring an async call. Encoding your concurrency rules in CLAUDE.md prevents the errors from appearing in the first place.

Can I use Claude Code with a SwiftData project?

SwiftData (Apple’s newer persistence framework) uses macros that Claude Code handles reasonably well in terms of syntax. The key thing to encode in CLAUDE.md is which container is your source of truth and how you inject the ModelContext — the same dependency injection rules that apply to network services apply here.

What about Objective-C files in a mixed project?

Claude Code can read and modify Objective-C files, but its quality drops significantly compared to Swift. If you have a mixed Swift/ObjC project, note in CLAUDE.md that Objective-C files are legacy and should not have new logic added — Swift should be the target for any new work.

Related Articles

Explore the collection

Browse all AI coding rules — CLAUDE.md, .cursorrules, AGENTS.md, and more.

Browse Rules