Contents

simonnickel/snap-settings-service

> This package is part of the [SNAP](https://github.com/simonnickel/snap) suite.

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

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

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

How to use

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:

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.

// TODO: How to handle privacy requirements.

.ubiquitous

Stored in iCloud using NSUbiquitousKeyValueStore.

If user is not logged in, the value is stored locally and a warning is logged.

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<T>(_ key: SettingsService.Setting<T>) -> Data?
	func set<T>(_ key: SettingsService.Setting<T>, to data: Data?)
}

Access Setting

Get & Set

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

Observable Value

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 used by the provided EnvironmentKey.

Inject into the @Environment:

@Environment(\.serviceSettings) private var settings

Access from @Environment:

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

TODO

// TODO: App Groups? Access in Widget? // TODO: Handle iCloud not available. It does store locally and logs a warning, but should do something? // TODO: Compare with https://github.com/kylehughes/PersistentKeyValueKit // TODO: Comoare with https://github.com/sindresorhus/Defaults

Package Metadata

Repository: simonnickel/snap-settings-service

Default branch: main

README: README.md