如何在 ViewModel 中使用带有自定义类型和 @Published 的 @AppStorage?

Posted

技术标签:

【中文标题】如何在 ViewModel 中使用带有自定义类型和 @Published 的 @AppStorage?【英文标题】:How to use @AppStorage in ViewModel with custom types and @Published? 【发布时间】:2021-09-16 10:15:16 【问题描述】:

如何在我的 ViewModel 中使用 @AppStorage 来保存一组自定义类型?如何将@AppStorage 与@Published 属性一起使用?

这是我的示例代码:

型号

struct MyModel: Hashable, Codable 
    var text: String
    var number: Int

查看模型

class ViewModel: ObservableObject 

    @Published private(set) var myModels = [MyModel]()
    @Published private(set) var subscribedMyModels = [MyModel]() // TODO: sync this with AppStorage

    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)
        
    


查看

struct ContentView: View 

    @StateObject var viewModel = ViewModel()
    @AppStorage(wrappedValue: false, "settings0") private var settings0

    var body: some View 
        List 

            Section(header: Text("Some Settings")) 
                Toggle(isOn: $settings0, label: 
                    Text("Setting 0")
                )
            
            Section(header: Text("Some other Settings")) 
                ForEach(viewModel.myModels, id: \.self)  myModel in
                    Button(action: 
                        viewModel.toggleSubscription(for: myModel)
                    , label: 
                        HStack 
                            HStack 
                                Text(myModel.text)
                                Text(String(myModel.number))
                            
                            Spacer()

                            if viewModel.subscribedMyModels.contains(myModel) 
                                Image(systemName: "checkmark.circle.fill")
                             else 
                                Image(systemName: "circle")
                            

                        

                    )
                
                .foregroundColor(.black)
            
        
        .listStyle(InsetGroupedListStyle())
        .onAppear 
            viewModel.fetchMyModels()
        
    

用户界面:

待办事项: 在 AppStorage / UserDefauls 中存储列表“其他一些设置”的检查设置。

【问题讨论】:

在存储到 AppStorage 之前将您的自定义类型编码为字符串或数据(NSUserDefaults 支持的类型)并在加载时解码。 This question 向您展示如何订阅和here is 一种编码选项 【参考方案1】:

首先我推荐使用 CoreData 而不是 AppStorage

但要回答你的问题:

将此添加到 ViewModel :

// this is a computed variable which changes with subscribedMyModels changes
var encodedSubscribedMyModels : Data? 
    let encoder = JSONEncoder()
    return try? encoder.encode(subscribedMyModels)

在你的视野中:

    .onChange(of: viewModel.encodedSubscribedMyModels, perform:  newValue in
        // change @AppStore to newValue
        settings0 =  newValue
    )

注意:您需要使用 JSONDecoder 将数据解码回您的数据模型

【讨论】:

以上是关于如何在 ViewModel 中使用带有自定义类型和 @Published 的 @AppStorage?的主要内容,如果未能解决你的问题,请参考以下文章

【WPF】Command 自定义命令

如何在带有打字稿的反应功能组件中定义自定义道具?

在自定义对象类型的核心数据中保存带有子项的类别

带有自定义类型的角度单元测试给出了找不到命名空间

WPF 自定义控件将文本框文本重置为默认值

使用 Ajax 的带有自定义分类法的 Wordpress 多个自定义帖子类型过滤器 - 所有新创建的帖子都不会在响应中显示