skills/swiftui/SKILL.md
SwiftUI development — SPM+XcodeGen project setup, global state tree pattern, and SwiftUITap agent SDK.
npx skillsauth add hayeah/dotfiles swiftuiInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
TLDR: Push all code into SPM packages. XcodeGen only generates a thin .xcodeproj shell for the app target, signing, and assets. App/Sources/ should be < 100 lines.
MyApp/
├── project.yml ← XcodeGen: app shell only
├── App/Sources/ ← @main + root wiring (tiny)
├── App/Resources/ ← app icon, launch screen
├── Packages/ ← ALL code lives here
│ ├── Core/ ← shared models, utilities
│ ├── Features/ ← multi-target: one per feature
│ └── DesignSystem/ ← shared UI components
└── .gitignore ← *.xcodeproj, .build/
New project setup:
mkdir MyApp && cd MyApp
# Create Packages/Core/Package.swift, Packages/Features/Package.swift, etc.
# Create project.yml (only declares local packages + app target)
xcodegen generate
echo -e "*.xcodeproj\n.build/" >> .gitignore
Key rules:
Package.swiftproject.yml — only packages App/Sources/ directly importsplatforms: [.iOS(.v17), .macOS(.v14)] in Package.swift, supportedDestinations: [iOS, macOS] in project.ymlFull reference: XcodeGen + SPM-First Guide
TLDR: One @Observable class (AppState) holds the entire app state. Child domains get their own @Observable classes. All views bind to paths in the tree. No scattered stores, no ViewModels per screen.
#if DEBUG
@SwiftUITap
#endif
@Observable
final class AppState {
var library: LibraryState = LibraryState()
var sessions: [ReadingSession] = []
var __doc__: String {
"""
AppState — root state tree.
library.searchQuery (String), library.books ([BookEntry])
sessions.N.currentChapterIndex (Int)
Methods: openBook(bookID:chapter:), closeSession(sessionID:)
"""
}
func openBook(bookID: String, chapter: Int) -> [String: Any]? { ... }
}
Key rules:
AppState → child state classes → plain structs for leaf data__doc__ on root class documents the entire tree — no per-class docs@State only for ephemeral view-local state (animation, sheet). App state goes in the treeFile organization:
State/
├── AppState.swift # Root @Observable
├── LibraryState.swift # Domain subtrees
├── ReadingSession.swift
└── Models/ # Plain structs (Codable, Identifiable)
Full reference: SwiftUI Global State Guide
TLDR: Add @SwiftUITap macro to @Observable classes. Agents can then get/set properties and call methods over HTTP via swiftui-tap CLI. Wrap in #if DEBUG for zero production overhead.
# Start the relay server
swiftui-tap server --port 9876
# Read state
swiftui-tap state get __doc__
swiftui-tap state get . # full snapshot
swiftui-tap state get library.searchQuery
# Write state
swiftui-tap state set counter 42
swiftui-tap state set label '"hello"'
# Call methods
swiftui-tap state call addTodo '{"title": "Ship it"}'
# View inspection
swiftui-tap view tree # hierarchy dump
swiftui-tap view screenshot # full app
swiftui-tap view screenshot ContentView.todoList -o list.png # cropped
App wiring:
private let sharedAppState = AppState()
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environment(sharedAppState)
.tapInspectable() // enables view tree + screenshots
.onAppear {
#if DEBUG
SwiftUITap.poll(state: sharedAppState, server: "http://localhost:9876")
#endif
}
}
}
}
Tag views with .tapID("name") for targeted screenshots. IDs auto-prefix with source file name.
Key rules:
_) params are skipped#if DEBUG around @SwiftUITap and SwiftUITap.poll() — strips agent code in releasepoll() in init() — SwiftUI state isn't wired up yet, use .onAppear.windows, .app, .defaults) access NSObject builtins via KVCFull reference: SwiftUITap Guide
tools
Web UI development — Vite+ toolchain setup and browser-based E2E testing workflow.
tools
Tooling and style guide for TypeScript projects.
development
Capture tmux pane content and export as text, HTML, SVG, PNG, or JPG. Use when you need a screenshot or text dump of a tmux pane for sharing, feeding to AI, or archiving terminal state.
testing
Copy-edit text. Fix grammar and/or tidy text into a concise listicle.