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:
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
}
}使用方式与原生支持的类型一致:
@AppStorage("date") var date = Date()2. 支持数组类型
为支持数组类型,需要对数组实现泛型版本的 RawRepresentable,要求数组的元素符合 Codable 协议:
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
}
}使用示例:
@AppStorage("selections") var selections = [3, 4, 5]3. 支持枚举类型
如果枚举的 RawValue 是 Int 或 String,则无需额外实现扩展,@AppStorage 已经可以直接支持:
enum Options: Int {
case a, b, c, d
}
@AppStorage("option") var option = Options.a注意事项
- 确保扩展的数据类型能正确编码和解码,以避免存储和读取数据时发生错误。
- 为复杂数据类型实现
RawRepresentable时,推荐使用 JSON 编码,以便处理更加灵活。
延伸阅读
以下文章对 SwiftUI 属性包装器及其扩展应用提供了更多背景信息和实践参考:
"加入我们的 Discord 社区,与超过 2000 名苹果生态的中文开发者一起交流!"