TL;DR: Extend SwiftUI’s
@AppStorage
to handleDate
, arrays, and custom enums using theRawRepresentable
protocol. Ensure the data type’sRawValue
isInt
orString
, and leverage JSON encoding/decoding for seamless storage and retrieval.
Background
@AppStorage
is a property wrapper in SwiftUI that allows easy saving and reading of variables in UserDefaults
. However, its default supported data types are limited, excluding commonly used types like Date
and arrays.
This article demonstrates how to extend the supported types for @AppStorage
, enabling it to handle more custom data types.
Solution
Although @AppStorage
natively supports limited types, its flexibility increases when paired with the RawRepresentable
protocol. Any data type conforming to RawRepresentable
with a RawValue
of Int
or String
can be stored using @AppStorage
.
Below are extensions for common data types.
1. Support for Date
By implementing the RawRepresentable
protocol for Date
, it becomes compatible with @AppStorage
:
extension Date: RawRepresentable {
public typealias RawValue = String
public init?(rawValue: RawValue) {
guard let data = rawValue.data(using: .utf8),
let date = try? JSONDecoder().decode(Date.self, from: data) else {
return nil
}
self = date
}
public var rawValue: RawValue {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8) else {
return ""
}
return result
}
}
Usage is identical to native types:
@AppStorage("date") var date = Date()
2. Support for Arrays
To support arrays, a generic implementation of RawRepresentable
is needed, requiring the array’s elements to conform to the Codable
protocol:
extension Array: RawRepresentable where Element: Codable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode([Element].self, from: data) else {
return nil
}
self = result
}
public var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8) else {
return "[]"
}
return result
}
}
Example usage:
@AppStorage("selections") var selections = [3, 4, 5]
3. Support for Enums
Enums with RawValue
of Int
or String
are already supported natively by @AppStorage
, requiring no additional implementation:
enum Options: Int {
case a, b, c, d
}
@AppStorage("option") var option = Options.a
Considerations
- Ensure the extended data types can be correctly encoded and decoded to avoid errors during storage and retrieval.
- For complex data types, JSON encoding is recommended for greater flexibility.
Further Reading
Explore these articles for additional context and practical examples of SwiftUI property wrappers and their extensions: