edonv/user-default-entries
Usage
.package(url: "https://github.com/edonv/user-default-entries.git", from: "0.0.0").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:
import DefaultEntry
extension UserDefaults {
@DefaultEntry("defaultValue")
var exampleWithDefault: String
@DefaultEntry<String>
var exampleWithoutDefault: String?
@DefaultEntry(123)
var exampleIntWithDefault: Int
@DefaultEntry<Int>(prefixedWith: "customPrefix_")
var exampleIntWithoutDefault: Int?
}or, manually:
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:
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
UserDefaultabletoBinaryIntegerandBinaryFloatingPointtypes. - [ ] Add DocC content from README.
Package Metadata
Repository: edonv/user-default-entries
Default branch: main
README: README.md