This skill should be used when the user is working with Swift or SwiftUI, asks to "write SwiftUI code", "create iOS app", "fix SwiftUI view", "help with Swift", "SwiftUI architecture", "iOS development", or needs expert-level Swift/SwiftUI guidance with modern patterns and best practices.
Expert-level guidance for Swift and SwiftUI development with modern patterns, iOS 17+/macOS 14+ best practices, and production-ready code generation.
When writing Swift/SwiftUI code, ALWAYS follow these principles:
Use @Observable macro instead of ObservableObject:
// MODERN (iOS 17+)
@Observable
class UserViewModel {
var name: String = ""
var isLoggedIn: Bool = false
}
// View owns the model
struct ContentView: View {
@State private var viewModel = UserViewModel()
var body: some View {
Text(viewModel.name)
}
}
// Child view (read-only) - NO wrapper needed
struct ProfileView: View {
var viewModel: UserViewModel // Just pass it
var body: some View {
Text(viewModel.name)
}
}
// Child view (needs binding)
struct EditView: View {
@Bindable var viewModel: UserViewModel
var body: some View {
TextField("Name", text: $viewModel.name)
}
}
| Use Case | Property Wrapper |
|----------|------------------|
| Local view state (value types) | @State |
| Two-way binding to parent state | @Binding |
| Observable model (view owns) | @State + @Observable class |
| Observable model (read-only child) | No wrapper (just pass object) |
| Observable model (needs binding) | @Bindable |
| Shared app-wide state | @Environment |
| User defaults | @AppStorage |
| Scene storage | @SceneStorage |
| Focus state | @FocusState |
For most apps: Modern MVVM with @Observable
For large apps with teams: TCA (Composable Architecture)
For simple views: No ViewModel needed
See references/architecture.md for detailed patterns.
// WRONG: @ObservedObject with initialization
@ObservedObject var viewModel = ViewModel() // Will crash!
// RIGHT: Use @StateObject or @State
@StateObject private var viewModel = ViewModel() // Legacy
@State private var viewModel = ViewModel() // Modern (iOS 17+)
// WRONG: @State with reference types
@State var user: User // Where User is a class
// RIGHT: @State only for value types
@State var userName: String
@State private var viewModel = ViewModel() // Only with @Observable
// WRONG: Clearing data on error
func refresh() async {
self.items = [] // Data disappears if error occurs!
do {
self.items = try await fetchItems()
} catch {
// Items now empty forever
}
}
// RIGHT: Preserve existing data
func refresh() async {
do {
self.items = try await fetchItems()
} catch {
// Keep existing items, show error toast
}
}
// WRONG: Heavy computation in body
var body: some View {
let sorted = items.sorted { ... }.filter { ... } // Runs every render!
List(sorted) { ... }
}
// RIGHT: Memoize or compute elsewhere
var sortedItems: [Item] {
items.sorted { ... }.filter { ... }
} // Or use @State for expensive computations
// WRONG: Using id modifier on lazy content
LazyVStack {
ForEach(items) { item in
ItemView(item: item)
.id(item.id) // Breaks lazy loading!
}
}
// RIGHT: Let ForEach handle identity
LazyVStack {
ForEach(items) { item in
ItemView(item: item) // ForEach uses Identifiable
}
}
// WRONG: Using deprecated NavigationView
NavigationView {
List { ... }
}
// RIGHT: Use NavigationStack (iOS 16+)
NavigationStack {
List { ... }
}
When generating Swift/SwiftUI code:
@Observable instead of ObservableObject/@Published.task instead of .onAppear for async workFor detailed guidance, consult:
references/state-management.md - Property wrappers, @Observable, data flowreferences/architecture.md - MVVM, TCA, Clean Architecture patternsreferences/navigation.md - NavigationStack, programmatic navigation, deep linksreferences/performance.md - Lazy views, List optimization, render cyclesreferences/concurrency.md - async/await, actors, MainActor, Taskreferences/testing.md - Unit tests, ViewInspector, UI testsreferences/accessibility.md - VoiceOver, Dynamic Type, accessibility modifiersreferences/swiftdata.md - @Model, @Query, persistence patternsreferences/anti-patterns.md - Common mistakes and how to fix themstruct CounterView: View {
@State private var count = 0
var body: some View {
VStack(spacing: 20) {
Text("Count: \(count)")
.font(.largeTitle)
Button("Increment") {
count += 1
}
.buttonStyle(.borderedProminent)
}
.accessibilityElement(children: .combine)
.accessibilityLabel("Counter at \(count)")
.accessibilityHint("Double tap the button to increment")
}
}
@Observable
class TaskListViewModel {
var tasks: [Task] = []
var isLoading = false
var errorMessage: String?
@MainActor
func loadTasks() async {
isLoading = true
defer { isLoading = false }
do {
tasks = try await TaskService.shared.fetchTasks()
} catch {
errorMessage = error.localizedDescription
}
}
}
struct TaskListView: View {
@State private var viewModel = TaskListViewModel()
var body: some View {
NavigationStack {
Group {
if viewModel.isLoading {
ProgressView()
} else {
List(viewModel.tasks) { task in
TaskRow(task: task)
}
}
}
.navigationTitle("Tasks")
}
.task {
await viewModel.loadTasks()
}
}
}
enum Route: Hashable {
case detail(Item)
case settings
case profile(userId: String)
}
struct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
List(items) { item in
Button(item.name) {
path.append(Route.detail(item))
}
}
.navigationDestination(for: Route.self) { route in
switch route {
case .detail(let item):
DetailView(item: item)
case .settings:
SettingsView()
case .profile(let userId):
ProfileView(userId: userId)
}
}
}
}
// Programmatic navigation
func navigateToSettings() {
path.append(Route.settings)
}
func popToRoot() {
path.removeLast(path.count)
}
}
This skill activates automatically when working with Swift/SwiftUI files or when the user mentions iOS/macOS/SwiftUI development.
Explicit invocation:
/swift-swiftui help me refactor this view to use @Observable
/swift-swiftui create a settings screen with navigation
/swift-swiftui fix state management in this component
npx skills add ojowwalker77/Swift & SwiftUI Expert下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer