无法使用合并框架将自定义数据保存到CoreData [英] Unable to save custom data into CoreData using Combine framework

查看:23
本文介绍了无法使用合并框架将自定义数据保存到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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆