无法使用合并框架将自定义数据保存到CoreData [英] Unable to save custom data into CoreData using Combine framework
本文介绍了无法使用合并框架将自定义数据保存到CoreData的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我正在创建自定义数据,我希望在进行收藏时将其保存到CoreData中。为此,我使用组合框架,将CoreData值订阅回我的自定义数据。问题是,当我试图映射CoreData和自定义数据时,出现了一些问题,甚至无法在画布上显示我的自定义数据。老实说,我甚至不知道我在做什么,因为大多数ViewModel代码都是基于Nick的教程视频(来自Swifful Think YouTube频道)。请帮我解决一下我的代码有什么问题。提前谢谢。
我创建的CoreData的名称为";DataContainer";,实体名称为";DataEntity";。在DataEntity中,有三个属性:
'id' with a type "Integer32"
'isFavorite' with a type "Boolean"
'timestamp' with a type "Date"
import Foundation
import CoreData
// This is CoreData class without using Singleton
class CoreDataManager {
private let container: NSPersistentContainer
@Published var savedEntities: [DataEntity] = []
init() {
container = NSPersistentContainer(name: "DataContainer")
container.loadPersistentStores { _, error in
if let error = error {
print("Error loading CoreData! (error)")
}
}
fetchData()
}
// MARK: Privates
private func fetchData() {
let request = NSFetchRequest<DataEntity>(entityName: "DataEntity")
do {
savedEntities = try container.viewContext.fetch(request)
} catch let error {
print("Error fetching DataEntity! (error)")
}
}
// Add to CoreData
private func addFavorite(dataID: DataArray, onTappedFavorite: Bool) {
let newFavorite = DataEntity(context: container.viewContext)
newFavorite.id = Int32(dataID.id)
newFavorite.isFavorite = onTappedFavorite
applyChanges()
}
// Update time
private func updateTime() {
let newTime = DataEntity(context: container.viewContext)
newTime.timestamp = Date()
}
// Save to CoreData
private func save() {
do {
try container.viewContext.save()
} catch let error {
print("Error saving to CoreData! (error)")
}
}
private applyChanges() {
save()
updateTime()
fetchData()
}
private func update(entity: DataEntity, updateFavorite: Bool) {
entity.isFavorite = updateFavorite
applyChanges()
}
private func delete(entity: DataEntity) {
container.viewContext.delete(entity)
applyChanges()
}
// MARK: Public
func updateFavorite(dataID: DataArray, onTappedFavorite: Bool) {
// Checking the data is already taken
if let entity = savedEntities.first(where: { $0.id == dataID.id }) {
if onTappedFavorite {
update(entity: entity, updateFavorite: onTappedFavorite)
} else {
delete(entity: entity)
}
} else {
addFavorite(dataID: dataID, onTappedFavorite: onTappedFavorite)
}
}
}
这将是我的模型:
import Foundation
import SwiftUI
struct DataArray: Identifiable {
let id: Int
let cities: String
let name1: String
let name2: String
let isFavorite: Bool
func updateFavorite(favorited: Bool) -> DataArray {
return DataArray(id: id, cities: cities, name1: name1, name2: name2, isFavorite: favorited)
}
}
public struct ListDataArray {
static let dot = [
DataArray(id: 1,
cities: "Baltimore"
name1: "John",
name2: "Mike",
isFavorite: False),
DataArray(id: 2,
cities: "Frederick"),
name1: "Joe",
name2: "Swift",
isFavorite: False),
DataArray(id: 3,
cities: "Catonsville"
name1: "Susan",
name2: "Oliver",
isFavorite: False),
// There will be a lot of data
]
}
这将是我的主页视图模型:
import Foundation
import SwiftUI
import Combine
class Prospect: ObservableObject {
@Published var datas: [DataArray] = []
private let coreDataServices = CoreDataManager()
private var cancellables = Set<AnyCancellable>()
init() {
fetchDataArrays()
fetchCoreData()
}
private func fetchDataArrays() {
let items = ListDataArray.dot
datas = items
}
private func fetchCoreData() {
coreDataServices.$savedEntities
.map({ (coreData) -> [DataArray] in
// Here is something wrong when I check and try to convert CoreData to DataArray
let arrays: [DataArray] = []
return arrays
.compactMap { (data) -> DataArray? in
guard let entity = coreData.first(where: { $0.id == data.id }) else {
return nil
}
return data.updateFavorite(favorited: entity.isFavorite)
}
})
.sink {[weak self] (receivedEntities) in
self?.datas = receivedEntities
}
.store(in: &cancellables)
}
func updateFavoriteData(dataID: DataArray, isFavorite: Bool) {
coreDataServices.updateFavorite(dataID: dataID, onTappedFavorite: isFavorite)
}
// To View Favorite
@Published var showFavorite: Bool = false
}
这是我的视图:
import SwiftUI
struct Home: View {
@EnvironmentObject var items: Prospect
var body: some View {
ScrollView {
LazyVStack {
ForEach(items.datas) { data in
VStack {
HStack {
Button {
//Action for making favorite or unfavorite
items.updateFavoriteData(dataID: data, isFavorite: data.isFavorite)
} label: {
Image(systemName: data.isFavorite ? "suit.heart.fill" : "suit.heart")
}
Spacer()
Button {
items.showFavorite.toggle()
} label: {
Image(systemName: "music.note.house.fill")
}
.sheet(isPresented: $items.showFavorite) {
FavoriteView()
.environmentObject(items)
}
}
Text("(data.id)")
.font(.title3)
Text(data.cities)
.font(.subheadline)
Spacer()
}
padding()
}
.padding()
}
}
}
}
struct FavoriteView: View {
@EnvironmentObject var items: Prospect
var body: some View {
VStack {
List {
ForEach(items.datas) { data in
if data.isFavorite {
VStack(spacing: 10) {
Text(data.cities)
Text(data.name1)
Text(data.name2)
}
.font(.body)
}
}
.padding()
}
Spacer()
}
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
Home()
.environmentObject(Prospect())
}
}
推荐答案
这里有一种不同的方法
//extend DataEntity
extension DataEntity{
//Have a dataArray variable that links with your array
var dataArray: DataArray?{
ListDataArray.dot.first(where: {
$0.id == self.id
})
}
}
//extend DataArray
extension DataArray{
//have a data entity variable that retrieves the CoreData object
var dataEntity: DataEntity{
//Use DataEntity Manager to map
//Find or Create
return DataEntityManager().retrieve(dataArray: self)
}
}
//Have an entity manager
class DataEntityManager{
let container = PersistenceController.previewAware
//convenience to create
func create(id: Int32) -> DataEntity{
let entity = DataEntity(context: container.container.viewContext)
entity.id = id
save()
return entity
}
//for updating to centralize work
func update(entity: DataEntity){
//I think this is what you intend to update the timestamp when the value changes
entity.timestamp = Date()
//get the array variable
var dataArry = entity.dataArray
//See if they match to prevent loops
if dataArry?.isFavorite != entity.isFavorite{
//if they dont update the array
dataArry = dataArry?.updateFavorite(favorited: entity.isFavorite)
}else{
//Leave alone
}
save()
}
//for updating to centralize work
func update(dataArray: DataArray){
//get the entity
let entity = dataArray.dataEntity
//See if they match to prevent loops
if entity.isFavorite != dataArray.isFavorite{
//if they dont update the entity
DataEntityManager().update(entity: entity)
}else{
//leave alone
}
}
func retrieve(dataArray: DataArray) -> DataEntity{
let request: NSFetchRequest = DataEntity.fetchRequest()
request.predicate = NSPredicate(format: "id == %@", dataArray.id)
do{
let result = try controller.container.viewContext.fetch(request).first
//This is risky because it will create a new one
//You can handle it differently if you prefer
let new = create(id: Int32(dataArray.id))
update(entity: new)
return result ?? new
}catch{
print(error)
//This is risky because it will create a new one
//You can handle it differently if you prefer
let new = create(id: Int32(dataArray.id))
update(entity: new)
return new
}
}
func save() {
do{
try container.container.viewContext.save()
}catch{
print(error)
}
}
}
struct DataArray: Identifiable {
let id: Int
let cities: String
let name1: String
let name2: String
var isFavorite: Bool
//Mostly your method
mutating func updateFavorite(favorited: Bool) -> DataArray {
//get the new value
isFavorite = favorited
//update the entity
DataEntityManager().update(dataArray: self)
//return the new
return self
}
}
这样,您现在可以使用任一对象访问匹配的变量
dataEntity.dataArray
或
dataArray.dataEntity
请记住使用管理器或数组中的方法进行更新,以便所有内容保持同步。
要注意的事情。CoreData对象是ObservableObject
,只要您想查看DataEntity
的更改,就应该将它们包装在@ObservedObject
这篇关于无法使用合并框架将自定义数据保存到CoreData的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文