Marketplace

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.

$ Installieren

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 @Observable macro (not @Published or 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 @Bindable wrapper to create bindings from @Observable model
  • settingsBody returns SettingsContent (not 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:

  1. File → Add Package Dependencies
  2. Enter repository URL: https://github.com/aeastr/SettingsKit.git
  3. Select "Up to Next Major Version" from "1.0.0"
  4. 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 @Observable macro (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:

  • SettingsGroup creates navigation links (default) or section headers (.inline)
  • SettingsItem wraps 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 SettingsStyle protocol
  • 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.md when implementing advanced SettingsContainer customization, custom SettingsContent types, or need detailed API documentation for all protocols and modifiers

  • Load references/styling-guide.md when customizing settings appearance, implementing custom SettingsStyle, or need platform-specific styling guidance

  • Load references/search-implementation.md when implementing custom search logic, debugging search behavior, or need to understand SettingsKit's search architecture

  • Load references/advanced-patterns.md when building complex nested hierarchies, implementing dynamic settings, working with persistence, or need production-ready architectural patterns

  • Load references/performance-edge-cases.md when 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 modifiers
  • references/styling-guide.md - Comprehensive guide to customizing settings appearance and platform-specific behaviors
  • references/search-implementation.md - Deep dive into search architecture, custom search implementation, and search scoring
  • references/advanced-patterns.md - Production patterns for complex hierarchies, state management, persistence, and testing
  • references/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 implementation
  • assets/custom-style-template.swift - Custom SettingsStyle implementation template (5 examples)
  • assets/modular-settings-template.swift - Multi-file settings organization pattern
  • assets/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


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?

  1. Check references/api-reference.md for detailed API documentation
  2. Review references/advanced-patterns.md for complex scenarios
  3. Verify all steps in the 4-step setup process
  4. Check official repository: https://github.com/Aeastr/SettingsKit
  5. Ensure Swift 6.0+ and iOS 17+ / macOS 14+ requirements met