Skip to content

Repository files navigation

Settings Icon

Settings - Type-Safe UserDefaults for Swift

Settings FrameworkSwift 6.2PlatformsiOS 17.0+macOS 15.0+SPMLicense

Type-safe, macro-powered Swift package for UserDefaults.

Features

  • Type-safe — Compile-time type checking with generated keys
  • No boilerplate@Settings and @Setting generate accessors and metadata
  • UserDefaults-compatible — Integrates with existing suites without migration
  • Codable-first — Encode any Codable type via JSON or property list
  • Native storage — Stores property list–compatible types directly (String, Int, Bool, Date, Data, URL, Array, Dictionary)
  • Observable — Built-in Combine publishers and AsyncSequence streams
  • Customizable — Namespaced keys and pluggable encoders/decoders

SwiftUI Integration

import Settings
import SwiftUI
// Special mockable Settings container.
// Uses UserDefaults.standard per default.
extensionAppSettingValues{@Settingpublicvarscore:Int=0}structAppSettingsView:View{@AppSetting(\.$score)varscorevarbody:someView{Form{TextField("Enter your score", value: $score, format:.number).textFieldStyle(.roundedBorder).padding()Text("Your score was \(score).")}}}

Use the UserDefaultsStoreMock class in Previews:

#if DEBUGimport SettingsMock
#Preview {AppSettingsView().environment(\.userDefaultsStore,UserDefaultsStoreMock.standard)}#endif

Note: AppSettingValues is a mockable UserDefaults container already declared in the Settings library.

Set a global key prefix:

@mainstructMyApp:App{init(){AppSettingValues.prefix ="myapp_"}varbody:someScene{WindowGroup{MainView()}}}

Note: When the prefix is not customized, Settings library will use the bundle identifier from the main bundle to derive a unique prefix for every key.

Custom Settings Container

You do not need to put the user settings into a type annotated with the @Settings macro. You also can declare or use any regular struct, class or enum as your UserDefaults container:

structAppSettings{@Settingstaticvarusername:String="Guest"}

This will store and read from the setting in Foundation's UserDefaults.standard.

If you require more control, like having key prefixes, using your own UserDefaults suite, or if you want to mock Foundation UserDefaults in Previews or elsewhere, use the @Settings macro:

@Settings(prefix:"app_") // keys prefixed with "app_"
structAppSettings{@Settingstaticvarusername:String="Guest"}

Projected Value ($propertyName)

Access metadata and observe changes:

// Key
print(AppSettings.$username.key) // "app_username"
print(AppSettings.$theme.key) // "colorScheme"
// Reset
AppSettings.$theme.reset()
// Observe (Combine)
AppSettings.$theme.publisher.sink{print("Theme:", $0)}
// Observe (AsyncSequence)
fortryawaitthemeinAppSettings.$theme.stream {print("Theme:", theme)}

Codable

structUserProfile:Codable,Equatable{letname:Stringletage:Int}@Settings(prefix:"app_")structSettings{@Setting(encoding:.json)staticvarprofile:UserProfile=.init(name:"Guest", age:0)}Settings.profile =.init(name:"Alice", age:30)

Optionals

@Settings(prefix:"app_")structSettings{@SettingstaticvarapiKey:String? // no default value!
}Settings.apiKey ="secret123"Settings.apiKey =nil // removes key from UserDefaults

Nested Containers

Support keys within a name space:

structAppSettings{}extensionAppSettings{enumUserSettings{}}extensionAppSettings.UserSettings{@Settingstaticvaremail:String?}print(AppSettings.UserSettings.$email.key) // "UserSettings::email"
AppSettings.UserSettings.email ="alice@example.com"

Concurrency

Use explicit isolation if accessed across actors.

@Settings(prefix:"app_")structSettings{@MainActor@Settingstaticvartheme:String="light"}

Requirements

  • Swift 6.2
  • macOS 10.15+ / iOS 17.0+ / tvOS 17.0+ / watchOS 10.0+

Installation

Swift Package Manager

Add Settings to your Package.swift:

dependencies:[.package(url:"https://github.com/couchdeveloper/Settings.git", from:"0.5.0")]

Or in Xcode:

  1. File → Add Package Dependencies
  2. Enter: https://github.com/couchdeveloper/Settings.git
  3. Select version: 0.5.0 or later

Advanced

// Suite (App Group)
@Settings(prefix:"shared_", suiteName:"group.com.myapp")structSharedSettings{@SettingstaticvarsyncEnabled:Bool=true}
// Custom key
@Setting(name:"custom_key")staticvarvalue:Int=42
// Custom encoders/decoders
@Setting(encoder:JSONEncoder(), decoder:JSONDecoder())staticvarcustomProfile:UserProfile=.init(name:"Custom", age:0)
// Key-path observation
structUser:Codable,Equatable{varname:String; varage:Int}@Settings(prefix:"app_")structSettings{@Setting(encoding:.json)staticvaruser:User=.init(name:"Guest", age:0)}fortryawaitnameinSettings.$user.stream(for: \.name){print("Name:", name)}

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

License

Apache License (v2.0) - See LICENSE file for details

About

Type-safe, macro-powered Swift package for Foundation's UserDefaults.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages