--- trigger: glob glob: *.swift --- | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL | |----|------------|----------------------------------|--------------------------------------------------|--------------------------------------------------------------------|---------------------------------------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------|------------|-------------------------------------------------------------------------| | 1 | Views | Use struct for views | SwiftUI views are value types | struct MyView: View | class MyView: View | struct ContentView: View { var body: some View } | class ContentView: View | High | [SwiftUI View](https://developer.apple.com/documentation/swiftui/view) | | 2 | Views | Keep views small and focused | Single responsibility for each view | Extract subviews for complex layouts | Large monolithic views | Extract HeaderView FooterView | 500+ line View struct | Medium | - | | 3 | Views | Use body computed property | body returns the view hierarchy | var body: some View { } | func body() -> some View | var body: some View { Text("Hello") } | func body() -> Text | High | - | | 4 | Views | Prefer composition over inheritance | Compose views using ViewBuilder | Combine smaller views | Inheritance hierarchies | VStack { Header() Content() } | class SpecialView extends BaseView | Medium | - | | 5 | State | Use @State for local state | Simple value types owned by view | @State for view-local primitives | @State for shared data | @State private var count = 0 | @State var sharedData: Model | High | [SwiftUI State](https://developer.apple.com/documentation/swiftui/state) | | 6 | State | Use @Binding for two-way data | Pass mutable state to child views | @Binding for child input | @State in child for parent data | @Binding var isOn: Bool | $isOn to pass binding | Medium | [SwiftUI Binding](https://developer.apple.com/documentation/swiftui/binding) | | 7 | State | Use @StateObject for reference types | ObservableObject owned by view | @StateObject for view-created objects | @ObservedObject for owned objects | @StateObject private var vm = ViewModel() | @ObservedObject var vm = ViewModel() | High | [SwiftUI StateObject](https://developer.apple.com/documentation/swiftui/stateobject) | | 8 | State | Use @ObservedObject for injected objects | Reference types passed from parent | @ObservedObject for injected dependencies | @StateObject for injected objects | @ObservedObject var vm: ViewModel | @StateObject var vm: ViewModel (injected) | High | [SwiftUI ObservedObject](https://developer.apple.com/documentation/swiftui/observedobject) | | 9 | State | Use @EnvironmentObject for shared state | App-wide state injection | @EnvironmentObject for global state | Prop drilling through views | @EnvironmentObject var settings: Settings | Pass settings through 5 views | Medium | [SwiftUI EnvironmentObject](https://developer.apple.com/documentation/swiftui/environmentobject) | | 10 | State | Use @Published in ObservableObject | Automatically publish property changes | @Published for observed properties | Manual objectWillChange calls | @Published var items: [Item] = [] | var items: [Item] { didSet { objectWillChange.send() } } | Medium | - | | 11 | Observable | Use @Observable macro (iOS 17+) | Modern observation without Combine | @Observable class for view models | ObservableObject for new projects | @Observable class ViewModel { } | class ViewModel: ObservableObject | Medium | [Observation](https://developer.apple.com/documentation/observation) | | 12 | Observable | Use @Bindable for @Observable | Create bindings from @Observable | @Bindable var vm for bindings | @Binding with @Observable | @Bindable var viewModel | $viewModel.name with @Observable | Medium | - | | 13 | Layout | Use VStack HStack ZStack | Standard stack-based layouts | Stacks for linear arrangements | GeometryReader for simple layouts | VStack { Text() Image() } | GeometryReader for vertical list | Medium | [SwiftUI VStack](https://developer.apple.com/documentation/swiftui/vstack) | | 14 | Layout | Use LazyVStack LazyHStack for lists | Lazy loading for performance | Lazy stacks for long lists | Regular stacks for 100+ items | LazyVStack { ForEach(items) } | VStack { ForEach(largeArray) } | High | [SwiftUI LazyVStack](https://developer.apple.com/documentation/swiftui/lazyvstack) | | 15 | Layout | Use GeometryReader sparingly | Only when needed for sizing | GeometryReader for responsive layouts | GeometryReader everywhere | GeometryReader for aspect ratio | GeometryReader wrapping everything | Medium | - | | 16 | Layout | Use spacing and padding consistently | Consistent spacing throughout app | Design system spacing values | Magic numbers for spacing | .padding(16) or .padding() | ".padding(13), .padding(17)" | Low | - | | 17 | Layout | Use frame modifiers correctly | Set explicit sizes when needed | .frame(maxWidth: .infinity) | Fixed sizes for responsive content | .frame(maxWidth: .infinity) | .frame(width: 375) | Medium | - | | 18 | Modifiers | Order modifiers correctly | Modifier order affects rendering | Background before padding for full coverage | Wrong modifier order | .padding().background(Color.red) | .background(Color.red).padding() | High | - | | 19 | Modifiers | Create custom ViewModifiers | Reusable modifier combinations | ViewModifier for repeated styling | Duplicate modifier chains | struct CardStyle: ViewModifier | .shadow().cornerRadius() everywhere | Medium | [SwiftUI ViewModifier](https://developer.apple.com/documentation/swiftui/viewmodifier) | | 20 | Modifiers | Use conditional modifiers carefully | Avoid changing view identity | if-else with same view type | Conditional that changes view identity | Text(title).foregroundColor(isActive ? .blue : .gray) | if isActive { Text().bold() } else { Text() } | Medium | - | | 21 | Navigation | Use NavigationStack (iOS 16+) | Modern navigation with type-safe paths | NavigationStack with navigationDestination | NavigationView for new projects | NavigationStack { } | NavigationView { } (deprecated) | Medium | [SwiftUI NavigationStack](https://developer.apple.com/documentation/swiftui/navigationstack) | | 22 | Navigation | Use navigationDestination | Type-safe navigation destinations | .navigationDestination(for:) | NavigationLink(destination:) | .navigationDestination(for: Item.self) | NavigationLink(destination: DetailView()) | Medium | - | | 23 | Navigation | Use @Environment for dismiss | Programmatic navigation dismissal | @Environment(\.dismiss) var dismiss | presentationMode (deprecated) | @Environment(\.dismiss) var dismiss | @Environment(\.presentationMode) | Low | - | | 24 | Lists | Use List for scrollable content | Built-in scrolling and styling | List for standard scrollable content | ScrollView + VStack for simple lists | List { ForEach(items) { } } | ScrollView { VStack { ForEach } } | Low | [SwiftUI List](https://developer.apple.com/documentation/swiftui/list) | | 25 | Lists | Provide stable identifiers | Use Identifiable or explicit id | Identifiable protocol or id parameter | Index as identifier | ForEach(items) where Item: Identifiable | "ForEach(items.indices, id: \.self)" | High | - | | 26 | Lists | Use onDelete and onMove | Standard list editing | onDelete for swipe to delete | Custom delete implementation | .onDelete(perform: delete) | .onTapGesture for delete | Low | - | | 27 | Forms | Use Form for settings | Grouped input controls | Form for settings screens | Manual grouping for forms | Form { Section { Toggle() } } | VStack { Toggle() } | Low | [SwiftUI Form](https://developer.apple.com/documentation/swiftui/form) | | 28 | Forms | Use @FocusState for keyboard | Manage keyboard focus | @FocusState for text field focus | Manual first responder handling | @FocusState private var isFocused: Bool | UIKit first responder | Medium | [SwiftUI FocusState](https://developer.apple.com/documentation/swiftui/focusstate) | | 29 | Forms | Validate input properly | Show validation feedback | Real-time validation feedback | Submit without validation | TextField with validation state | TextField without error handling | Medium | - | | 30 | Async | Use .task for async work | Automatic cancellation on view disappear | .task for view lifecycle async | onAppear with Task | .task { await loadData() } | onAppear { Task { await loadData() } } | Medium | [SwiftUI View.task](https://developer.apple.com/documentation/swiftui/view/task(priority:_:)) | | 31 | Async | Handle loading states | Show progress during async operations | ProgressView during loading | Empty view during load | if isLoading { ProgressView() } | No loading indicator | Medium | - | | 32 | Async | Use @MainActor for UI updates | Ensure UI updates on main thread | @MainActor on view models | Manual DispatchQueue.main | @MainActor class ViewModel | DispatchQueue.main.async | Medium | - | | 33 | Animation | Use withAnimation | Animate state changes | withAnimation for state transitions | No animation for state changes | withAnimation { isExpanded.toggle() } | isExpanded.toggle() | Low | [SwiftUI withAnimation](https://developer.apple.com/documentation/swiftui/withanimation(_:_:)) | | 34 | Animation | Use .animation modifier | Apply animations to views | .animation(.spring()) on view | Manual animation timing | .animation(.easeInOut) | CABasicAnimation equivalent | Low | - | | 35 | Animation | Respect reduced motion | Check accessibility settings | Check accessibilityReduceMotion | Ignore motion preferences | @Environment(\.accessibilityReduceMotion) | Always animate regardless | High | - | | 36 | Preview | Use #Preview macro (Xcode 15+) | Modern preview syntax | #Preview for view previews | PreviewProvider protocol | #Preview { ContentView() } | struct ContentView_Previews: PreviewProvider | Low | - | | 37 | Preview | Create multiple previews | Test different states and devices | Multiple previews for states | Single preview only | "#Preview("Light") { } #Preview("Dark") { }" | Single preview configuration | Low | - | | 38 | Preview | Use preview data | Dedicated preview mock data | Static preview data | Production data in previews | Item.preview for preview | Fetch real data in preview | Low | - | | 39 | Performance| Avoid expensive body computations | Body should be fast to compute | Precompute in view model | Heavy computation in body | vm.computedValue in body | Complex calculation in body | High | - | | 40 | Performance| Use Equatable views | Skip unnecessary view updates | Equatable for complex views | Default equality for all views | struct MyView: View Equatable | No Equatable conformance | Medium | - | | 41 | Performance| Profile with Instruments | Measure before optimizing | Use SwiftUI Instruments | Guess at performance issues | Profile with Instruments | Optimize without measuring | Medium | - | | 42 | Accessibility| Add accessibility labels | Describe UI elements | .accessibilityLabel for context | Missing labels | ".accessibilityLabel("Close button")" | Button without label | High | [SwiftUI View.accessibilityLabel](https://developer.apple.com/documentation/swiftui/view/accessibilitylabel(_:)-1d7jv) | | 43 | Accessibility| Support Dynamic Type | Respect text size preferences | Scalable fonts and layouts | Fixed font sizes | .font(.body) with Dynamic Type | .font(.system(size: 16)) | High | - | | 44 | Accessibility| Use semantic views | Proper accessibility traits | Correct accessibilityTraits | Wrong semantic meaning | Button for actions Image for display | Image that acts like button | Medium | - | | 45 | Testing | Use ViewInspector for testing | Third-party view testing | ViewInspector for unit tests | UI tests only | ViewInspector assertions | Only XCUITest | Medium | - | | 46 | Testing | Test view models | Unit test business logic | XCTest for view model | Skip view model testing | Test ViewModel methods | No unit tests | Medium | - | | 47 | Testing | Use preview as visual test | Previews catch visual regressions | Multiple preview configurations | No visual verification | Preview different states | Single preview only | Low | - | | 48 | Architecture| Use MVVM pattern | Separate view and logic | ViewModel for business logic | Logic in View | ObservableObject ViewModel | @State for complex logic | Medium | - | | 49 | Architecture| Keep views dumb | Views display view model state | View reads from ViewModel | Business logic in View | view.items from vm.items | Complex filtering in View | Medium | - | | 50 | Architecture| Use dependency injection | Inject dependencies for testing | Initialize with dependencies | Hard-coded dependencies | init(service: ServiceProtocol) | let service = RealService() | Medium | - |