TL;DR: 使用
RawRepresentable
协议扩展 SwiftUI 的@AppStorage
支持,处理更多数据类型,如Date
、数组和自定义枚举。确保数据类型的RawValue
是Int
或String
,可通过 JSON 编解码实现灵活的存储和读取。
背景
@AppStorage
是 SwiftUI 提供的属性包装器,可用于在视图中便捷地保存和读取 UserDefaults
中的变量。然而,默认情况下,@AppStorage
支持的数据类型较为有限,例如常见的 Date
类型和数组都不在支持范围内。
本文将介绍如何扩展 @AppStorage
的支持类型,使其能够处理更多自定义数据类型。
解决方案
尽管 @AppStorage
原生支持的类型有限,但通过 RawRepresentable
协议,它能够灵活地存储更多自定义数据类型,有效拓展了其存储能力。只要数据类型符合 RawRepresentable
协议,并且其 RawValue
是 Int
或 String
,就可以被 @AppStorage
支持。
以下是针对几种常用数据类型的扩展方法。
1. 支持 Date 类型
通过为 Date
类型实现 RawRepresentable
协议,可以让它兼容 @AppStorage
:
Swift
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
}
}
使用方式与原生支持的类型一致:
Swift
@AppStorage("date") var date = Date()
2. 支持数组类型
为支持数组类型,需要对数组实现泛型版本的 RawRepresentable
,要求数组的元素符合 Codable
协议:
Swift
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
}
}
使用示例:
Swift
@AppStorage("selections") var selections = [3, 4, 5]
3. 支持枚举类型
如果枚举的 RawValue
是 Int
或 String
,则无需额外实现扩展,@AppStorage
已经可以直接支持:
Swift
enum Options: Int {
case a, b, c, d
}
@AppStorage("option") var option = Options.a
注意事项
- 确保扩展的数据类型能正确编码和解码,以避免存储和读取数据时发生错误。
- 为复杂数据类型实现
RawRepresentable
时,推荐使用 JSON 编码,以便处理更加灵活。
延伸阅读
以下文章对 SwiftUI 属性包装器及其扩展应用提供了更多背景信息和实践参考: