SwiftUI Views
- Strongly prefer to avoid breaking up view bodies using computed properties or methods that return
some View, even if@ViewBuilderis used. Extract them into separateViewstructs instead, placing each into its own file. - Flag
bodyproperties that are excessively long; they should be broken into extracted subviews. - If the user has created a handful of small, private helper
some Viewproperties for structural readability, and they both belong to the same concern asbodyand would fit inbodyat an acceptable length if inlined, these can be left alone. Otherwise, they should be extracted to newViewstructs. - Button actions should be extracted from view bodies into separate methods, to avoid mixing layout and logic.
- Similarly, general business logic should not live inline in
task(),onAppear()or elsewhere inbody. - Prefer to place view logic into view models or similar, so it can be tested. For more help with testing, suggest the Swift Testing Pro agent skill.
- Each type (struct, class, enum) should be in its own Swift file. Flag files containing multiple type definitions.
- Unless a full-screen editing experience is required, prefer using
TextFieldwithaxis: .verticalto usingTextEditor, because it allows placeholder text. If a specific minimum height is required forTextField, use something likelineLimit(5...). - If a button action can be provided directly as an
actionparameter, do so. For example:Button("Label", systemImage: "plus", action: myAction)is preferred overButton("Label", systemImage: "plus") { action() }. - When rendering SwiftUI views to images, strongly prefer
ImageRendereroverUIGraphicsImageRenderer. #Previewshould be used for previews, not the legacyPreviewProviderprotocol.- When using
TabView(selection:), use a binding to a property that stores an enum rather than an integer or string. For example,Tab("Home", systemImage: "house", value: .home)is better thanTab("Home", systemImage: "house", value: 0). - Strongly prefer to avoid breaking up view bodies using computed properties or methods that return
some View, even if@ViewBuilderis used. Extract them into separateViewstructs instead, placing each into its own file. (Yes, this is repeated, but it’s so important it needs to be mentioned twice.)
Animating views
- Strongly prefer to use the
@Animatablemacro over creatinganimatableDatamanually – the macro automatically adds conformance to theAnimatableprotocol and creates the correctanimatableDataproperty. If some properties should not or cannot be animated (e.g. Booleans, integers, etc), mark them@AnimatableIgnored. - Never use
animation(_ animation: Animation?); always provide a value to watch, such as.animation(.bouncy, value: score). - Chaining animations must be done using a
completionclosure passed towithAnimation(), rather than trying to execute multiplewithAnimation()calls using delays.
For example:
Button("Animate Me") {
withAnimation {
scale = 2
} completion: {
withAnimation {
scale = 1
}
}
}