SwiftUI、Combine和Core Data——没有正确映射/显示的项



我是SwiftUI和Combine的新手,并且有一个我用来测试的简单项目。我使用本文中概述的CDPublisher类来创建Core Data和Combine之间的桥梁。我已经声明了一个名为ItemEntity的核心数据实体类。它只有一个属性,一个String,包含一个序列化的JSON对象。该对象反序列化为一个名为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类从核心数据中获取实体。它还有一个映射函数,将每个条目中的JSON字符串反序列化为Item实例。最后,Item对象的Array是通过我的ViewModel提供的。它看起来像这样:

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()方法生成的print语句,指示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对象都有一行。相反,我看到多行,但他们都有完全相同的文本匹配第一个Item在我的数组。它本质上是一遍又一遍地重复的同一行。

我显然在这里做错了什么,但我没有足够的经验来确切地知道是什么。为什么我的项目属性有正确的值,但我没有看到我的UI反映?是由于我的地图功能和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对象的id都是第一个Item对象的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,并且作为其可编码实现的一部分进行存储和检索。

相关内容

  • 没有找到相关文章

最新更新