https://github.com/edonv/user-default-entries
SwiftUI property wrapper for type-safe and key-safe use of UserDefaults.
https://github.com/edonv/user-default-entries
propertywrapper swift swiftui userdefaults
Last synced: about 15 hours ago
JSON representation
SwiftUI property wrapper for type-safe and key-safe use of UserDefaults.
- Host: GitHub
- URL: https://github.com/edonv/user-default-entries
- Owner: edonv
- License: mit
- Created: 2026-06-09T16:31:48.000Z (19 days ago)
- Default Branch: main
- Last Pushed: 2026-06-11T14:20:23.000Z (18 days ago)
- Last Synced: 2026-06-17T14:34:20.082Z (12 days ago)
- Topics: propertywrapper, swift, swiftui, userdefaults
- Language: Swift
- Homepage: https://swiftpackageindex.com/edonv/user-default-entries
- Size: 46.9 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# User Default Entries
[](https://swiftpackageindex.com/edonv/user-default-entries)
[](https://swiftpackageindex.com/edonv/user-default-entries)
## Usage
```swift
.package(url: "https://github.com/edonv/user-default-entries.git", from: "0.0.0")
```
```swift
.product(name: "UserDefault", package: "user-default-entries")
```
### Macro vs Manual
To use the `@UserDefault` property wrapper, you need to have added a `get`/`set` property to `UserDefaults` via an extension. These can be defined manually or with the packaged `@DefaultEntry` macro.
Macro:
```swift
import DefaultEntry
extension UserDefaults {
@DefaultEntry(default: "defaultValue")
var exampleWithDefault: String
@DefaultEntry
var exampleWithoutDefault: String?
@DefaultEntry(default: 123)
var exampleIntWithDefault: Int
@DefaultEntry(prefixedWith: "customPrefix_")
var exampleIntWithoutDefault: Int?
}
```
would be equivalent to the following output:
```swift
extension UserDefaults {
var exampleWithDefault: String {
get { String(withKey: "key_exampleWithDefault", in: self) ?? "defaultValue" }
set { newValue.store(in: self, withKey: "key_exampleWithDefault") }
}
var exampleWithoutDefault: String? {
get { String(withKey: "key_exampleWithoutDefault", in: self) }
set { newValue.store(in: self, withKey: "key_exampleWithoutDefault") }
}
var exampleIntWithDefault: Int {
get { Int(withKey: "key_exampleIntWithDefault", in: self) ?? "defaultValue" }
set { newValue.store(in: self, withKey: "key_exampleValue") }
}
var exampleIntWithoutDefault: Int? {
get { Int(withKey: "customPrefix_exampleIntWithoutDefault", in: self) }
set { newValue.store(in: self, withKey: "customPrefix_exampleIntWithoutDefault") }
}
}
```
Then, in a `SwiftUI` view, you can do the following:
```swift
import SwiftUI
import UserDefault
struct ExampleView: View {
@UserDefault(\.exampleWithDefault)
private var exampleWithDefault // implied to be a String
@UserDefault(\.exampleWithoutDefault)
private var exampleWithoutDefault // implied to be an optional String
var body: some View {
VStack {
TextField("Example Field", text: $exampleWithDefault)
Text(exampleWithoutDefault ?? "Value is empty")
}
}
}
```
### `UserDefaultable`
The `@UserDefault` property wrapper relies on both the `@DefaultEntry` macro (or manual entry) and the `UserDefaultable` protocol. Any type used must conform to `UserDefaultable`. If it's a custom type, it must explicitly conform to `UserDefaultable` and one of `RawRepresentable` or `Codable`. See the following section for more details on those.
### RawRepresentable/Codable
`UserDefaultable` supports types that conform to `RawRepresentable` (whose `RawValue` types conform to `UserDefaultable`) and `Codable`, but due to Swift language restrictions, you still have to explicitly add conformance of `UserDefaultable` to your own type. The protocol requirements will be automatically synthesized for you.
## Notes
### NSNumber
Out of the box, `NSNumber` is supported by `UserDefaults`, but due to Swift language restrictions when it comes to adding initializers for a protocol to an existing non-final class, it's not possible to add `UserDefaultable` conformance to `NSNumber`. I'd recommend using `Int`, `Float`, or `Double` instead.
## To-Do's
- [x] Write a macro equivalent to `@Entry`
- `@UserDefaultsEntry(_ key: String, in userDefaults: UserDefaults? = nil)
- [ ] Add conformance of `UserDefaultable` to `BinaryInteger` and `BinaryFloatingPoint` types.
- [ ] Add DocC content from README.