**Status**: Production Ready ✅
Creates modern SwiftUI settings interfaces using Swift 6.0 @Observable macro.
npx claudepluginhub secondsky/claude-skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/basic-settings-template.swiftassets/custom-style-template.swiftassets/demo-app-template.swiftassets/modular-settings-template.swiftreferences/advanced-patterns.mdreferences/api-reference.mdreferences/performance-edge-cases.mdreferences/search-implementation.mdreferences/styling-guide.mdStatus: Production Ready ✅ Last Updated: 2025-11-23 Dependencies: None (standalone Swift package) Latest Version: SettingsKit 1.0.0+
Minimum Requirements:
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+.
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:
import SwiftUI
import SettingsKit
@Observable
class AppSettings {
var notificationsEnabled = true
var darkMode = false
var username = "Guest"
var fontSize: Double = 14.0
}
CRITICAL:
@Observable macro (not @Published or ObservableObject)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:
@Bindable wrapper to create bindings from @Observable modelsettingsBody returns SettingsContent (not View)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:
Add SettingsKit via Swift Package Manager in Xcode:
https://github.com/aeastr/SettingsKit.gitKey Points:
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:
@Observable macro (Swift 6.0+) for modern observationImplement 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.settingsTags([...]) for enhanced search discoverabilityChoose 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 navigationSettingsStyle protocol✅ 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 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
This skill prevents 5 documented issues:
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
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
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
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
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
// 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:
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
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
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
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
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
No scripts included - SettingsKit is a pure Swift package with no build scripts needed.
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 hierarchiesWhen to load: See "When to Load References" section above for specific scenarios
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)Required:
Optional:
{
"dependencies": {
"SettingsKit": "1.0.0"
}
}
Platform Requirements:
This skill is based on the official SettingsKit repository and examples:
Solution: Import SettingsKit at top of file: import SettingsKit. Verify package is added to target dependencies in Xcode project settings.
Solution: Ensure settings model is in SwiftUI environment (.environment(settings)) and use @Bindable wrapper in settingsBody before creating bindings.
Solution: Verify using SettingsGroup (not raw NavigationLink), and not wrapping SettingsContainer in custom NavigationStack/NavigationSplitView.
Solution: Add .settingsTags([...]) to groups with relevant keywords. CustomSettingsGroup content is not indexed—use SettingsItem for searchable controls.
Use this checklist to verify your setup:
Questions? Issues?
references/api-reference.md for detailed API documentationreferences/advanced-patterns.md for complex scenariosExpert guidance for Next.js Cache Components and Partial Prerendering (PPR). **PROACTIVE ACTIVATION**: Use this skill automatically when working in Next.js projects that have `cacheComponents: true` in their next.config.ts/next.config.js. When this config is detected, proactively apply Cache Components patterns and best practices to all React Server Component implementations. **DETECTION**: At the start of a session in a Next.js project, check for `cacheComponents: true` in next.config. If enabled, this skill's patterns should guide all component authoring, data fetching, and caching decisions. **USE CASES**: Implementing 'use cache' directive, configuring cache lifetimes with cacheLife(), tagging cached data with cacheTag(), invalidating caches with updateTag()/revalidateTag(), optimizing static vs dynamic content boundaries, debugging cache issues, and reviewing Cache Component implementations.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.