[英]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.