简体   繁体   English

SwiftUI 如何使用 查看 Model 与嵌套 JSON

[英]SwiftUI How to use View Model with nested JSON

I'm learning iOS development and I'm trying to modify my app to use MVVM model.我正在学习 iOS 开发,我正在尝试修改我的应用程序以使用 MVVM model。 Below I'm pasting json structure that I'm using.下面我粘贴我正在使用的 json 结构。 I'm able to access categories, but I encountered an issue when I tried to iterate through Items.我可以访问类别,但是当我尝试遍历 Items 时遇到了问题。 How my view model should look like?我的观点 model 应该是什么样子? Do I need 2 view models one for Category and another one for Item?我需要 2 个视图模型,一个用于类别,另一个用于项目吗? Also how to combine View Model with AppStorage?另外如何将 View Model 与 AppStorage 结合使用?

[
    {
        "id": "8DC6D7CB-C8E6-4654-BAFE-E89ED7B0AF94",
        "name": "Category",
        "items": [
            {
                "id": "59B88932-EBDD-4CFE-AE8B-D47358856B93",
                "name": "Item1",
                "isOn": false
            },
            {
                "id": "E124AA01-B66F-42D0-B09C-B248624AD228",
                "name": "Item2",
                "isOn": false
            }
     }
 ]

View看法

struct ContentView: View {
    
    @ObservedObject var viewModel = MyModel()
    var body: some View {
        List {
            ForEach(viewModel.items, id: \.self) { id in
                Text(id.name)
                //how to iterate through items?
            }
        }
    }
}

ViewModel视图模型

class MyModel: ObservableObject {
    
    @Published var items: [ItemsSection] = [ItemsSection]()

    init(){
        loadData()
    }

    func loadData()  {
        guard let url = Bundle.main.url(forResource: "items", withExtension: "json")
        else {
            print("Json file not found")
            return
        }
        let data = try? Data(contentsOf: url)
        let items = try? JSONDecoder().decode([ItemsSection].self, from: data!)
        self.items = items!
    }
    
    
    func getSelectedItemsCount() -> Int{
        var i: Int = 0
        for itemSection in items {
            let filteredItems = itemSection.items.filter { item in
                return item.isOn
            }
            i = i + filteredItems.count
        }
        return i
    }
}

Model: Model:

struct ItemSection: Codable, Identifiable, Hashable  {
    var id: UUID = UUID()
    var name: String
    var items: [Item]
}

struct Item: Codable, Equatable, Identifiable,Hashable  {
    var id: UUID = UUID()
    var name: String
    var isOn: Bool = false
}

To iterate over the your items array you can do something like this:要遍历您的items数组,您可以执行以下操作:

struct ContentView: View {
    // This should be @StateObject as this View owns the viewmodel
    @StateObject var viewModel = MyModel()
    
    var body: some View {
        List {
            //ItemSection is Identifiable so no need for `id: \.self` here.
            ForEach(viewModel.sections) { section in
            //Section is a View provided by Apple that can help you laying
            // out your View. You don´t have to, you can use your own
                Section(section.name){
                    ForEach(section.items){ item in
                        Text(item.name)
                    }
                }
            }
        }
    }
}

I´ve changed the naming in thew Viewmodel as I think the naming of items var should really be sections .我已经更改了 Viewmodel 中的命名,因为我认为items var 的命名实际上应该是sections

class MyModel: ObservableObject {
    
    @Published var sections: [ItemsSection] = [ItemsSection]()
    
    init(){
        loadData()
    }
    
    func loadData()  {
        guard let url = Bundle.main.url(forResource: "items", withExtension: "json")
        else {
            print("Json file not found")
            return
        }
        do {
            let data = try Data(contentsOf: url)
            let sections = try JSONDecoder().decode([ItemsSection].self, from: data)
            
            self.sections = sections
        } catch {
            print("failed loading or decoding with error: ", error)
        }
    }
    
    func getSelectedItemsCount() -> Int{
        var i: Int = 0
        for itemSection in sections {
            let filteredItems = itemSection.items.filter { item in
                return item.isOn
            }
            i = i + filteredItems.count
        }
        return i
    }
}

And never use try?并且从不使用try? use a proper do / catch block and print the error.使用正确的do / catch块并打印错误。 This will help you in future to identify problems better.这将帮助您在未来更好地发现问题。 For example the example JSON you provided is malformatted.例如,您提供的示例 JSON 格式错误。 Without proper do / catch it will just crash while force unwrap.如果没有正确的do / catch它只会在强制展开时崩溃。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM