{"id":20650022,"url":"https://github.com/kc-2001ms/swiftstorage","last_synced_at":"2026-04-21T19:33:21.065Z","repository":{"id":248073163,"uuid":"826579800","full_name":"KC-2001MS/SwiftStorage","owner":"KC-2001MS","description":"SwiftStorage is an easy way to persist data without Key Value. ","archived":false,"fork":false,"pushed_at":"2024-12-11T19:41:33.000Z","size":12385,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-17T18:01:50.835Z","etag":null,"topics":["macro","macros","observation","swift","swiftui","userdefaults"],"latest_commit_sha":null,"homepage":"https://iroiro.dev/SwiftStorage/documentation/swiftstorage/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KC-2001MS.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-10T01:47:24.000Z","updated_at":"2024-12-11T19:41:37.000Z","dependencies_parsed_at":"2024-12-06T06:30:12.799Z","dependency_job_id":null,"html_url":"https://github.com/KC-2001MS/SwiftStorage","commit_stats":null,"previous_names":["kc-2001ms/swiftstorage"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KC-2001MS%2FSwiftStorage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KC-2001MS%2FSwiftStorage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KC-2001MS%2FSwiftStorage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KC-2001MS%2FSwiftStorage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KC-2001MS","download_url":"https://codeload.github.com/KC-2001MS/SwiftStorage/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242750755,"owners_count":20179254,"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":["macro","macros","observation","swift","swiftui","userdefaults"],"created_at":"2024-11-16T17:17:38.487Z","updated_at":"2026-04-21T19:33:21.052Z","avatar_url":"https://github.com/KC-2001MS.png","language":"Swift","funding_links":["https://www.buymeacoffee.com/iroiro","https://paypal.me/iroiroWork?country.x=JP\u0026locale.x=ja_JP"],"categories":[],"sub_categories":[],"readme":"# SwiftStorage\nSwiftStorage is an easy way to persist data without Key Value. And it is designed to integrate seamlessly with SwiftUI.\n\n## Features\n- [x] Support for Observation framework\n- [x] Can be disabled on a per-property basis\n- [x] Observation-only properties (no persistence)\n- [x] Save in UserDefaults\n- [x] Save in Key Value Store (iCloud)\n- [x] Custom UserDefaults suites (e.g., App Groups)\n- [x] Specify keys individually\n- [x] Key hashing with [Hashify](https://github.com/KC-2001MS/Hashify) for security\n- [x] Key Value Store and UserDefaults together\n- [x] Dynamic storage type via variables\n\n## Usage\n### Basic Framework Usage\nBasically, the usage is the same as the Observable macro in the Observation framework.\nUse the Storage macro instead of the Observable macro. This change alone allows you to store properties permanently.\n\n#### Model Definition\n```swift\nimport SwiftStorage\n\n@Storage\nfinal class SwiftStorageModel {\n    var storedValue: Bool\n\n    init() {\n        self.storedValue = false\n    }\n}\n```\n\n#### Use with SwiftUI\n```swift\nimport SwiftUI\n\nstruct SwiftStorageView: View {\n    @State private var swiftStorage = SwiftStorageModel()\n\n    var body: some View {\n        NavigationStack {\n            Form {\n                Toggle(\"Bool\", isOn: $swiftStorage.storedValue)\n            }\n        }\n    }\n}\n```\n\n### Property Macros\nEach property in a `@Storage` class can be annotated with a macro to control its behavior. The following table summarizes the available macros:\n\n| Macro | Persistence | Observation | Description |\n|---|---|---|---|\n| *(none)* | Yes | Yes | Default. Persisted and observed. |\n| `@Attribute` | Yes | Yes | Customize storage key, type, and options. |\n| `@Attribute(.ephemeral)` | No | Yes | Observation tracking only, not persisted. |\n| `@ObservationTracked` | No | Yes | Standard Apple Observation macro. |\n| `@Transient` | No | No | Neither persisted nor observed. |\n| `@ObservationIgnored` | No | No | Standard Apple macro to ignore observation. |\n\n#### Disable Persistence\nIf it is a constant, it will not be saved. Also, if the `@Transient` macro is applied, even if it is a variable, it will not be saved and it will not participate in observation tracking.\n\n```swift\nimport SwiftStorage\n\n@Storage\nfinal class SwiftStorageModel {\n    var storedValue: Bool\n\n    @Transient\n    var temporaryValue: String\n\n    let constantValue: String\n\n    init() {\n        self.storedValue = false\n        self.temporaryValue = \"\"\n        self.constantValue = \"\"\n    }\n}\n```\n\n#### Observation-Only Properties\nUse `@Attribute(.ephemeral)` or `@ObservationTracked` for properties that should trigger SwiftUI view updates but should **not** be persisted to storage. This is useful for transient UI state.\n\n```swift\nimport SwiftStorage\n\n@Storage\nfinal class SwiftStorageModel {\n    var persistedValue: Bool\n\n    @Attribute(.ephemeral)\n    var observedOnly: Bool\n\n    @ObservationTracked\n    var trackedOnly: Bool\n\n    init() {\n        self.persistedValue = false\n        self.observedOnly = false\n        self.trackedOnly = false\n    }\n}\n```\n\nBoth `@Attribute(.ephemeral)` (provided by SwiftStorage, inspired by SwiftData's `Schema.Attribute.Option.ephemeral`) and `@ObservationTracked` (provided by Apple's Observation framework) are supported.\n\n### Customize Keys\nUse `@Attribute` to specify a custom storage key for a property.\n\n```swift\nimport SwiftStorage\n\n@Storage\nfinal class SwiftStorageModel {\n    @Attribute(key: \"MyCustomKey\")\n    var customKeyValue: Bool\n\n    init() {\n        self.customKeyValue = false\n    }\n}\n```\n\n### Key Hashing\nBy default, all storage keys are hashed at compile time using [Hashify](https://github.com/KC-2001MS/Hashify). This means the original key names do not appear in the compiled binary, preventing them from being discovered through disassembly.\n\nTo disable hashing for a specific property (e.g., for debugging or interoperability), use `hashed: false`:\n\n```swift\nimport SwiftStorage\n\n@Storage\nfinal class SwiftStorageModel {\n    // Key is hashed (default, recommended)\n    var secureValue: Bool\n\n    // Key is hashed with a custom key\n    @Attribute(key: \"MyKey\")\n    var hashedCustomKey: Bool\n\n    // Key is NOT hashed (plain text)\n    @Attribute(key: \"PlainKey\", hashed: false)\n    var debugValue: Bool\n\n    init() {\n        self.secureValue = false\n        self.hashedCustomKey = false\n        self.debugValue = false\n    }\n}\n```\n\n### iCloud Key Value Store\nUse `@Storage(type: .cloud)` to store properties in iCloud Key Value Store (`NSUbiquitousKeyValueStore`). Changes sync automatically across devices.\n\n```swift\nimport SwiftStorage\n\n@Storage(type: .cloud)\nfinal class CloudSettings {\n    var syncedValue: Bool\n\n    var syncedName: String\n\n    init() {\n        self.syncedValue = false\n        self.syncedName = \"\"\n    }\n}\n```\n\nWhen another device changes a value, SwiftStorage automatically receives the `didChangeExternallyNotification` and triggers Observation updates. All SwiftUI views that reference the property will refresh automatically.\n\n#### Mixing Local and Cloud Storage\nYou can override the class-level storage type on a per-property basis using `@Attribute(type:)`. This lets you keep some properties local while syncing others via iCloud.\n\n```swift\nimport SwiftStorage\n\n@Storage(type: .cloud)\nfinal class CloudSettings {\n    // Synced via iCloud (inherits class default)\n    var cloudValue: Bool\n\n    // Stored locally only (overrides class default)\n    @Attribute(type: .local, key: \"localOnly\")\n    var localValue: Bool\n\n    init() {\n        self.cloudValue = false\n        self.localValue = false\n    }\n}\n```\n\nThe type resolution order is:\n1. `@Attribute(type:)` on the property (highest priority)\n2. `@Storage(type:)` on the class\n3. `.local` (default)\n\n#### Custom UserDefaults Suite\nUse `.localWith(suite:)` to store properties in a specific UserDefaults suite (e.g., for App Groups shared between app and extensions).\n\n```swift\nimport SwiftStorage\n\n@Storage(type: .localWith(suite: \"group.com.example\"))\nfinal class SharedSettings {\n    var sharedValue: Bool\n\n    init() {\n        self.sharedValue = false\n    }\n}\n```\n\nYou can also override the suite per property:\n\n```swift\n@Storage\nfinal class MixedSettings {\n    var defaultValue: Bool\n\n    @Attribute(type: .localWith(suite: \"group.com.example\"))\n    var sharedValue: Bool\n\n    init() {\n        self.defaultValue = false\n        self.sharedValue = false\n    }\n}\n```\n\n#### Dynamic Storage Type\n`@Attribute(type:)` accepts variables in addition to literals. The storage backend is resolved at runtime via the `StorageBackend` protocol.\n\n```swift\nimport SwiftStorage\n\nlet customType: StorageType = .localWith(suite: \"group.com.example\")\n\n@Storage\nfinal class DynamicSettings {\n    @Attribute(type: customType)\n    var dynamicValue: Bool\n\n    init() {\n        self.dynamicValue = false\n    }\n}\n```\n\n#### View Integration\nNo special property wrapper is needed. Use `@State`, `@Bindable`, and `@Binding` as usual:\n\n```swift\nstruct ParentView: View {\n    @State private var settings = CloudSettings()\n\n    var body: some View {\n        Toggle(\"Synced\", isOn: $settings.cloudValue)\n        ChildView(settings: settings)\n    }\n}\n\nstruct ChildView: View {\n    @Bindable var settings: CloudSettings\n\n    var body: some View {\n        Toggle(\"Also Synced\", isOn: $settings.cloudValue)\n        // Updates from other devices are reflected here automatically\n    }\n}\n```\n\n\u003e **Note:** Your app must have the iCloud Key-Value Store entitlement enabled for cloud storage to work.\n\n## Installation\nYou can add it to your project using the Swift Package Manager To add SwiftStorage to your Xcode project, select File \u003e Add Package Dependancies... and find the repository URL:  \n`https://github.com/KC-2001MS/SwiftStorage.git`.\n\n## Contributions\nSee [CONTRIBUTING.md](https://github.com/KC-2001MS/SwiftStorage/blob/main/CONTRIBUTING.md) if you want to make a contribution.\n\n## Documents\nDocumentation on the SwiftStorage framework can be found [here](https://iroiro.dev/SwiftStorage/documentation/swiftstorage/).\n\n## License\nThis library is released under Apache-2.0 license. See [LICENSE](https://github.com/KC-2001MS/SwiftStorage/blob/main/LICENSE) for details.\n\n## Supporting\nIf you would like to make a donation to this project, please click here. The money you give will be used to improve my programming skills and maintain the application.  \n\u003ca href=\"https://www.buymeacoffee.com/iroiro\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" style=\"height: 60px !important;width: 217px !important;\" \u003e\n\u003c/a\u003e  \n[Pay by PayPal](https://paypal.me/iroiroWork?country.x=JP\u0026locale.x=ja_JP)\n\n## Author\n[Keisuke Chinone](https://github.com/KC-2001MS)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkc-2001ms%2Fswiftstorage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkc-2001ms%2Fswiftstorage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkc-2001ms%2Fswiftstorage/lists"}