如何在ViewModel中结合@Published使用@AppStorage存储自定义类型数组
实现方案
因为你定义的MyModel已经遵守Codable协议,有两种常用的方式实现自定义数组持久化+@Published联动:
方案1:简洁版,通过扩展让@AppStorage直接支持Codable数组
该方案无需引入Combine监听,代码量最少,适合小项目使用。
第一步:扩展Array适配@AppStorage要求的RawRepresentable协议
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 } }
第二步:修改ViewModel的属性定义
直接把原来的@Published属性替换为@AppStorage即可,@AppStorage本身会自动触发视图刷新,原有业务逻辑完全不用改:
class ViewModel: ObservableObject { @Published private(set) var myModels = [MyModel]() // 直接用@AppStorage修饰数组,自动实现持久化+变更通知 @AppStorage("subscribedMyModels") private(set) var subscribedMyModels = [MyModel]() func fetchMyModels() { myModels = [ MyModel(text: "Setting", number: 1), MyModel(text: "Setting", number: 2), MyModel(text: "Setting", number: 3), MyModel(text: "Setting", number: 4), MyModel(text: "Setting", number: 5), MyModel(text: "Setting", number: 6), MyModel(text: "Setting", number: 7), MyModel(text: "Setting", number: 8) ] } func toggleSubscription(for myModel: MyModel) { if subscribedMyModels.contains(myModel) { subscribedMyModels.removeAll(where: { $0 == myModel } ) } else { subscribedMyModels.append(myModel) } } }
方案2:无全局扩展版,通过Combine监听实现持久化
如果不想全局扩展Array,可通过监听@Published属性变更实现自动持久化,对现有代码侵入性也很低。
第一步:扩展UserDefaults实现Codable数组读写
extension UserDefaults { func save<T: Codable>(_ items: [T], forKey key: String) { guard let data = try? JSONEncoder().encode(items) else { return } set(data, forKey: key) } func load<T: Codable>(forKey key: String) -> [T] { guard let data = data(forKey: key), let items = try? JSONDecoder().decode([T].self, from: data) else { return [] } return items } }
第二步:修改ViewModel添加监听逻辑
import Combine class ViewModel: ObservableObject { @Published private(set) var myModels = [MyModel]() @Published private(set) var subscribedMyModels = [MyModel]() private let subscribedKey = "subscribedMyModels" private var cancellables = Set<AnyCancellable>() init() { // 初始化读取缓存 subscribedMyModels = UserDefaults.standard.load(forKey: subscribedKey) // 监听变更自动存到UserDefaults $subscribedMyModels .sink { [weak self] items in self?.saveSubscribedItems(items) } .store(in: &cancellables) } private func saveSubscribedItems(_ items: [MyModel]) { UserDefaults.standard.save(items, forKey: subscribedKey) } // 原有业务方法完全不用修改 func fetchMyModels() { myModels = [ MyModel(text: "Setting", number: 1), MyModel(text: "Setting", number: 2), MyModel(text: "Setting", number: 3), MyModel(text: "Setting", number: 4), MyModel(text: "Setting", number: 5), MyModel(text: "Setting", number: 6), MyModel(text: "Setting", number: 7), MyModel(text: "Setting", number: 8) ] } func toggleSubscription(for myModel: MyModel) { if subscribedMyModels.contains(myModel) { subscribedMyModels.removeAll(where: { $0 == myModel } ) } else { subscribedMyModels.append(myModel) } } }
两种方案都不需要修改View层的代码,运行后勾选的选项会自动持久化,重启App会自动恢复之前的选择状态。
内容的提问来源于stack exchange,提问作者WalterBeiter
相关产品推荐
相关产品推荐

