Persisted Property Wrapper

Persisted Property Wrapper is a Swift library to enable extremely easy persistance of variables in the UserDefaults
database on Apple platforms.
To use Persisted Property Wrapper you simply annotate a variable as being @Persisted
. It supports the standard UserDefaults
types (Int
, String
, Bool
, Date
and more), along with RawRepresentable
enums where the RawValue
is storable in UserDefaults
, as well as any type which conforms to Codable
or NSSecureCoding
. Plus of course any Optional
wrapper of any of these types. The type-validity is checked at compile-time: attempting to use on any variables of a non-supported type will cause a compile-time error.
Usage
Stick a @Persisted
attribute on your variable.
The first argument of the initializer is the string key under which the value will be stored in UserDefaults
. If the type is non-Optional, you must also supply a defaultValue
, which will be used when there is no value stored in UserDefaults
.
For example:
@Persisted("UserSetting1", defaultValue: 42)
var someUserSetting: Int
@Persisted("UserSetting2") // defaultValue not necessary since Int? is an Optional type
var someOtherUserSetting: Int?
Storing Enums
Want to store an enum value? If the enum has a backing type which is supported for storage in UserDefaults
, then those can also be marked as @Persisted
, and the actual value stored in UserDefaults
will be the enum’s raw value. For example:
enum AppTheme: Int {
case brightRed
case vibrantOrange
case plainBlue
}
struct ThemeSettings {
// Stores the underlying integer backing the selected AppTheme
@Persisted("theme", defaultValue: .plainBlue)
var selectedTheme: AppTheme
}
Storing Codable types
Any codable type can be Persisted too; this will store in UserDefaults the JSON-encoded representation of the variable. For example:
struct AppSettings: Codable {
var welcomeMessage = "Hello world!"
var isSpecialModeEnabled = false
var launchCount = 0
@Persisted(encodedDataKey: "appSettings", defaultValue: .init())
static var current: AppSettings
}
// Example usage: this will update the value of the stored AppSettings
func appDidLaunch() {
AppSettings.current.launchCount += 1
}
Note that the argument label encodedDataKey
must be used. This is required to remove ambiguity about which storage method is used, since UserDefaults
-storable types can be Codable
too.
For example, the following two variables are stored via different mechanisms:
// Stores the integer in UserDefaults
@Persisted("storedAsInteger", defaultValue: 10)
var storedAsInteger: Int
// Store the data of a JSON-encoded representation of the value. Don't use on iOS 12!
@Persisted(encodedDataKey: "storedAsData", defaultValue: 10)
var storedAsData: Int
Note: on iOS 12, using the encodedDataKey
initializer with a value which would encode to a JSON fragment (e.g. Int
, String
, Bool
, etc) will cause a crash. This is due to a bug in the Swift runtime shipped prior to iOS 13. Using encodedDataKey
has no benefit in these cases anyway.
Storing types which implement NSCoding
Any NSObject
which conforms to NSSecureCoding
can be Persisted too; this will store in UserDefaults the encoded representation of the object obtained from NSKeyedArchiver
. For example:
class CloudKitSyncManager {
@Persisted(archivedDataKey: "ckServerChangeToken")
var changeToken: CKServerChangeToken?
}
Note that the argument label archivedDataKey
must be used. As above, this is required to remove ambiguity about which storage method is used.
Note: this storage mechanism is only supported on iOS 11 and up.
Alternative Storage
By default, a @Persisted
property is stored in the UserDefaults.standard
database; to store values in a different location, pass the storage:
parameter to the property wrapper:
extension UserDefaults {
static var alternative = UserDefaults(suiteName: "alternative")!
}
@Persisted("alternativeStoredValue", storage: .alternative)
var alternativeStoredValue: Int?
Why a Library?
After all, there are lots of examples of similar utilities on the web. For example, this post by John Sundell shows how a @UserDefaultsBacked
property wrapper can be written in a handful of lines.
However, during development of my app, I found that I really wanted to store enum values in UserDefaults
. For any enum which is backed by integer or a string, there was an obvious ideal implementation - store the enum’s raw value. To provide a single API to persist both UserDefaults
-supported types as well as enum values backed by UserDefaults
-supported types proved a little tricky; adding the requirement that everything needed to also work on Optional
wrappers of any supported type, and the problem became more complex still. Once solved for my app, I thought why not package up?
Requirements
Installation
Swift Package Manager
Add https://github.com/AndrewBennet/PersistedPropertyWrapper.git
as a Swift Package Dependency in Xcode.
CocoaPods
To install via CocoaPods, add the following line to your Podfile:
pod 'PersistedPropertyWrapper', '~> 2.0'
Manually
Copy the contents of the Sources
directory into your project.
Alternatives
- SwiftyUserDefaults has more functionality, but you are required to define your stored properties in a specific extension.
- AppStorage: native Apple property wrapper, but tailored to (and defined in) SwiftUI, and only available in iOS 14
Persisted Property Wrapper
Persisted Property Wrapper is a Swift library to enable extremely easy persistance of variables in the
UserDefaults
database on Apple platforms.To use Persisted Property Wrapper you simply annotate a variable as being
@Persisted
. It supports the standardUserDefaults
types (Int
,String
,Bool
,Date
and more), along withRawRepresentable
enums where theRawValue
is storable inUserDefaults
, as well as any type which conforms toCodable
orNSSecureCoding
. Plus of course anyOptional
wrapper of any of these types. The type-validity is checked at compile-time: attempting to use on any variables of a non-supported type will cause a compile-time error.Usage
Stick a
@Persisted
attribute on your variable.The first argument of the initializer is the string key under which the value will be stored in
UserDefaults
. If the type is non-Optional, you must also supply adefaultValue
, which will be used when there is no value stored inUserDefaults
.For example:
Storing Enums
Want to store an enum value? If the enum has a backing type which is supported for storage in
UserDefaults
, then those can also be marked as@Persisted
, and the actual value stored inUserDefaults
will be the enum’s raw value. For example:Storing Codable types
Any codable type can be Persisted too; this will store in UserDefaults the JSON-encoded representation of the variable. For example:
Note that the argument label
encodedDataKey
must be used. This is required to remove ambiguity about which storage method is used, sinceUserDefaults
-storable types can beCodable
too.For example, the following two variables are stored via different mechanisms:
Note: on iOS 12, using the
encodedDataKey
initializer with a value which would encode to a JSON fragment (e.g.Int
,String
,Bool
, etc) will cause a crash. This is due to a bug in the Swift runtime shipped prior to iOS 13. UsingencodedDataKey
has no benefit in these cases anyway.Storing types which implement
NSCoding
Any
NSObject
which conforms toNSSecureCoding
can be Persisted too; this will store in UserDefaults the encoded representation of the object obtained fromNSKeyedArchiver
. For example:Note that the argument label
archivedDataKey
must be used. As above, this is required to remove ambiguity about which storage method is used.Note: this storage mechanism is only supported on iOS 11 and up.
Alternative Storage
By default, a
@Persisted
property is stored in theUserDefaults.standard
database; to store values in a different location, pass thestorage:
parameter to the property wrapper:Why a Library?
After all, there are lots of examples of similar utilities on the web. For example, this post by John Sundell shows how a
@UserDefaultsBacked
property wrapper can be written in a handful of lines.However, during development of my app, I found that I really wanted to store enum values in
UserDefaults
. For any enum which is backed by integer or a string, there was an obvious ideal implementation - store the enum’s raw value. To provide a single API to persist bothUserDefaults
-supported types as well as enum values backed byUserDefaults
-supported types proved a little tricky; adding the requirement that everything needed to also work onOptional
wrappers of any supported type, and the problem became more complex still. Once solved for my app, I thought why not package up?Requirements
Installation
Swift Package Manager
Add
https://github.com/AndrewBennet/PersistedPropertyWrapper.git
as a Swift Package Dependency in Xcode.CocoaPods
To install via CocoaPods, add the following line to your Podfile:
Manually
Copy the contents of the
Sources
directory into your project.Alternatives