TL;DR: Extend SwiftUI’s
@AppStorageto handleDate, arrays, and custom enums using theRawRepresentableprotocol. Ensure the data type’sRawValueisIntorString, 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.aConsiderations
- 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:
If this article helped you, feel free to buy me a coffee ☕️ . For sponsorship inquiries, please check out the details here.