Loading...
Loading...
Agent skill that helps AI coding assistants write smarter, modern SwiftUI code with best practices for API usage, design, performance, and accessibility
npx skill4agent add aradotso/ai-agent-skills swiftui-agent-skillSkill by ara.so — AI Agent Skills collection.
npx skills add https://github.com/twostraws/swiftui-agent-skill --skill swiftui-pro/plugin marketplace add twostraws/SwiftUI-Agent-Skill
/plugin install swiftui-pro@swiftui-agent-skill/swiftui-pro/swiftui-pro Check for deprecated API
/swiftui-pro Focus on accessibility
/swiftui-pro Review navigation patterns$swiftui-pro$swiftui-pro Look for performance issues
$swiftui-pro Check VoiceOver supportUse the SwiftUI Pro skill to review this view for best practices
Check this SwiftUI code with the agent skillstruct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
List {
NavigationLink("Detail", value: "detail")
}
.navigationDestination(for: String.self) { value in
DetailView(item: value)
}
}
}
}struct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
Button("Go to Detail") {
path.append("detail")
}
.navigationDestination(for: String.self) { value in
DetailView(item: value)
}
}
}
}struct CounterView: View {
@State private var count = 0
var body: some View {
Button("Count: \(count)") {
count += 1
}
}
}@Observable
class AppState {
var username = ""
var isLoggedIn = false
}
struct ContentView: View {
let state = AppState()
var body: some View {
Text("User: \(state.username)")
.onChange(of: state.isLoggedIn) { oldValue, newValue in
print("Login state changed")
}
}
}struct ProfileView: View {
@Bindable var user: User
var body: some View {
TextField("Name", text: $user.name)
}
}struct CustomButton: View {
var body: some View {
Image(systemName: "star.fill")
.foregroundStyle(.yellow)
.onTapGesture {
// action
}
.accessibilityLabel("Favorite")
.accessibilityAddTraits(.isButton)
}
}struct UserCard: View {
let name: String
let age: Int
var body: some View {
VStack {
Text(name)
Text("\(age) years old")
}
.accessibilityElement(children: .combine)
}
}Button("Delete") {
// delete action
}
.accessibilityHint("Deletes this item permanently")// Grid for structured layouts
Grid(alignment: .leading, horizontalSpacing: 20) {
GridRow {
Text("Name:")
Text("John")
}
GridRow {
Text("Age:")
Text("30")
}
}
// ViewThatFits for responsive layouts
ViewThatFits {
HStack {
content
}
VStack {
content
}
}// BAD
GeometryReader { outer in
GeometryReader { inner in
// layout code
}
}
// GOOD - use .containerRelativeFrame or layout protocol
Color.blue
.containerRelativeFrame(.horizontal) { width, axis in
width * 0.5
}// Always use LazyVStack/LazyHStack for scrolling content
ScrollView {
LazyVStack {
ForEach(items) { item in
ItemView(item: item)
}
}
}struct ContentView: View {
@State private var items: [Item] = []
// BAD - computed every time body runs
var body: some View {
let sortedItems = items.sorted()
List(sortedItems) { item in
Text(item.name)
}
}
}
// GOOD - cache computed values
struct ContentView: View {
@State private var items: [Item] = []
private var sortedItems: [Item] {
items.sorted()
}
var body: some View {
List(sortedItems) { item in
Text(item.name)
}
}
}// GOOD
struct ContentView: View {
@State private var data: [Item] = []
var body: some View {
List(data) { item in
Text(item.name)
}
.task {
await loadData()
}
}
func loadData() async {
// async loading
}
}struct AnimatedView: View {
@State private var isExpanded = false
var body: some View {
VStack {
if isExpanded {
Text("Details")
.transition(.move(edge: .top))
}
Button("Toggle") {
withAnimation(.spring(response: 0.3)) {
isExpanded.toggle()
}
}
}
}
}struct RotatingView: View {
@State private var rotation = 0.0
var body: some View {
Image(systemName: "arrow.clockwise")
.rotationEffect(.degrees(rotation))
.animation(.linear(duration: 1).repeatForever(autoreverses: false), value: rotation)
.onAppear {
rotation = 360
}
}
}NavigationStackNavigationStackcontainerRelativeFrame@Observable@Observable@State@ObservableonChange(of:initial:_:)npx skills add https://github.com/twostraws/swiftui-agent-skill --skill swiftui-pro
# Select "all projects" during installationnpx skills add https://github.com/twostraws/swiftui-agent-skill --skill swiftui-pro
# Select "this project only" during installation@Observable
class FormData {
var email = ""
var password = ""
var isValid: Bool {
!email.isEmpty && password.count >= 8
}
}
struct LoginForm: View {
@State private var formData = FormData()
var body: some View {
Form {
TextField("Email", text: $formData.email)
.textContentType(.emailAddress)
.keyboardType(.emailAddress)
SecureField("Password", text: $formData.password)
.textContentType(.password)
Button("Login") {
// login action
}
.disabled(!formData.isValid)
}
}
}@Observable
class AppSettings {
var theme = "light"
var fontSize = 14.0
}
@main
struct MyApp: App {
@State private var settings = AppSettings()
var body: some Scene {
WindowGroup {
ContentView()
.environment(settings)
}
}
}
struct ContentView: View {
@Environment(AppSettings.self) private var settings
var body: some View {
Text("Theme: \(settings.theme)")
}
}struct CardStyle: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(.background.secondary)
.clipShape(RoundedRectangle(cornerRadius: 12))
.shadow(radius: 2)
}
}
extension View {
func cardStyle() -> some View {
modifier(CardStyle())
}
}
// Usage
Text("Hello")
.cardStyle()TaskDispatchQueue.main.async.onAppear {
Task {
await loadData()
}
}// BAD
struct ChildView: View {
let settings: AppSettings // Won't observe changes
}
// GOOD
struct ChildView: View {
@Environment(AppSettings.self) private var settings
}@State private var path = NavigationPath()
// Restore path from storage
.task {
if let savedPath = try? await loadPath() {
path = savedPath
}
}List(items, id: \.id) { item in
ItemRow(item: item)
}
.id(items.map(\.id)) // Force refresh when needed