NSManagedObject 关系变化处理

NSManagedObject relationship changes handling

我在视图更新中遇到问题,核心数据上下文不知道关系变化,所以我不是专家 swift 还是初学者,我想知道如何观察那些更改或要做的事情,以查看模型中的特定更改,(我尝试 ENTITY.objectWillChange.send() 不适用于关系) 我当前的解决方案是在上下文中刷新 Head 实体,这不是解决方案,因为整个 UI 将随着 ( context.refresh()).

而改变

所以这里的例子正在使用,假设我们有“个人资料”和OneToMany钱包" 和带有 OneToMany Balance 的钱包,所以核心数据图将是:

   ,----------------.
   |  Profile       |
   |----------------|
   `----------------'
          | 1     
          | *      
     ,---------.  
     |Wallet   |  
     |---------|  
     `---------'  
          | 1     
          | *      
  ,-------------.  
  |Balance.     |  
  |-------------|  
  `-------------'  

swift 示例,我尝试将所有内容放在一个视图中,你可以试试,你只需要添加模型和 PersistentContainer

import SwiftUI
import CoreData

struct TestView : View {
    @Environment(\.managedObjectContext)
    var context : NSManagedObjectContext
    
    
    @FetchRequest(sortDescriptors: [])
    var profiles : FetchedResults<Profile>
    
    var body: some View {
        NavigationView {
            List{
                ForEach(profiles){ profile in
                    NavigationLink( destination: {
                        profileView(profile : profile)
                    }){
                        if(profile.isDefault){
                            Text(profile.profileName).foregroundColor(Color.red).lineLimit(1)
                        }else{
                            Text(profile.profileName).foregroundColor(Color.blue).lineLimit(1)
                        }
                    }
                }
                .onDelete(perform: deleteProfile)
                .navigationTitle("Profiles")
                .toolbar{
                    Button(action: addProfile, label: {
                        Text("Add").foregroundColor(Color.black)
                    })
                }
            }
        }
    }
    
    private func deleteProfile(offset : IndexSet) {
        offset.map{
            profiles[[=11=]]
        }.forEach { profile in
            context.delete(profile)
            saveContext()
        }
    }
    private func deleteWallet(profile : Profile , offset : IndexSet) {
        offset.map{
            profile.profileWalletsArray()[[=11=]]
        }.forEach { wallet in
            context.delete(wallet)
            saveContext()
        }
    }
    private func deleteBalance(wallet : Wallet , offset : IndexSet) {
        offset.map{
            wallet.walletBalanceArray()[[=11=]]
        }.forEach { balance in
            context.delete(balance)
            saveContext()
        }
    }
    
    @ViewBuilder
    func profileView(profile : Profile) -> some View {
        List{
            if profile.profileWalletsArray().isEmpty{
                Text("Empty")
            }else{
                ForEach(profile.profileWalletsArray()){ wallet in
                    NavigationLink(destination: {
                        walletView(wallet: wallet)
                    }){
                        Text("\(wallet.walletName)").lineLimit(1)
                    }
                }
                .onDelete(perform: { index in
                    return deleteWallet(profile: profile, offset: index)
                })
            }
        }
        .navigationTitle(Text(profile.profileName))
        .toolbar{
            Button(action: {
                addWallet(profile : profile)
            }, label: {
                Text("Add").foregroundColor(Color.black)
            })
        }
    }
    
    @ViewBuilder
    func walletView(wallet : Wallet) -> some View {
        if wallet.walletBalanceArray().isEmpty {
            Text("Empty Wallet").lineLimit(1)
        }else {
            VStack(alignment: .leading, spacing: 0){
                List{
                    ForEach(wallet.walletBalanceArray()) { balance in
                        VStack{
                            HStack{
                                Text("Asset : \(balance.balanceAsset)").lineLimit(1)
                                Text("Balance : \(balance.balanceAmount) ")
                            }
                        }
                    }
                    .onDelete(perform: { index in
                        return deleteBalance(wallet: wallet, offset: index)
                    })
                }
            }
            .navigationTitle(Text(wallet.walletName))
            .toolbar{
                Button(action: {
                    addBalance(wallet: wallet)
                }, label: {
                    Text("Add").foregroundColor(Color.black)
                })
            }
        }
    }
    
    func addWallet(profile : Profile) {
        withAnimation{
            debugPrint("Add Wallet")
            let wallet = Wallet(context: context)
            let balance1 = Balance(context: context)
            let balance2 = Balance(context: context)
            
            balance1.asset = "EUR"
            balance1.amount = Int64.random(in: 1..<100)
            balance2.asset = "USD"
            balance2.amount = Int64.random(in: 1..<100)
            //Wallet
            wallet.addToBalances(balance1)
            wallet.addToBalances(balance2)
            wallet.name = UUID().uuidString
            wallet.createdAt = Date()
            wallet.updatedAt = Date()
            profile.addToWallets(wallet)
            
            context.refresh(profile, mergeChanges: true)
            saveContext()
        }
    }
    
    func addBalance(wallet : Wallet) {
        
        withAnimation{
            wallet.profile?.objectWillChange.send()
            wallet.objectWillChange.send()
            debugPrint("Add Balance")
            let balance1 = Balance(context: context)
            let balance2 = Balance(context: context)
            
            balance1.asset = "EUR"
            balance1.amount = Int64.random(in: 1..<100)
            balance2.asset = "USD"
            balance2.amount = Int64.random(in: 1..<100)
            //Wallet
            wallet.addToBalances(balance1)
            wallet.addToBalances(balance2)
            wallet.name = UUID().uuidString
            wallet.createdAt = Date()
            wallet.updatedAt = Date()
            
            
            saveContext()
        }
    }
    
    func addProfile() {
        withAnimation{
            do {
                let oldProfile = try context.fetch(Profile.fetchRequest()).filter{
                    [=11=].isDefault
                }.first
                oldProfile?.isDefault = false
                debugPrint("old profile \(String(describing: oldProfile?.profileName))")
            }catch {
                fatalError(error.localizedDescription)
            }
            
            let profile = Profile(context: context)
            let wallet = Wallet(context: context)
            let balance1 = Balance(context: context)
            let balance2 = Balance(context: context)
            
            balance1.asset = "EUR"
            balance1.amount = Int64.random(in: 1..<100)
            balance2.asset = "USD"
            balance2.amount = Int64.random(in: 1..<100)
            
            //Wallet
            wallet.addToBalances(balance1)
            wallet.addToBalances(balance2)
            wallet.name = UUID().uuidString
            wallet.createdAt = Date()
            wallet.updatedAt = Date()
            
            //Profile
            profile.name = UUID().uuidString
            profile.isDefault = true
            profile.addToWallets(wallet)
            profile.createdAt = Date()
            profile.updatedAt = Date()
            
            saveContext()
        }
    }
    
    private func saveContext() {
        do {
            try context.save()
        }catch {
            let error = error as NSError
            fatalError(error.debugDescription)
        }
    }
}
extension Profile {
    public func profileWalletsArray() -> [Wallet] {
        return wallets?.allObjects as? [Wallet] ?? []
    }
    
    var profileName : String {
        return name ?? "unknown"
        
    }
}

extension Wallet {
    var walletName : String {
        return name ?? "unknown"
        
    }
    
    public func walletBalanceArray() -> [Balance] {
        return balances?.allObjects as? [Balance] ?? []
    }
}

extension Balance {
    var balanceAmount : Int {
        return Int(amount)
        
    }
    
    var balanceAsset : String {
        return asset ?? "unknown"
        
    }
}

============= 答案 + 修复 =========

这里是“@lorem ipsum”的解决方案,它通过创建单独的视图并添加 @ObservedObject

解决了问题

测试视图更新=>

var body: some View {
        NavigationView {
            List{
                ForEach(profiles){ profile in
                    NavigationLink( destination: {
                        ProfileView(profile : profile)
                    }){
                        if(profile.isDefault){
                            Text(profile.profileName).foregroundColor(Color.red).lineLimit(1)
                        }else{
                            Text(profile.profileName).foregroundColor(Color.blue).lineLimit(1)
                        }
                    }
                }
                .onDelete(perform: deleteProfile)
                .navigationTitle("Profiles")
                .toolbar{
                    Button(action: addProfile, label: {
                        Text("Add").foregroundColor(Color.black)
                    })
                }
            }
        }
    }

ProfileView.swift

import SwiftUI
import CoreData

struct ProfileView: View {
    
    @Environment(\.managedObjectContext)
    var context : NSManagedObjectContext
    
    @ObservedObject
    var profile : Profile
    
    var body: some View {
        List{
            if profile.profileWalletsArray().isEmpty{
                Text("Empty")
            }else{
                ForEach(profile.profileWalletsArray()){ wallet in
                    NavigationLink(destination: {
                        WalletView(wallet: wallet)
                    }){
                        Text("\(wallet.walletName)").lineLimit(1)
                    }
                }
                .onDelete(perform: { index in
                    return deleteWallet(profile: profile, offset: index)
                })
            }
        }
        .navigationTitle(Text(profile.profileName))
        .toolbar{
            Button(action: {
                addWallet(profile : profile)
            }, label: {
                Text("Add").foregroundColor(Color.black)
            })
        }
    }
    private func deleteWallet(profile : Profile , offset : IndexSet) {
        offset.map{
            profile.profileWalletsArray()[[=13=]]
        }.forEach { wallet in
            context.delete(wallet)
            saveContext()
        }
    }
    
   
    func addWallet(profile : Profile) {
        withAnimation{
            debugPrint("Add Wallet")
            let wallet = Wallet(context: context)
            let balance1 = Balance(context: context)
            let balance2 = Balance(context: context)
            
            balance1.asset = "EUR"
            balance1.amount = Int64.random(in: 1..<100)
            balance2.asset = "USD"
            balance2.amount = Int64.random(in: 1..<100)
            //Wallet
            wallet.addToBalances(balance1)
            wallet.addToBalances(balance2)
            wallet.name = UUID().uuidString
            wallet.createdAt = Date()
            wallet.updatedAt = Date()
            profile.addToWallets(wallet)
            
            context.refresh(profile, mergeChanges: true)
            saveContext()
        }
    }
    
    private func saveContext() {
        do {
            try context.save()
        }catch {
            let error = error as NSError
            fatalError(error.debugDescription)
        }
    }
}

WalletView.swift

import SwiftUI
import CoreData

struct WalletView: View {
    @Environment(\.managedObjectContext)
    var context : NSManagedObjectContext
    
    @ObservedObject
    var wallet : Wallet
    
    var body: some View {
        if wallet.walletBalanceArray().isEmpty {
            Text("Empty Wallet").lineLimit(1)
        }else {
            VStack(alignment: .leading, spacing: 0){
                List{
                    ForEach(wallet.walletBalanceArray()) { balance in
                        BalanceView(balance: balance)
                    }
                    .onDelete(perform: { index in
                        return deleteBalance(wallet: wallet, offset: index)
                    })
                }
            }
            .navigationTitle(Text(wallet.profile?.profileName ?? "unknown"))
            .toolbar{
                Button(action: {
                    addBalance(wallet: wallet)
                }, label: {
                    Text("Add").foregroundColor(Color.black)
                })
            }
        }
    }
    private func deleteBalance(wallet : Wallet , offset : IndexSet) {
        offset.map{
            wallet.walletBalanceArray()[[=14=]]
        }.forEach { balance in
            context.delete(balance)
            saveContext()
        }
    }
    
    func addBalance(wallet : Wallet) {
        
        withAnimation{
            wallet.profile?.objectWillChange.send()
            wallet.objectWillChange.send()
            debugPrint("Add Balance")
            let balance1 = Balance(context: context)
            let balance2 = Balance(context: context)
            
            balance1.asset = "EUR"
            balance1.amount = Int64.random(in: 1..<100)
            balance2.asset = "USD"
            balance2.amount = Int64.random(in: 1..<100)
            //Wallet
            wallet.addToBalances(balance1)
            wallet.addToBalances(balance2)
            wallet.name = UUID().uuidString
            wallet.createdAt = Date()
            wallet.updatedAt = Date()
            
            
            saveContext()
        }
    }
    
    private func saveContext() {
        do {
            try context.save()
        }catch {
            let error = error as NSError
            fatalError(error.debugDescription)
        }
    }
}

BalanceView.swift

import SwiftUI
import CoreData

struct BalanceView: View {
    
    @Environment(\.managedObjectContext)
    var context : NSManagedObjectContext
    
    @ObservedObject
    var balance : Balance
    
    var body: some View {
        VStack{
            HStack{
                Text("Asset : \(balance.balanceAsset)").lineLimit(1)
                Text("Balance : \(balance.balanceAmount) ")
            }
        }
    }
    
    private func deleteBalance(wallet : Wallet , offset : IndexSet) {
        offset.map{
            wallet.walletBalanceArray()[[=15=]]
        }.forEach { balance in
            context.delete(balance)
            saveContext()
        }
    }
    
    func addBalance(wallet : Wallet) {
        
        withAnimation{
            wallet.profile?.objectWillChange.send()
            wallet.objectWillChange.send()
            debugPrint("Add Balance")
            let balance1 = Balance(context: context)
            let balance2 = Balance(context: context)
            
            balance1.asset = "EUR"
            balance1.amount = Int64.random(in: 1..<100)
            balance2.asset = "USD"
            balance2.amount = Int64.random(in: 1..<100)
            //Wallet
            wallet.addToBalances(balance1)
            wallet.addToBalances(balance2)
            wallet.name = UUID().uuidString
            wallet.createdAt = Date()
            wallet.updatedAt = Date()
            
            
            saveContext()
        }
    }
    
    private func saveContext() {
        do {
            try context.save()
        }catch {
            let error = error as NSError
            fatalError(error.debugDescription)
        }
    }
}

希望这个例子对其他人有帮助:P

所有 CoreData 对象都是 ObservableObject,如果您想查看更改,您必须将它们包装在 @ObservedObject 中。

为此,创建将 profilewalletbalance 作为参数的子视图,而不是将所有这些变量与您的子视图一起使用。