npx claudepluginhub ojowwalker77/claude-matrix --plugin matrixWant just this skill?
Then install: npx claudepluginhub u/[userId]/[slug]
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.
This skill is limited to using the following tools:
references/accessibility.mdreferences/anti-patterns.mdreferences/architecture.mdreferences/concurrency.mdreferences/navigation.mdreferences/performance.mdreferences/state-management.mdreferences/swiftdata.mdreferences/testing.mdSwift & SwiftUI Expert
Expert-level guidance for Swift and SwiftUI development with modern patterns, iOS 17+/macOS 14+ best practices, and production-ready code generation.
Core Principles
When writing Swift/SwiftUI code, ALWAYS follow these principles:
1. Modern State Management (iOS 17+)
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)
}
}
2. Property Wrapper Decision Tree
| 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 |
3. Architecture Patterns
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.
Anti-Patterns to AVOID
State Management
// 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
}
}
Performance
// 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
}
}
Navigation
// WRONG: Using deprecated NavigationView
NavigationView {
List { ... }
}
// RIGHT: Use NavigationStack (iOS 16+)
NavigationStack {
List { ... }
}
Code Generation Guidelines
When generating Swift/SwiftUI code:
- Target iOS 17+/macOS 14+ unless user specifies otherwise
- Use
@Observableinstead ofObservableObject/@Published - Use NavigationStack instead of NavigationView
- Use
.taskinstead of.onAppearfor async work - Use Swift concurrency (async/await) instead of Combine for new code
- Make @State private always
- Prefer value types (structs, enums) over classes
- Use optionals correctly - never force unwrap without safety
- Handle errors explicitly - use do-catch, not try?
- Add accessibility - labels, hints, traits
Reference Documents
For 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 them
Examples
Basic View with State
struct 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")
}
}
ViewModel with @Observable
@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()
}
}
}
Navigation with Type-Safe Routes
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)
}
}
Usage
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
Similar Skills
Expert guidance for Next.js Cache Components and Partial Prerendering (PPR). **PROACTIVE ACTIVATION**: Use this skill automatically when working in Next.js projects that have `cacheComponents: true` in their next.config.ts/next.config.js. When this config is detected, proactively apply Cache Components patterns and best practices to all React Server Component implementations. **DETECTION**: At the start of a session in a Next.js project, check for `cacheComponents: true` in next.config. If enabled, this skill's patterns should guide all component authoring, data fetching, and caching decisions. **USE CASES**: Implementing 'use cache' directive, configuring cache lifetimes with cacheLife(), tagging cached data with cacheTag(), invalidating caches with updateTag()/revalidateTag(), optimizing static vs dynamic content boundaries, debugging cache issues, and reviewing Cache Component implementations.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.