XcodeBuildMCP-iOS-Template / rules
Cursor rule42 starsChanged 17 months agoMITView on GitHub
Modern SwiftUI patterns using @Observable, state management with MV architecture, async operations with .task modifier, accessibility guidelines, and performance optimizations. Apply when creating or modifying SwiftUI views, handling state, or implementing UI interactions.
We split this swiftui-patterns.mdc into the rules it gives an agent
25 rules. About 0 are common, 16 are distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈16
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 45
Use @State for view-specific state
- Very specific: line 46
Use enum for view states (loading, loaded, error)
- Very specific: line 47
Use .task(id:) and .onChange(of:) for side effects
- Distinctive: line 53
Test services independently, not views
- Distinctive: line 54
Use SwiftUI previews for visual testing
- Distinctive: line 58
Use compound views that compose smaller views
- Distinctive: line 60
Never reach for a ViewModel as the solution
- Distinctive: line 67
Use @Observable for model classes that need to be observed by SwiftUI:
- Distinctive: line 122
Note: If your app targets iOS 26+, you can take advantage of these cutting-edge SwiftUI APIs introduced in June 2025.
- Distinctive: line 126
When targeting iOS 26+, consider using these new APIs:
- Distinctive: line 139
Search role for tabs with search field replacing tab bar
- Distinctive: line 156
Only use when targeting iOS 26+: Ensure your deployment target supports these APIs
- Distinctive: line 157
Progressive enhancement: Use availability checks if supporting multiple iOS versions
- Distinctive: line 158
Feature detection: Test on older simulators to ensure graceful fallbacks
- Distinctive: line 184
Use @Bindable: when you need bindings to @Observable objects
- Distinctive: line 186
No ViewModels: - use native SwiftUI state management instead
- Distinctive: line 233
Every interactive element must have:
- Very specific: line 249
Use LazyVStack/LazyHStack for large collections
- Very specific: line 251
Use .id() modifier sparingly as it forces view recreation
- Very specific: line 252
Avoid heavy computations in body - move to computed properties or methods
- Very specific: line 257
Use SwiftUI's built-in animations with .animation() modifier
- Very specific: line 258
Prefer withAnimation {} for coordinated animations
- Very specific: line 259
Use .transition() for view appearance/disappearance
- Distinctive: line 260
Consider accessibility preferences for reduced motion
- Distinctive: line 262
All SwiftUI views should be placed in MyProjectPackage/Sources following this modular architecture pattern.
≈ “Common” is an estimate: at least 1 in 5 of the 6,350 Cursor rule files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
---
description: "Modern SwiftUI patterns using @Observable, state management with MV architecture, async operations with .task modifier, accessibility guidelines, and performance optimizations. Apply when creating or modifying SwiftUI views, handling state, or implementing UI interactions."
globs:
alwaysApply: false
---
name: "SwiftUI Development Patterns"
description: "Modern SwiftUI patterns using @Observable, state management with MV architecture, async operations with .task modifier, accessibility guidelines, and performance optimizations. Apply when creating or modifying SwiftUI views, handling state, or implementing UI interactions."
agent_requested: true
applies_to: ["**/Sources/**/*View.swift", "**/Sources/**/*Screen.swift", "**/*View.swift", "**/*Screen.swift"]
---
# SwiftUI Development Patterns (2025)
## Modern SwiftUI Architecture Guidelines
### No ViewModels - Use Native SwiftUI Data Flow
**New features MUST follow these patterns:**
1. **Views as Pure State Expressions**
```swift
struct MyView: View {
@Environment(MyService.self) private var service
@State private var viewState: ViewState = .loading
enum ViewState {
case loading
case loaded(data: [Item])
case error(String)
}
var body: some View {
// View is just a representation of its state
}
}
```
2. **Use Environment Appropriately**
- **App-wide services**: Router, Theme, CurrentAccount, Client, etc. - use `@Environment`
- **Feature-specific services**: Timeline services, single-view logic - use `let` properties with `@Observable`
- Rule: Environment for cross-app/cross-feature dependencies, let properties for single-feature services
- Access app-wide via `@Environment(ServiceType.self)`
- Feature services: `private let myService = MyObservableService()`
3. **Local State Management**
- Use `@State` for view-specific state
- Use `enum` for view states (loading, loaded, error)
- Use `.task(id:)` and `.onChange(of:)` for side effects
- Pass state between views using `@Binding`
4. **No ViewModels Required**
- Views should be lightweight and disposable
- Business logic belongs in services/clients
- Test services independently, not views
- Use SwiftUI previews for visual testing
5. **When Views Get Complex**
- Split into smaller subviews
- Use compound views that compose smaller views
- Pass state via bindings between views
- Never reach for a ViewModel as the solution
## State Management (MV Pattern)
SwiftUI views should follow the Model-View pattern using modern Swift state management:
### @Observable Classes
Use @Observable for model classes that need to be observed by SwiftUI:
```swift
@Observable
class UserSettings {
var theme: Theme = .light
var fontSize: Double = 16.0
}
```
### View State Usage
```swift
@MainActor
struct ContentView: View {
@State private var settings = UserSettings()
var body: some View {
VStack {
// Direct property access, no $ prefix needed
Text("Font Size: \(settings.fontSize)")
// For bindings, use @Bindable
@Bindable var settings = settings
Slider(value: $settings.fontSize, in: 10...30)
}
}
}
```
### Environment for Shared State
```swift
@MainActor
struct ContentView: View {
@State private var userSettings = UserSettings()
var body: some View {
NavigationStack {
MainView()
.environment(userSettings)
}
}
}
@MainActor
struct MainView: View {
@Environment(UserSettings.self) private var settings
var body: some View {
Text("Current theme: \(settings.theme)")
}
}
```
## iOS 26 Features (Optional)
**Note**: If your app targets iOS 26+, you can take advantage of these cutting-edge SwiftUI APIs introduced in June 2025. These features are optional and should only be used when your deployment target supports iOS 26.
### Available iOS 26 SwiftUI APIs
When targeting iOS 26+, consider using these new APIs:
#### Liquid Glass Effects
- `glassEffect(_:in:isEnabled:)` - Apply Liquid Glass effects to views
- `buttonStyle(.glass)` - Apply Liquid Glass styling to buttons
- `ToolbarSpacer` - Create visual breaks in toolbars with Liquid Glass
#### Enhanced Scrolling
- `scrollEdgeEffectStyle(_:for:)` - Configure scroll edge effects
- `backgroundExtensionEffect()` - Duplicate, mirror, and blur views around edges
#### Tab Bar Enhancements
- `tabBarMinimizeBehavior(_:)` - Control tab bar minimization behavior
- Search role for tabs with search field replacing tab bar
- `TabViewBottomAccessoryPlacement` - Adjust accessory view content based on placement
#### Animation
- `@Animatable` macro - SwiftUI synthesizes custom animatable data properties
#### UI Components
- `Slider` with automatic tick marks when using step parameter
- `windowResizeAnchor(_:)` - Set window anchor point for resizing
#### Text Enhancements
- `TextEditor` now supports `AttributedString`
- `AttributedTextSelection` - Handle text selection with attributed text
- `AttributedTextFormattingDefinition` - Define text styling in specific contexts
- `FindContext` - Create find navigator in text editing views
### iOS 26 Usage Guidelines
- **Only use when targeting iOS 26+**: Ensure your deployment target supports these APIs
- **Progressive enhancement**: Use availability checks if supporting multiple iOS versions
- **Feature detection**: Test on older simulators to ensure graceful fallbacks
- **Modern aesthetics**: Leverage Liquid Glass effects for cutting-edge UI design
```swift
// Example: Using iOS 26 features with availability checks
struct ModernButton: View {
var body: some View {
Button("Tap me") {
// Action
}
.buttonStyle({
if #available(iOS 26.0, *) {
.glass
} else {
.bordered
}
}())
}
}
```
## Required Patterns
1. **Always use @MainActor** for view structs and UI-related code
2. **Always use .task modifier** for async operations tied to view lifecycle
3. **Never use Task {} in onAppear** - it doesn't auto-cancel and can cause issues
4. **Use @Bindable** when you need bindings to @Observable objects
5. **Extract complex views** into separate components when body gets large
6. **No ViewModels** - use native SwiftUI state management instead
## Async Operations in Views
### Correct: Use .task Modifier
```swift
@MainActor
struct ItemListView: View {
@State private var model = DataModel()
var body: some View {
List(model.items) { item in
Text(item.name)
}
.overlay {
if model.isLoading {
ProgressView()
}
}
.task {
// This task automatically cancels when view disappears
do {
try await model.loadData()
} catch {
// Handle error
}
}
.refreshable {
// Pull to refresh also uses async/await
try? await model.loadData()
}
}
}
```
### Incorrect: Task in onAppear
```swift
// ❌ DON'T DO THIS
.onAppear {
Task { // This doesn't auto-cancel!
items = try await loadItems()
}
}
```
## Accessibility Requirements
Every interactive element must have:
- `accessibilityLabel` for screen readers
- `accessibilityIdentifier` for UI testing
- `accessibilityHint` when the action isn't obvious
```swift
Button("Save") {
save()
}
.accessibilityLabel("Save document")
.accessibilityIdentifier("saveButton")
.accessibilityHint("Saves the current document to disk")
```
## Performance Guidelines
- Use `LazyVStack`/`LazyHStack` for large collections
- Implement `Equatable` on models for better diffing
- Use `.id()` modifier sparingly as it forces view recreation
- Avoid heavy computations in `body` - move to computed properties or methods
- @Observable tracks only accessed properties, improving performance over @Published
## Animation Best Practices
- Use SwiftUI's built-in animations with `.animation()` modifier
- Prefer `withAnimation {}` for coordinated animations
- Use `.transition()` for view appearance/disappearance
- Consider accessibility preferences for reduced motion
All SwiftUI views should be placed in [MyProjectPackage/Sources](mdc:MyProjectPackage/Sources) following this modular architecture pattern.
Where files are headingNew Cursor rule files have more rules about planning and tracking tasks, and fewer about code style.
Of the Cursor rule files we read, the 3,978 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository8 other files it gives its agents.
- template/CLAUDE.mdCLAUDE.md
- template/.github/copilot-instructions.mdCopilot instructions
- template/.cursor/rules/foundation-models.mdcCursor rule
- template/.cursor/rules/general-rules.mdcCursor rule
- template/.cursor/rules/swift-concurrency.mdcCursor rule
- template/.cursor/rules/swift-ios-project.mdcCursor rule
- template/.cursor/rules/swift-testing.mdcCursor rule
- template/.cursor/rules/xcodebuildmcp-tools.mdcCursor rule
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

