Unnamed Skill
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.
$ Installer
git clone https://github.com/secondsky/claude-skills /tmp/claude-skills && cp -r /tmp/claude-skills/plugins/swift-settingskit/skills/swift-settingskit ~/.claude/skills/claude-skills// tip: Run this command in your terminal to install the skill
name: swift-settingskit description: 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.
Keywords: SettingsKit, SwiftUI settings, settings interface, preferences UI, searchable settings, settings navigation, SettingsContainer, SettingsGroup, SettingsItem, CustomSettingsGroup, settings tags, settings search, Observable settings, Bindable settings, iOS 17, macOS 14, Swift 6, settings style, sidebar settings, declarative settings, settings hierarchy license: MIT
Swift 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
@Observablemacro (not@PublishedorObservableObject) - 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
@Bindablewrapper to create bindings from @Observable model settingsBodyreturnsSettingsContent(notView)- 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
@Observablemacro (Swift 6.0+) for modern observation - 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:
SettingsGroupcreates navigation links (default) or section headers (.inline)SettingsItemwraps individual controls- Add
.settingsTags([...])for enhanced search discoverability - 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:
.sidebar: NavigationSplitView with selection-based navigation (default).single: Single NavigationStack with push navigation- Custom styles conform to
SettingsStyleprotocol - 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' to expected argument type 'Binding'"
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
references/api-reference.mdwhen implementing advanced SettingsContainer customization, custom SettingsContent types, or need detailed API documentation for all protocols and modifiers -
Load
references/styling-guide.mdwhen customizing settings appearance, implementing custom SettingsStyle, or need platform-specific styling guidance -
Load
references/search-implementation.mdwhen implementing custom search logic, debugging search behavior, or need to understand SettingsKit's search architecture -
Load
references/advanced-patterns.mdwhen building complex nested hierarchies, implementing dynamic settings, working with persistence, or need production-ready architectural patterns -
Load
references/performance-edge-cases.mdwhen building large settings hierarchies (100+ groups or 1000+ items), experiencing performance issues, or need stress testing guidance and optimization strategies
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:
references/api-reference.md- Complete API documentation for all SettingsKit protocols, types, and modifiersreferences/styling-guide.md- Comprehensive guide to customizing settings appearance and platform-specific behaviorsreferences/search-implementation.md- Deep dive into search architecture, custom search implementation, and search scoringreferences/advanced-patterns.md- Production patterns for complex hierarchies, state management, persistence, and testingreferences/performance-edge-cases.md- Performance optimization, stress testing, and handling large hierarchies
When to load: See "When to Load References" section above for specific scenarios
Assets (assets/)
Swift template files for quick setup:
assets/basic-settings-template.swift- Complete minimal settings implementationassets/custom-style-template.swift- Custom SettingsStyle implementation template (5 examples)assets/modular-settings-template.swift- Multi-file settings organization patternassets/demo-app-template.swift- Full demo app matching official SettingsKit demo (25+ properties, all control types, stress tests)
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
references/api-reference.mdfor detailed API documentation - Review
references/advanced-patterns.mdfor complex scenarios - 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
Repository
