Claude-skills swift-settingskit
SettingsKit for SwiftUI settings interfaces (iOS, macOS, watchOS, tvOS, visionOS). Use for settings/preferences screens, searchable settings, nested navigation, @Observable/@Bindable state, or encountering settings update errors, navigation state issues.
git clone https://github.com/secondsky/claude-skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/secondsky/claude-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/swift-settingskit/skills/swift-settingskit" ~/.claude/skills/secondsky-claude-skills-swift-settingskit && rm -rf "$T"
plugins/swift-settingskit/skills/swift-settingskit/SKILL.mdSwift SettingsKit
Status: Production Ready ✅ Last Updated: 2025-11-23 Dependencies: None (standalone Swift package) Latest Version: SettingsKit 1.0.0+
Supported Toolchains
Minimum Requirements:
- Swift: 6.0+ (required for @Observable macro and SettingsKit compilation)
- Xcode: 16.0+ (provides Swift 6.0 toolchain)
- Platforms: iOS 17.0+ / macOS 14.0+ / watchOS 10.0+ / tvOS 17.0+ / visionOS 1.0+
Note: While @Observable was introduced in Swift 5.9, SettingsKit's Package.swift specifies Swift 6.0+ as the minimum toolchain version. All examples in this skill target Swift 6.0+.
Quick Start (5 Minutes)
1. Add SettingsKit Package
Add the Swift package to your project via Xcode:
// File → Add Package Dependencies // Enter: https://github.com/aeastr/SettingsKit.git // Version: 1.0.0 or later
Or via Package.swift:
dependencies: [ .package(url: "https://github.com/aeastr/SettingsKit.git", from: "1.0.0") ]
Why this matters:
- SettingsKit requires iOS 17+ / macOS 14+ for modern SwiftUI features
- Swift 6.0+ is required for @Observable macro support
- Framework is platform-adaptive across all Apple platforms
2. Create Observable Settings Model
import SwiftUI import SettingsKit @Observable class AppSettings { var notificationsEnabled = true var darkMode = false var username = "Guest" var fontSize: Double = 14.0 }
CRITICAL:
- Use
macro (not@Observable
or@Published
)ObservableObject - SettingsKit is designed for Swift's modern observation system
- Settings model must be in SwiftUI environment for binding
3. Implement SettingsContainer Protocol
struct MySettings: SettingsContainer { @Environment(AppSettings.self) var appSettings var settingsBody: some SettingsContent { @Bindable var settings = appSettings SettingsGroup("General", systemImage: "gear") { SettingsItem("Notifications") { Toggle("Enable", isOn: $settings.notificationsEnabled) } SettingsItem("Dark Mode") { Toggle("Enable", isOn: $settings.darkMode) } } SettingsGroup("Profile", systemImage: "person") { SettingsItem("Username") { TextField("Username", text: $settings.username) } SettingsItem("Font Size") { Slider(value: $settings.fontSize, in: 10...24) Text("\(Int(settings.fontSize))pt") } } } }
CRITICAL:
- Must use
wrapper to create bindings from @Observable model@Bindable
returnssettingsBody
(notSettingsContent
)View- Groups appear as navigation links in sidebar style (tappable rows)
4. Add to Your App
import SwiftUI import SettingsKit @main struct MyApp: App { @State private var settings = AppSettings() var body: some Scene { WindowGroup { MySettings() .environment(settings) } } }
Result: Complete settings interface with:
- Automatic navigation (sidebar on iPad/Mac, single column on iPhone)
- Built-in search functionality
- Platform-adaptive presentation
- Reactive state updates
The 4-Step Setup Process
Step 1: Install Package Dependency
Add SettingsKit via Swift Package Manager in Xcode:
- File → Add Package Dependencies
- Enter repository URL:
https://github.com/aeastr/SettingsKit.git - Select "Up to Next Major Version" from "1.0.0"
- Add to your app target
Key Points:
- Requires Xcode 16.0+ for Swift 6.0 support
- Package includes all platforms (iOS, macOS, watchOS, tvOS, visionOS)
- No additional configuration needed
Step 2: Define Settings Data Model
Create an
@Observable class to hold your settings state:
import SwiftUI @Observable class AppSettings { // General settings var notificationsEnabled = true var soundEnabled = true var hapticFeedback = true // Appearance settings var darkMode = false var accentColor: Color = .blue var fontSize: Double = 16.0 // User profile var username = "" var email = "" var profileImageURL: URL? }
Key Points:
- Use
macro (Swift 6.0+) for modern observation@Observable - Initialize all properties with default values
- Keep settings model separate from view logic
- Can include computed properties for derived state
Step 3: Build Settings Hierarchy
Implement
SettingsContainer protocol to define your settings UI:
import SettingsKit struct MySettings: SettingsContainer { @Environment(AppSettings.self) var appSettings var settingsBody: some SettingsContent { @Bindable var settings = appSettings // Navigation group (tappable row) SettingsGroup("General", systemImage: "gear") { SettingsItem("Notifications") { Toggle("Enable", isOn: $settings.notificationsEnabled) } SettingsItem("Sound Effects") { Toggle("Enable", isOn: $settings.soundEnabled) } } .settingsTags(["notifications", "sounds", "alerts"]) // Inline group (section header) SettingsGroup("Quick Settings", .inline) { SettingsItem("Dark Mode") { Toggle("Enable", isOn: $settings.darkMode) } } // Nested navigation SettingsGroup("Profile", systemImage: "person") { SettingsGroup("Account", systemImage: "person.circle") { SettingsItem("Username") { TextField("Username", text: $settings.username) } SettingsItem("Email") { TextField("Email", text: $settings.email) } } } } }
Key Points:
creates navigation links (default) or section headers (SettingsGroup
).inline
wraps individual controlsSettingsItem- Add
for enhanced search discoverability.settingsTags([...]) - Groups can be nested infinitely for deep hierarchies
Step 4: Configure Presentation Style
Choose how settings are displayed:
// Sidebar style (default) - Split view on iPad/Mac MySettings(settings: settings) .settingsStyle(.sidebar) // Single column style - Clean list on all platforms MySettings(settings: settings) .settingsStyle(.single) // Custom style - Full control over appearance MySettings(settings: settings) .settingsStyle(MyCustomStyle())
Key Points:
: NavigationSplitView with selection-based navigation (default).sidebar
: Single NavigationStack with push navigation.single- Custom styles conform to
protocolSettingsStyle - Platform automatically adapts to device context
Critical Rules
Always Do
✅ Use @Observable for settings models - Required for SettingsKit's reactive system ✅ Wrap environment settings with @Bindable - Enables two-way binding in settingsBody ✅ Add searchable tags to important groups - Improves discoverability via
.settingsTags([...])
✅ Keep settings models in SwiftUI environment - Use .environment(settings) on parent view
✅ Use SettingsItem for all interactive controls - Ensures proper search indexing
Never Do
❌ Never use ObservableObject with @Published - SettingsKit requires modern @Observable ❌ Never create bindings without @Bindable wrapper - Will cause compilation errors ❌ Never put heavy computation in settingsBody - Computed on every render, keep lightweight ❌ Never forget to inject settings into environment - Causes runtime crashes ❌ Never use CustomSettingsGroup for simple controls - Bypasses search indexing unnecessarily
Known Issues Prevention
This skill prevents 5 documented issues:
Issue #1: "Cannot convert value of type 'Binding<T>' to expected argument type 'Binding<U>'"
Error: Compilation error when trying to bind to @Observable properties without @Bindable wrapper Source: Swift concurrency migration guide, Observable macro documentation Why It Happens: @Observable models require @Bindable wrapper to create bindings, unlike @Published properties Prevention: Always use
@Bindable var settings = appSettings in settingsBody before creating bindings
Issue #2: Settings UI Not Updating When Model Changes
Error: Toggle switches, sliders, and text fields don't reflect model changes Source: SettingsKit GitHub issues, SwiftUI observation system documentation Why It Happens: Settings model not properly injected into SwiftUI environment, breaking observation Prevention: Use
.environment(settings) on parent view and @Environment(AppSettings.self) in SettingsContainer
Issue #3: Navigation State Conflicts in Sidebar Style
Error: Selecting settings items doesn't navigate, or navigation stack becomes corrupted Source: SettingsKit architecture documentation, NavigationSplitView best practices Why It Happens: Using NavigationLink directly instead of SettingsGroup in sidebar style causes state conflicts Prevention: Always use SettingsGroup for navigation (never raw NavigationLink), let SettingsKit manage navigation state
Issue #4: Custom Groups Not Appearing in Search Results
Error: CustomSettingsGroup content is invisible to search functionality Source: SettingsKit README - "Custom groups are searchable by title/icon/tags but content renders without element indexing" Why It Happens: CustomSettingsGroup bypasses standard indexing for full UI control, only metadata is searchable Prevention: Use regular SettingsGroup/SettingsItem for searchable content, reserve CustomSettingsGroup for complex custom UI
Issue #5: Settings Crashes on macOS with "Nil coalescing" Runtime Error
Error: App crashes when opening settings on macOS with destination-based navigation issues Source: SettingsKit macOS-specific implementation notes Why It Happens: macOS uses destination-based NavigationLink (not selection-based) to prevent control update issues, but requires proper navigation state setup Prevention: Let SettingsKit handle navigation stack creation, don't wrap SettingsContainer in custom NavigationStack on macOS
Configuration Files Reference
Package.swift (Full Example)
// swift-tools-version: 6.0 import PackageDescription let package = Package( name: "MyApp", platforms: [ .iOS(.v17), .macOS(.v14), .watchOS(.v10), .tvOS(.v17), .visionOS(.v1) ], products: [ .executable(name: "MyApp", targets: ["MyApp"]) ], dependencies: [ .package(url: "https://github.com/aeastr/SettingsKit.git", from: "1.0.0") ], targets: [ .executableTarget( name: "MyApp", dependencies: [ .product(name: "SettingsKit", package: "SettingsKit") ] ) ] )
Why these settings:
- Platform versions match SettingsKit minimum requirements (iOS 17+, etc.)
- Swift tools version 6.0+ required for @Observable macro
- SettingsKit is added as package dependency with semantic versioning
Common Patterns
Pattern 1: Modular Settings Groups
Extract complex settings sections into separate SettingsContent types for better organization:
struct NotificationSettings: SettingsContent { @Bindable var settings: AppSettings var body: some SettingsContent { SettingsGroup("Notifications", systemImage: "bell") { SettingsItem("Enable Notifications") { Toggle("Enable", isOn: $settings.notificationsEnabled) } if settings.notificationsEnabled { SettingsItem("Sound") { Toggle("Enable", isOn: $settings.soundEnabled) } SettingsItem("Badge") { Toggle("Show badge", isOn: $settings.badgeEnabled) } } } .settingsTags(["notifications", "alerts", "sounds", "badges"]) } } // Use in main settings: var settingsBody: some SettingsContent { NotificationSettings(settings: settings) AppearanceSettings(settings: settings) PrivacySettings(settings: settings) }
When to use: Complex settings hierarchies with 5+ groups, conditional content, or team collaboration on different sections
Pattern 2: Searchable Custom Developer Tools
Use CustomSettingsGroup for advanced UIs while maintaining searchability:
CustomSettingsGroup("Developer Tools", systemImage: "hammer") { VStack(spacing: 20) { GroupBox("Debug Information") { VStack(alignment: .leading, spacing: 8) { Text("App Version: 1.0.0") Text("Build: 42") Text("Environment: Production") } .frame(maxWidth: .infinity, alignment: .leading) } Button("Clear Cache") { clearCache() } .buttonStyle(.borderedProminent) Button("Export Logs", systemImage: "square.and.arrow.up") { exportLogs() } } .padding() } .settingsTags(["debug", "testing", "logs", "advanced"])
When to use: Complex custom UI that doesn't fit standard SettingsItem pattern, but still needs search discoverability via tags
Pattern 3: Conditional Settings Visibility
Show/hide settings based on feature flags or user permissions:
SettingsGroup("Advanced", systemImage: "gearshape.2") { SettingsItem("Enable Advanced Features") { Toggle("Enable", isOn: $settings.showAdvanced) } if settings.showAdvanced { SettingsItem("Beta Features") { Toggle("Enable", isOn: $settings.betaFeaturesEnabled) } SettingsItem("Developer Mode") { Toggle("Enable", isOn: $settings.developerMode) } } if settings.developerMode { SettingsGroup("Developer Options", systemImage: "wrench.and.screwdriver") { SettingsItem("Verbose Logging") { Toggle("Enable", isOn: $settings.verboseLogging) } } } }
When to use: Feature flags, user permission levels, progressive disclosure of complexity
Pattern 4: All Control Types
SettingsItem supports all standard SwiftUI controls:
SettingsGroup("All Controls", systemImage: "slider.horizontal.3") { // Toggle (Boolean) SettingsItem("Enable Feature") { Toggle("Enable", isOn: $settings.featureEnabled) } // Slider (Continuous value) SettingsItem("Volume") { VStack(alignment: .leading, spacing: 8) { Slider(value: $settings.volume, in: 0...100) Text("\(Int(settings.volume))%") .font(.caption) .foregroundStyle(.secondary) } } // TextField (Text input) SettingsItem("Username") { TextField("Enter username", text: $settings.username) .textFieldStyle(.roundedBorder) } // Picker (Selection) SettingsItem("Theme") { Picker("", selection: $settings.theme) { Text("Light").tag(Theme.light) Text("Dark").tag(Theme.dark) Text("Auto").tag(Theme.auto) } .pickerStyle(.segmented) } // Stepper (Increment/Decrement) SettingsItem("Font Size") { Stepper("\(Int(settings.fontSize)) pt", value: $settings.fontSize, in: 10...24) } // Button (Action) SettingsItem("Reset Settings") { Button("Reset") { resetSettings() } .buttonStyle(.borderedProminent) .tint(.red) } // ColorPicker (Color selection) SettingsItem("Accent Color") { ColorPicker("Choose color", selection: $settings.accentColor) } // DatePicker (Date/Time selection) SettingsItem("Reminder Time") { DatePicker("", selection: $settings.reminderTime, displayedComponents: .hourAndMinute) } }
When to use: Reference for available controls when building settings
When to Load References
Load additional reference files for detailed documentation on specific topics:
-
Load
when implementing advanced SettingsContainer customization, custom SettingsContent types, or need detailed API documentation for all protocols and modifiersreferences/api-reference.md -
Load
when customizing settings appearance, implementing custom SettingsStyle, or need platform-specific styling guidancereferences/styling-guide.md -
Load
when implementing custom search logic, debugging search behavior, or need to understand SettingsKit's search architecturereferences/search-implementation.md -
Load
when building complex nested hierarchies, implementing dynamic settings, working with persistence, or need production-ready architectural patternsreferences/advanced-patterns.md -
Load
when building large settings hierarchies (100+ groups or 1000+ items), experiencing performance issues, or need stress testing guidance and optimization strategiesreferences/performance-edge-cases.md
Using Bundled Resources
Scripts (scripts/)
No scripts included - SettingsKit is a pure Swift package with no build scripts needed.
References (references/)
Detailed documentation files for advanced topics:
- Complete API documentation for all SettingsKit protocols, types, and modifiersreferences/api-reference.md
- Comprehensive guide to customizing settings appearance and platform-specific behaviorsreferences/styling-guide.md
- Deep dive into search architecture, custom search implementation, and search scoringreferences/search-implementation.md
- Production patterns for complex hierarchies, state management, persistence, and testingreferences/advanced-patterns.md
- Performance optimization, stress testing, and handling large hierarchiesreferences/performance-edge-cases.md
When to load: See "When to Load References" section above for specific scenarios
Assets (assets/)
Swift template files for quick setup:
- Complete minimal settings implementationassets/basic-settings-template.swift
- Custom SettingsStyle implementation template (5 examples)assets/custom-style-template.swift
- Multi-file settings organization patternassets/modular-settings-template.swift
- Full demo app matching official SettingsKit demo (25+ properties, all control types, stress tests)assets/demo-app-template.swift
Dependencies
Required:
- Swift 6.0+ (for @Observable macro support)
- iOS 17.0+ / macOS 14.0+ / watchOS 10.0+ / tvOS 17.0+ / visionOS 1.0+
- SwiftUI framework (built-in)
Optional:
- None - SettingsKit is a standalone framework with no external dependencies
Official Documentation
- SettingsKit GitHub: https://github.com/Aeastr/SettingsKit
- Swift Observation: https://developer.apple.com/documentation/observation
- SwiftUI Navigation: https://developer.apple.com/documentation/swiftui/navigation
- Context7 Library ID: N/A (not indexed yet)
Package Versions (Verified 2025-11-23)
{ "dependencies": { "SettingsKit": "1.0.0" } }
Platform Requirements:
- iOS 17.0+
- macOS 14.0+
- watchOS 10.0+
- tvOS 17.0+
- visionOS 1.0+
- Swift 6.0+
- Xcode 16.0+
Production Example
This skill is based on the official SettingsKit repository and examples:
- Repository: https://github.com/Aeastr/SettingsKit
- License: MIT
- Validation: ✅ Tested on iOS 17+, macOS 14+, watchOS 10+, tvOS 17+, visionOS 1+
- Architecture: Hybrid metadata/view/rendering system for searchable + reactive settings
Troubleshooting
Problem: "Cannot find 'SettingsContainer' in scope"
Solution: Import SettingsKit at top of file:
import SettingsKit. Verify package is added to target dependencies in Xcode project settings.
Problem: Settings toggles don't update when model changes
Solution: Ensure settings model is in SwiftUI environment (
.environment(settings)) and use @Bindable wrapper in settingsBody before creating bindings.
Problem: Navigation doesn't work in sidebar style
Solution: Verify using SettingsGroup (not raw NavigationLink), and not wrapping SettingsContainer in custom NavigationStack/NavigationSplitView.
Problem: Search can't find my custom settings
Solution: Add
.settingsTags([...]) to groups with relevant keywords. CustomSettingsGroup content is not indexed—use SettingsItem for searchable controls.
Complete Setup Checklist
Use this checklist to verify your setup:
- SettingsKit package added (1.0.0+) via Swift Package Manager
- Minimum platform versions met (iOS 17+ / macOS 14+ / Swift 6.0+)
- Settings model uses @Observable (not ObservableObject)
- SettingsContainer protocol implemented with settingsBody
- @Bindable wrapper used before creating bindings
- Settings model injected into environment (.environment(settings))
- Settings groups use .settingsTags([...]) for search
- App compiles without errors
- Settings UI displays correctly on target platforms
- Search functionality works (try searching for tagged keywords)
- Settings state persists and updates reactively
Questions? Issues?
- Check
for detailed API documentationreferences/api-reference.md - Review
for complex scenariosreferences/advanced-patterns.md - Verify all steps in the 4-step setup process
- Check official repository: https://github.com/Aeastr/SettingsKit
- Ensure Swift 6.0+ and iOS 17+ / macOS 14+ requirements met