NSManagedObject关系变更处理



我在视图更新中遇到了问题,Core Data上下文不知道关系的变化,所以我不是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[$0]
}.forEach { profile in
context.delete(profile)
saveContext()
}
}
private func deleteWallet(profile : Profile , offset : IndexSet) {
offset.map{
profile.profileWalletsArray()[$0]
}.forEach { wallet in
context.delete(wallet)
saveContext()
}
}
private func deleteBalance(wallet : Wallet , offset : IndexSet) {
offset.map{
wallet.walletBalanceArray()[$0]
}.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{
$0.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"

}
}

============= ANSWER + FIX =========

这里的解决方案由"@lorem ipsum"通过创建单独的视图并添加@ObservedObject

修复了这个问题TestView Update =>

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()[$0]
}.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()[$0]
}.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()[$0]
}.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中。

要做到这一点,使子视图以profile,walletbalance为参数,而不是拥有所有这些变量与子视图。

相关内容

  • 没有找到相关文章

最新更新