skills/port-swiftui-to-appkit/SKILL.md
Analyze a macOS SwiftUI app and plan its conversion to native AppKit. Triggers on 'port to AppKit', 'convert SwiftUI to AppKit', 'remove SwiftUI', or /port-swiftui-to-appkit.
npx skillsauth add agentic-cookbook/tools port-swiftui-to-appkitInstall 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.
First action: If $ARGUMENTS is --version, print port-swiftui-to-appkit v1.1.0 and stop — do not run the skill.
Otherwise, print port-swiftui-to-appkit v1.1.0 as the first line of output, then proceed.
Version check: Read ${CLAUDE_SKILL_DIR}/SKILL.md from disk and extract the version: field from frontmatter. Compare to this skill's version (1.1.0). If they differ, print:
⚠ This skill is running v1.1.0 but vA.B.C is installed. Restart the session to use the latest version.
Continue running — do not stop.
Analyzes a macOS SwiftUI application and produces a comprehensive conversion report, then hands off to /plan-roadmap to create an implementation roadmap. The skill is read-only — it never modifies your code. The actual conversion happens later via /implement-roadmap.
/port-swiftui-to-appkit
Run from the root of a macOS SwiftUI project.
Before doing anything else, verify all prerequisites. Stop immediately if any fails.
macOS project. Check for .xcodeproj, Package.swift, or project.yml. If none found, stop: "This does not appear to be an Xcode or Swift Package project."
SwiftUI usage. Grep for import SwiftUI across all .swift files. If zero results, stop: "No SwiftUI imports found — nothing to convert."
macOS target. Check for import AppKit or NSApplication or macOS in project configuration. If the project only imports UIKit or targets iOS/tvOS/watchOS, stop: "This skill only supports macOS apps. iOS/tvOS/watchOS conversions are not supported."
Git clean. Run git status --porcelain. If output is non-empty, stop: "Working tree has uncommitted changes. Commit or stash before running this skill."
Load reference files. Read all three:
${CLAUDE_SKILL_DIR}/references/pattern-mappings.md${CLAUDE_SKILL_DIR}/references/conversion-checklist.md${CLAUDE_SKILL_DIR}/references/common-pitfalls.mdIf any file is missing, stop and inform the user.
This phase is entirely read-only. No files are modified.
Glob for **/*.swift in the project source directories. For each file, check whether it contains import SwiftUI. Categorize every file into one of three buckets:
| Bucket | Criteria |
|--------|----------|
| SwiftUI-dependent | Contains import SwiftUI and is primarily a view (some View, some Scene, NSViewRepresentable) |
| Hybrid | Contains import SwiftUI but primarily model/logic code (only uses SwiftUI for Color, @AppStorage, etc.) |
| Pure logic | No import SwiftUI — unchanged by conversion |
Count files in each bucket.
Scan the codebase for each pattern. Record whether it exists and which file(s) contain it:
| Pattern | Search For | Conversion Impact |
|---------|-----------|-------------------|
| SwiftUI App lifecycle | @main struct with : App | Required — Phase 2 |
| Existing AppDelegate | @NSApplicationDelegateAdaptor or NSApplicationDelegate | Expand existing vs create new |
| Window groups | WindowGroup | One NSWindowController per group |
| Settings scene | Settings { or Settings( as a scene | Phase 4 |
| Split views | HSplitView, NavigationSplitView | NSSplitViewController in Phase 2 |
| List views | List( with model binding | NSTableView in Phase 3 |
| Sidebar style | .listStyle(.sidebar) | Source list style |
| NSViewRepresentable | NSViewRepresentable | Delete — direct NSView management |
| Window accessor hacks | WindowAccessor, NSViewRepresentable used for window config | Delete entirely |
| Toolbar items | .toolbar { | NSToolbar in Phase 2 |
| Multiple window types | Multiple WindowGroup(id: | One controller per type |
| Focused values | @FocusedObject, @FocusedValue | Route through NSApp.mainWindow |
| Environment objects | @EnvironmentObject, @Environment( | Direct property injection |
| Geometry readers | GeometryReader | Manual frame calculation |
| Task modifier | .task { | Task {} in viewDidLoad() |
| Custom layouts | Layout protocol conformance | Manual NSView layout |
Find every @AppStorage("key") usage. For each, record:
These become direct UserDefaults.standard reads/writes in the converted code.
Find every .keyboardShortcut( usage. For each, record:
These must all appear in the AppKit NSMenu built in applicationWillFinishLaunching.
Check Package.swift, Package.resolved, or project.yml for package dependencies. For each dependency, check whether it is likely SwiftUI-based by examining the package name and its product names in the manifest. Do NOT grep through downloaded package sources (.build/, SPM cache) — this is unbounded. Instead, flag any dependency whose name or description suggests SwiftUI views (e.g., contains "SwiftUI", "View", "UI") for manual review by the user.
Check for patterns that need case-by-case discussion:
@Environment(\. — custom environment values need manual threadingGeometryReader — needs manual frame calculationLayout conformances — needs manual NSView layout@AppStorage with non-standard types — may need custom UserDefaults encodingPreferenceKey — needs a different communication pattern@FetchRequest — needs manual Core Data fetch in view controllerFor each one found, note the file and line for discussion with the user.
Using the analysis from Phase 1 and the pattern mappings reference, generate the conversion plan.
| Phase | Required When |
|-------|--------------|
| Phase 1: Model Layer Cleanup | Always (any hybrid files exist) |
| Phase 2: App Lifecycle & Windows | Always (SwiftUI App struct exists) |
| Phase 3: Sidebar / List Views | List views detected |
| Phase 4: Settings Window | Settings scene detected |
| Phase 5: Final Cleanup | Always |
Mark each phase as applicable or skipped.
For every Swift file in the project, determine its fate:
| Action | Criteria |
|--------|----------|
| DELETE | Pure SwiftUI view with no reusable logic |
| CREATE | New AppKit controller replacing a deleted view |
| MODIFY | Hybrid file needing import SwiftUI removal and type changes |
| UNCHANGED | Pure logic file with no SwiftUI dependency |
For each DELETE, identify what CREATE replaces it. Use the file mapping patterns from references/pattern-mappings.md:
For each applicable phase, estimate complexity as S/M/L:
Display the full analysis in a structured format:
=== SWIFTUI → APPKIT CONVERSION ANALYSIS ===
Project: <name>
Swift files: <n> total
SwiftUI-dependent: <n> (to delete/replace)
Hybrid: <n> (to modify)
Pure logic: <n> (unchanged)
Architecture Detected:
[x/] SwiftUI App lifecycle
[x/] Existing AppDelegate
[x/] Split views
[x/] List views (<n> files)
[x/] Settings scene
[x/] NSViewRepresentable wrappers (<n>)
[x/] Window accessor hacks
[x/] Toolbar items
[x/] Multiple window types
[x/] Focused values
...
@AppStorage keys: <n>
Keyboard shortcuts: <n>
Third-party SwiftUI deps: <list or "none">
Advanced patterns needing discussion:
<list or "none">
Applicable phases: <list>
Skipped phases: <list with reasons>
FILE PLAN:
DELETE (<n> files):
- <file> → replaced by <new file>
...
CREATE (<n> files):
- <file> (replaces <old file>)
...
MODIFY (<n> files):
- <file> (remove import SwiftUI, Color → NSColor)
...
UNCHANGED (<n> files):
- <file>
...
COMMON PITFALLS TO WATCH:
<list applicable pitfalls from references/common-pitfalls.md>
Then ask: "Does this analysis look correct? Any files miscategorized or patterns I missed?"
If the user identifies corrections, update the analysis and re-present.
If advanced patterns were flagged in Step 1.6, discuss each one with the user now to determine the conversion approach before proceeding.
Once the user confirms the analysis, invoke /plan-roadmap using the Skill tool.
Before invoking, print:
Analysis confirmed. Handing off to /plan-roadmap to create the implementation roadmap.
The roadmap will break this conversion into implementation steps following
the 5-phase process. Each step will map to a single PR with build verification.
When /plan-roadmap asks "What feature would you like to plan?", provide all of the following context from your analysis:
references/common-pitfalls.mdgrep -r "import SwiftUI" returns zero at the endEach roadmap step should correspond to a logical unit within a phase:
Every step must include:
references/pattern-mappings.md that applyreferences/common-pitfalls.md/plan-roadmap until the user confirms the analysis..xcodeproj, XcodeGen, or SPM and report it. Do not assume.references/pattern-mappings.md, tell the user explicitly rather than guessing.The skill completed successfully when all of these are true:
/plan-roadmap was invoked with the full analysis context/plan-roadmap produced a roadmap with build verification in every steptesting
Validate cookbook integrity — frontmatter, cross-references, indexes, skills, rules, file placement. Run from cookbook or consuming project.
tools
Remove the agentic cookbook from your project. Removes rules, state files, CLAUDE.md section, and optionally global plugins and status line.
tools
--- name: plan-cookbook-recipe version: 2.3.0 description: Interactively design a new cookbook recipe through guided discussion disable-model-invocation: true context: fork allowed-tools: Read, Glob, Grep, Agent, Write, Edit, AskUserQuestion, Bash(git *), Bash(mkdir *), Bash(ls *) argument-hint: [recipe-name] [--version] --- # Plan Agentic Cookbook Recipe v2.3.0 ## Startup **First action**: If `$ARGUMENTS` is `--version`, print `plan-cookbook-recipe v2.3.0` and stop. Otherwise, print `plan-c
development
Optimize Claude Code rules by consolidating into a single efficient file. Triggers on 'optimize rules', 'optimize my rules', or /optimize-rules.