In modern software development, data persistence plays a crucial role in preserving and accessing user preferences and app state across sessions. With the release of Swift 5.1, a new feature called property wrappers was introduced, which allows developers to encapsulate and automate common behaviors associated with property access. In this article, we will explore how property wrappers can simplify data persistence using a practical example.
Code Example
Let’s consider a scenario where we want to persist various user settings in our app, such as the user’s name, preferred currency, color scheme, profile picture, and the ID of the latest order placed. We can leverage the power of property wrappers to achieve this in a concise and efficient manner.
Add a property wrapper
In this way we add a @Storable
property to use in your application.
This code allows you to save your date in the UserDefault
object.
@propertyWrapper
struct Storable<T: Codable> {
private let key: String
private let defaultValue: T
init(key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
guard let data = UserDefaults.standard.object(forKey: key) as? Data else {
return defaultValue
}
let value = try? JSONDecoder().decode(T.self, from: data)
return value ?? defaultValue
}
set {
let data = try? JSONEncoder().encode(newValue)
UserDefaults.standard.set(data, forKey: key)
}
}
}
Second step is to map in an Enum
a list of items to store, example:
enum AppSettings {
@Storable(key: "userName", defaultValue: "")
static var userName: String
@Storable(key: "currency", defaultValue: "EUR")
static var currency: String?
@Storable(key: "primaryColor", defaultValue: nil)
static var primaryColor: String?
}
Explanation
In the given code snippet, we define a custom property wrapper called Storable
. This wrapper encapsulates the logic for reading and writing values to the UserDefaults
storage, while also providing a default value for each property.
To use the Storable
wrapper, we define an enumeration called AppSettings
. Inside this enumeration, we declare various properties using the @Storable
attribute. Each property is associated with a unique key and a default value. For example, shownName
represents the user’s displayed name, and its default value is an empty string.
The wrappedValue
computed property within the Storable
wrapper handles the actual reading and writing of the property value to the UserDefaults
. When accessing the property, it checks if a value exists in the storage for the associated key. If a value is found, it is decoded using JSONDecoder
and returned; otherwise, the default value is returned.
When assigning a value to the property, the wrappedValue
setter encodes the new value using JSONEncoder
and stores it in the UserDefaults
for the corresponding key.
Utilizing the AppSettings
enumeration, you can easily access and modify the stored values in a straightforward manner. Here’s an example of how you can use it in your code
Let’s use it with an example:
AppSettings.shownName = "John Doe"
print(AppSettings.shownName) // Output: "John Doe"
AppSettings.currency = "USD"
print(AppSettings.currency) // Output: "USD"
Now your value is shared and stored and can be read from everywhere.
You can also replace UserDefault
with Keychain
is you need something more secure or whatever you like.
By utilizing property wrappers, you eliminate the need for repetitive code for storing and retrieving values from persistent storage. The Storable
wrapper handles the serialization and deserialization of the property values, making it easier to manage and persist user settings and app state.
Conclusion
Property wrappers offer a powerful way to simplify data persistence in Swift. By encapsulating the reading and writing logic within a reusable wrapper, developers can easily store and retrieve values from persistent storage without boilerplate code. The example code provided demonstrates how property wrappers, combined with the UserDefaults
storage, can streamline the process of persisting user settings in an application.
For further reading on property wrappers and data persistence in Swift, you may refer to the following resources:
- Swift.org – Property Wrappers: https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID617
- Apple Developer Documentation – UserDefaults: https://developer.apple.com/documentation/foundation/userdefaults
- WWDC 2019 – Modern Swift API Design: https://developer.apple.com/videos/play/wwdc2019/415/