swift-settingskit — quality + safety report

In the Skillier index (secondsky__swift-settingskit) · scanned 2026-06-03 · engine: builtin+triage

B
Quality
88/100
Safety

✓ Clean — no heuristic safety flags surfaced.

Heuristic flags from the builtin scanner, which is known to over-flag (it trips on legitimate env-reading integrations, security skills, and library .eval calls). This is NOT an authoritative malicious verdict — re-scan with SkillSpector for the authoritative result. Run the authoritative scan →

Skillproof quality grade B

📇 This skill is in the Skillier index (curated · deduped · quality-filtered). Install Skillier to route & load it into your AI client.

Quality notes

Skill is large (~5430 tokens)
medium · quality · body
→ Tighten to the essential procedure; move long reference material to linked files.
No explicit trigger / 'when to use'
low · quality · body
→ Add a 'When to use' section or 'Use this when …' line listing trigger conditions.
No explicit output format / contract
low · quality · body
→ State the expected output format (structure, sections, or schema).

About this 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.

📄 Read the SKILL.md
---
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."

metadata:
  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:

```swift
// File → Add Package Dependencies
// Enter: https://github.com/aeastr/SettingsKit.git
// Version: 1.0.0 or later
```

**Or via Package.swift:**
```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

```swift
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

```swift
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

```swift
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:

```swift
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:

```swift
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:

```swift
// 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<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
// 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

… (truncated)
Scan or optimize your own skill →

Want a live grade + an embeddable README badge? Run your skill through the free scanner.

Graded independently by Skillproof — nothing to sell the author. Quality is mechanical + corpus-grounded; safety flags are heuristic (builtin+triage), not a malicious verdict.