Contents

simonnickel/snap-settings-service

This package is part of the SNAP suite.

Requirements

  • iOS 18+, macOS 15+
  • Swift 6.3
  • Dependencies: snap-core

Setup

To support settings stored in iCloud (NSUbiquitousKeyValueStore) you have to add the iCloud Capability to the target and enable the Key-value storage checkbox.

Demo project

The demo project shows an example usage of the SettingsService with settings in different scopes.

<img src="/screenshot.png" height="400">

Usage

Define your settings:

extension SettingsService.SettingDefinition {
	static let exampleNumber = SettingsService.Setting<Int>("ExampleNumber", in: .defaults, default: 1)
}

Read and write the settings:

let settings = SettingsService()
settings.set(.exampleNumber, to: 2)
let number = settings.get(.exampleNumber)

Use the binding, when you need it to update on changes (value(_:) is @MainActor):

struct MyView: View {
	let observableValue: SettingsService.Value<Int> = settings.value(.exampleNumber)
	var body: some View {
		Text("\(observableValue.value)")
		SomeView(binding: observableValue.binding)
	}
}

Scope

The SettingsService can be configured with a SettingsStore object for a Scope: .defaults, .ubiquitous, .custom(id:)

SettingsService.init(defaults: UserDefaults? = .standard, ubiquitous: NSUbiquitousKeyValueStore? = .default, storesForCustomScopes: [Scope : SettingsStore] = [:])

.defaults

Stored locally in UserDefaults.

How to handle privacy requirements is open, see TODO-5.

.ubiquitous

Stored in iCloud using NSUbiquitousKeyValueStore.

If iCloud is unavailable, NSUbiquitousKeyValueStore stores values locally as a fallback — SettingsService is not involved in that fallback. A warning is only logged if no store is registered for the scope at all. Handling this case is open, see TODO-2.

To use NSUbiquitousKeyValueStore, you must distribute your app through the App Store or Mac App Store, and you must request the com.apple.developer.ubiquity-kvstore-identifier entitlement in your Xcode project.

NSUbiquitousKeyValueStore Documentation

(see Setup)

.custom(id:)

You can provide one or multiple custom stores that implement SettingsStore.

If there is no store registered for the scope, a warning is logged.

SettingsStore

UserDefaults and NSUbiquitousKeyValueStore are extended to conform to SettingsStore.

Custom

You can create a custom store by implementing SettingsStore.

public protocol SettingsStore {
	func get(_ key: SettingsService.StorageKey) -> Data?
	func set(_ key: SettingsService.StorageKey, to data: Data?)
}

Access Setting

Get & Set

let settings = SettingsService()
settings.set(.exampleNumber, to: 2)
let number = settings.get(.exampleNumber)

Observable Value

value(_:) is @MainActor.

struct MyView: View {
	let observableValue: SettingsService.Value<Int> = settings.value(.exampleNumber)
	var body: some View {
		Text("\(observableValue.value)")
		SomeView(binding: observableValue.binding)
	}
}

Publisher

The SettingsService provides a Combine publisher to receive updated values. If the setting is stored in the .ubiquitous scope, the publisher is updated on remote changes (NSUbiquitousKeyValueStore.didChangeExternallyNotification).

Environment

The SettingsService can be injected via a SwiftUI environment key (@Entry on EnvironmentValues).

Inject into the @Environment:

View().environment(\.serviceSettings, settings)

Read from @Environment:

@Environment(\.serviceSettings) private var settings

Package Metadata

Repository: simonnickel/snap-settings-service

Default branch: main

README: README.md