繁体   English   中英

SwiftUI、组合和核心数据 — 项目未正确映射/显示

[英]SwiftUI, Combine, and Core Data — Items not being mapped/displayed properly

我是 SwiftUI 和 Combine 的新手,并且有一个用于测试的简单项目。 我正在使用本文中概述的CDPublisher class 来创建 Core Data 和 Combine 之间的桥梁。 我已经声明了一个名为ItemEntity 它有一个属性,一个String ,包含一个序列化的 JSON object。 这个 object 反序列化为一个名为Item的结构。 我的 SwiftUI 视图仅显示从我的 ViewModel 返回的项目列表:

struct ContentView: View {
    
    @ObservedObject
    var viewModel: MyListViewModel
    
    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.items, id: \.self.id) { item in
                    Text("\(item.name) - \(item.createdDate)")
                }
            }
            .onAppear {
                self.viewModel.fetchItems()
            }
            .toolbar {
                Button(action: addItem) {
                    Label("Add Item", systemImage: "plus")
                }
            }
        }
    }
    
    private func addItem() {
        let entity = ItemEntity(context: viewContext)
        let rand = arc4random()
        var model = Item(name: "Item \(rand)", createdDate: Date())
        model.id = UUID().uuidString
        let jsonData = try! JSONEncoder().encode(model)
        
        entity.modelJSON = String(data: jsonData, encoding: .utf8)
        do {
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("\(nsError) - \(nsError.userInfo)")
        }
    }
}

我的ViewModel使用CDPublisher class 从核心数据中获取实体。 它还有一个 map function 反序列化每个条目中的 JSON 字符串到Item实例。 最终,通过我的ViewModel可以使用一个Item对象Array 它看起来像这样:

import Foundation
import CoreData
import Combine
import SwiftUI

class MyListViewModel: ObservableObject {
    
    private var viewContext: NSManagedObjectContext =  PersistenceController.shared.container.viewContext
    
    @Published
    var items: [Item] = []
    
    private var cancellables = [AnyCancellable]()
    
    func fetchItems() {
        print("FETCHING...")
        
        let decoder = JSONDecoder()
        let fetchReq: NSFetchRequest<ItemEntity> = ItemEntity.fetchRequest()
        CoreDataPublisher(request: fetchReq, context: self.viewContext)
            .map({ (entities: [ItemEntity]) -> [Item] in
                var items: [Item] = []
                entities.forEach { (entity: ItemEntity) in
                    if let json = entity.modelJSON?.data(using: .utf8) {
                        if let item = try? decoder.decode(Item.self, from: json) {
                            items.append(item)
                        }
                    }
                }
                
                return items
            })
            .receive(on: DispatchQueue.main)
            .replaceError(with: [])
            .eraseToAnyPublisher()
            .sink { completion in
                print("COMPLETION : \(completion)")
            } receiveValue: { items in
                print("SUCCESS")
                items.forEach { (item) in
                    print("\t\(item)")
                }
                self.items = items
            }.store(in: &cancellables)
    }
}

我的代码编译并成功运行。 我看到打印语句来自我的fetchItems()方法,表明我的 items 数组包含所有预期的对象:

FETCHING...
SUCCESS
    Item(name: "Item 854277542", createdDate: 2021-01-25 19:19:16 +0000)
    Item(name: "Item 92334228", createdDate: 2021-01-25 19:19:17 +0000)
    Item(name: "Item 405319813", createdDate: 2021-01-25 19:19:18 +0000)
    Item(name: "Item 121330574", createdDate: 2021-01-25 19:19:18 +0000)
    Item(name: "Item 3025980536", createdDate: 2021-01-25 19:19:19 +0000)
    Item(name: "Item 1278077958", createdDate: 2021-01-25 19:19:19 +0000)
    Item(name: "Item 4274618146", createdDate: 2021-01-25 19:19:19 +0000)
    Item(name: "Item 2320455869", createdDate: 2021-01-25 19:19:19 +0000)
    Item(name: "Item 3542559526", createdDate: 2021-01-25 19:19:22 +0000)
    Item(name: "Item 4217121551", createdDate: 2021-01-25 19:19:23 +0000)
    Item(name: "Item 4139555338", createdDate: 2021-01-25 19:19:24 +0000)
    Item(name: "Item 1345067436", createdDate: 2021-01-25 19:20:49 +0000)

但是,我的 UI 没有按预期显示项目。 我希望每个Item object 都有一行。 相反,我看到多行,但它们都具有与我的数组中的第一个Item匹配的完全相同的文本。 它基本上是一遍又一遍地重复的同一行。

我显然在这里做错了什么,但我没有足够的经验来确切地知道是什么。 为什么我的 items 属性具有正确的值,但我没有在我的 UI 中看到它? 是因为我的 map function 和 JSON 解码吗? (另外,有没有更好的方法来完成将ItemEntity实体数组映射到Item对象数组?)有什么提示吗?

更新:下面是我的Item结构的定义

import Foundation

struct Item {
    
    var name: String = ""
    var createdDate: Date = Date()
    
}

extension Item: Codable {
    
}

extension Item: Identifiable {
    private struct IdentifiableHolder {
        static var _id: String = ""
    }
    
    var id: String {
        get {
            return IdentifiableHolder._id
        }
        set(newId) {
            IdentifiableHolder._id = newId
        }
    }
}

您为fetchItems显示的代码无法确保您解码的 Item 对象具有唯一的id值 - 但 List 明确依赖于这些 ( id: \.self.id )。 相反,您的所有 Item 对象都具有默认id ,即"" 这样每个Item object的id就是第一个Item object的id ,解释了这个现象。

你可能一直在追求这样的事情:

struct Item : Codable {
    var name: String = ""
    var createdDate: Date = Date()
    private var idHolder = IdentifiableHolder(id: UUID().uuidString)
}

extension Item: Identifiable {
    private struct IdentifiableHolder : Codable {
        var id: String = ""
    }
    var id: String {
        get {
            return idHolder.id
        }
        set(newId) {
            idHolder.id = newId
        }
    }
}

现在每个 Item 都有一个唯一的 ID,它作为 Codable 实现的一部分被存储和检索。

暂无
暂无

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

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