{"id":15055361,"url":"https://github.com/simonnickel/snap-settings-service","last_synced_at":"2025-05-16T08:32:26.840Z","repository":{"id":234507018,"uuid":"786780796","full_name":"simonnickel/snap-settings-service","owner":"simonnickel","description":"A single interface to handle different types of settings.","archived":false,"fork":false,"pushed_at":"2024-10-09T18:13:33.000Z","size":225,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-10-19T22:33:57.562Z","etag":null,"topics":["nsubiquitouskeyvaluestore","settings-storage","settings-sync","swift","swift-ui","swiftui","userdefaults"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simonnickel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-15T09:40:52.000Z","updated_at":"2024-10-09T18:13:39.000Z","dependencies_parsed_at":"2024-06-28T09:33:07.982Z","dependency_job_id":"01c940c0-34be-4c5a-b257-fd165b9b44a0","html_url":"https://github.com/simonnickel/snap-settings-service","commit_stats":{"total_commits":51,"total_committers":2,"mean_commits":25.5,"dds":0.1568627450980392,"last_synced_commit":"e2234e0f6172aa9a0317045ab1f1c0df3d6ee873"},"previous_names":["simonnickel/snap-settings-service"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonnickel%2Fsnap-settings-service","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonnickel%2Fsnap-settings-service/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonnickel%2Fsnap-settings-service/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonnickel%2Fsnap-settings-service/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simonnickel","download_url":"https://codeload.github.com/simonnickel/snap-settings-service/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225415311,"owners_count":17470866,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nsubiquitouskeyvaluestore","settings-storage","settings-sync","swift","swift-ui","swiftui","userdefaults"],"created_at":"2024-09-24T21:41:31.189Z","updated_at":"2025-05-16T08:32:26.831Z","avatar_url":"https://github.com/simonnickel.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- Copy badges from SPI --\u003e\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fsimonnickel%2Fsnap-settings-service%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/simonnickel/snap-settings-service)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fsimonnickel%2Fsnap-settings-service%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/simonnickel/snap-settings-service) \n\n\u003e This package is part of the [SNAP](https://github.com/simonnickel/snap) suite.\n\n# SnapSettingsService\n\nA single interface to handle different types of settings. It stores a `Codable` type for a `String` key, either locally (UserDefaults), synced (NSUbiquitousKeyValueStore) or in a custom store.\n\nThis package provides the `SettingsService` class, `SettingsStore` protocol and helper to define, save and read settings.\n\n[![Documentation][documentation badge]][documentation] \n\n[documentation]: https://swiftpackageindex.com/simonnickel/snap-settings-service/main/documentation/snapsettingsservice\n[documentation badge]: https://img.shields.io/badge/Documentation-DocC-blue\n\n\n## Setup\n\nTo support settings stored in iCloud (`NSUbiquitousKeyValueStore`) you have to add the `iCloud` Capability to the target and enable the `Key-value storage` checkbox.\n\n\n## Demo\n\nThe [demo project](/SnapSettingsServiceDemo) shows an example usage of the SettingsService with settings in different scopes.\n\n\u003cimg src=\"/screenshot.png\" height=\"400\"\u003e\n\n\n## How to use\n\nDefine your settings:\n```\nextension SettingsService.SettingDefinition {\n\tstatic let exampleNumber = SettingsService.Setting\u003cInt\u003e(\"ExampleNumber\", in: .defaults, default: 1)\n}\n```\n\nRead and write the settings:\n```\nlet settings = SettingsService()\nsettings.set(.exampleNumber, to: 2)\nlet number = settings.get(.exampleNumber)\n```\n\nUse the binding, when you need it to update on changes:\n```\nstruct MyView: View {\n\tlet observableValue: SettingsService.Value\u003cInt\u003e = settings.value(.exampleNumber)\n\tvar body: some View {\n\t\tText(\"\\(observableValue.value)\")\n\t\tSomeView(binding: observableValue.binding)\n\t}\n}\n```\n\n\n## Scope\n\nThe `SettingsService` can be configured with a `SettingsStore` object for a `Scope`: .defaults, .ubiquitous, .custom(id:)\n```\nSettingsService.init(defaults: UserDefaults? = .standard, ubiquitous: NSUbiquitousKeyValueStore? = .default, storesForCustomScopes: [Scope : SettingsStore] = [:])\n```\n\n\n### .defaults\n\nStored locally in `UserDefaults`.\n\n// TODO: How to handle privacy requirements.\n\n\n### .ubiquitous\n\nStored in iCloud using `NSUbiquitousKeyValueStore`. \n\nIf user is not logged in, the value is stored locally and a warning is logged. \n\n\u003e 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.\n[NSUbiquitousKeyValueStore Documentation](https://developer.apple.com/documentation/foundation/nsubiquitouskeyvaluestore#)\n\n(see [Setup](#Setup))\n\n\n### .custom(id:)\n\nYou can provide one or multiple custom stores that implement `SettingsStore`.\n\nIf there is no store registered for the scope, a warning is logged. \n\n\n\n## SettingsStore\n\nUserDefaults and NSUbiquitousKeyValueStore are extended to conform to SettingsStore.\n\n\n### Custom\n\nYou can create a custom store by implementing `SettingsStore`. \n\n```\npublic protocol SettingsStore {\n\tfunc get\u003cT\u003e(_ key: SettingsService.Setting\u003cT\u003e) -\u003e Data?\n\tfunc set\u003cT\u003e(_ key: SettingsService.Setting\u003cT\u003e, to data: Data?)\n}\n```\n\n\n## Access Setting\n\n\n### Get \u0026 Set\n\n```\nlet settings = SettingsService()\nsettings.set(.exampleNumber, to: 2)\nlet number = settings.get(.exampleNumber)\n```\n\n### Observable Value\n\n```\nstruct MyView: View {\n\tlet observableValue: SettingsService.Value\u003cInt\u003e = settings.value(.exampleNumber)\n\tvar body: some View {\n\t\tText(\"\\(observableValue.value)\")\n\t\tSomeView(binding: observableValue.binding)\n\t}\n}\n```\n\n### Publisher\n\nThe `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`). \n\n\n\n## Environment\n\nThe `SettingsService` can be used by the provided `EnvironmentKey`.\n\nInject into the `@Environment`: \n```\n@Environment(\\.serviceSettings) private var settings\n```\n\nAccess from `@Environment`:\n```\nView().environment(\\.serviceSettings, settings)\n```\n\n\n## TODO\n\n// TODO: App Groups? Access in Widget?\n// TODO: Handle iCloud not available. It does store locally and logs a warning, but should do something?\n// TODO: Compare with https://github.com/kylehughes/PersistentKeyValueKit\n// TODO: Comoare with https://github.com/sindresorhus/Defaults","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonnickel%2Fsnap-settings-service","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimonnickel%2Fsnap-settings-service","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonnickel%2Fsnap-settings-service/lists"}