如何解决核心数据合并策略有时会删除对象
我正在尝试创建一个简单的应用程序,该应用程序在拆分视图的左侧具有对话,在右侧具有消息。左侧的对话列表将显示每个对话的recordID,描述和最新消息的详细信息。消息视图具有“添加消息”按钮,该按钮将模拟向会话中添加消息。这将始终使用相同的消息ID。这是为了测试合并策略,它是NSMergeByPropertyObjectTrumpMergePolicy。我面临的问题是,在您添加新消息时,latestMessage变为50%。这将导致应用程序崩溃,因为会话列表正在使用LatestMessage属性来显示LatestMessage详细信息。
我正在寻找一种解决方案,该方法如何有效地在对话列表中显示LatestMessage而不导致崩溃。我也想首先了解导致此问题的原因。我的假设是它与合并策略有关,但是我不确定为什么,因为我所有其他对象都遵守合并策略而没有问题。
所有必需的代码在下面。如果您想下载该项目,请点击link。
struct ContentView: View {
var body: some View {
NavigationView{
ConversationsView()
Text("Select a conversation")
}
}
}
struct MessagesView: View{
@StateObject var model: MessagesModel
@ObservedObject var conversation: Conversation
init(conversation: Conversation) {
_model = StateObject(wrappedValue: MessagesModel(conversation: conversation))
self.conversation = conversation
}
var body: some View{
List(model.messages){ message in
VStack(alignment: .leading){
Text("ID: \(message.id)")
Text("Message: \(message.message)")
Text("Person: \(message.person.firstName)")
}
}
.navigationTitle("Messages \(model.messages.count)")
.toolbar(content: {
ToolbarItem(placement: .navigationBarTrailing){
Button("Add Messages"){
model.addMessage(to: conversation)
}
}
})
}
}
struct ConversationsView: View{
@StateObject var model = ConversationModel()
var body: some View{
List(model.conversations){ convo in
NavigationLink(
destination: MessagesView(conversation: convo),label: {
VStack(alignment: .leading){
Text("Record id: \(convo.id)")
.font(.headline)
Text("Description: \(convo.myDescription)")
.font(.headline)
Text("\(convo.latestMessage.person.fullName): \(convo.latestMessage.message)")
.font(.subheadline)
}
})
}.navigationTitle("Conversations \(model.conversations.count)")
}
}
class ConversationModel: NSObject,ObservableObject,NSFetchedResultsControllerDelegate{
@Published var conversations = [Conversation]()
private let conversationsController: NSFetchedResultsController<Conversation>
override init(){
conversationsController = NSFetchedResultsController(fetchRequest: Conversation.getAllConversations(),managedObjectContext: PersistentStore.shared.context,sectionNameKeyPath: nil,cacheName: nil)
super.init()
conversationsController.delegate = self
do {
try conversationsController.performFetch()
conversations = conversationsController.fetchedObjects ?? []
createConversations()
} catch {
print("Error: \(error.localizedDescription)")
}
}
private func createConversations(){
let context = PersistentStore.shared.context
let conversation = Conversation(context: context)
let ricky = Person(context: context)
let message = Message(context: context)
ricky.firstName = "Ricky"
ricky.lastName = "W"
ricky.title = "iOS Dev"
message.id = 1
message.message = "Testing 123"
message.timeStamp = Date().timeIntervalSince1970
message.person = ricky
conversation.myDescription = "This is a conversation"
conversation.id = 1
conversation.activeParticipants.insert(ricky)
conversation.latestMessage = message
conversation.messages.insert(message)
PersistentStore.shared.saveContext()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
if let items = controller.fetchedObjects as? [Conversation]{
conversations = items
}
}
}
class MessagesModel: NSObject,NSFetchedResultsControllerDelegate{
@Published var messages = [Message]()
private let messagesController: NSFetchedResultsController<Message>
init(conversation: Conversation){
messagesController = NSFetchedResultsController(fetchRequest: Message.getMessagesFor(conversation),cacheName: nil)
super.init()
messagesController.delegate = self
do {
try messagesController.performFetch()
messages = messagesController.fetchedObjects ?? []
} catch {
print("Error: \(error.localizedDescription)")
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
if let items = controller.fetchedObjects as? [Message]{
messages = items
}
}
func addMessage(to conversation: Conversation){
let context = PersistentStore.shared.context
let tim = Person(context: context)
tim.firstName = "Tim"
tim.lastName = "Apple"
tim.title = "CEO"
let message = Message(context: context)
message.id = 2
message.message = "I Am Tim Apple"
message.timeStamp = Date().timeIntervalSince1970
message.person = tim
conversation.messages.insert(message)
conversation.latestMessage = message
PersistentStore.shared.saveContext()
}
}
class PersistentStore: ObservableObject {
var context: NSManagedObjectContext {
persistentContainer.viewContext
}
static let shared = PersistentStore()
private init() {}
// MARK: - Core Data stack
let persistentContainer: NSPersistentContainer = {
let container = NSPersistentCloudKitContainer(name: "CoreDataTesting")
// Enable remote notifications
guard let description = container.persistentStoreDescriptions.first else {
fatalError("###\(#function): Failed to retrieve a persistent store description.")
}
description.setOption(true as NSNumber,forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
container.loadPersistentStores(completionHandler: { (storeDescription,error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error),\(error.userInfo)")
}
})
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror),\(nserror.userInfo)")
}
}
}
// MARK: - Delete For Debug
func deleteEverythingForDebug(){
let conversationsRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Conversation")
let messagesRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Message")
let personRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Person")
let conversationsDeleteRequest = NSBatchDeleteRequest(fetchRequest: conversationsRequest)
let messagesDeleteRequest = NSBatchDeleteRequest(fetchRequest: messagesRequest)
let personDeleteRequest = NSBatchDeleteRequest(fetchRequest: personRequest)
do {
try persistentContainer.viewContext.execute(conversationsDeleteRequest)
try persistentContainer.viewContext.execute(messagesDeleteRequest)
try persistentContainer.viewContext.execute(personDeleteRequest)
print("Deleted all of Core Data objects.")
} catch {
fatalError("Error deleting everything: \(error)")
}
saveContext()
}
}
public class Conversation : NSManagedObject,Identifiable{
@NSManaged public var activeParticipants : Set<Person>
@NSManaged public var myDescription : String
@NSManaged public var id : Int
@NSManaged public var messages : Set<Message>
@NSManaged public var latestMessage : Message
static func getAllConversations() -> NSFetchRequest<Conversation> {
let request : NSFetchRequest<Conversation> = Conversation.fetchRequest() as! NSFetchRequest<Conversation>
request.sortDescriptors = [NSSortDescriptor(key: "latestMessage.timeStamp",ascending: false)]
return request
}
}
public class Message : NSManagedObject,Identifiable {
@NSManaged public var id : Int
@NSManaged public var message : String
@NSManaged public var timeStamp : Double
@NSManaged public var person : Person
@NSManaged public var conversation : Conversation
@NSManaged public var latestConversation : Conversation
static func getMessagesFor(_ conversation: Conversation) -> NSFetchRequest<Message> {
let request : NSFetchRequest<Message> = Message.fetchRequest() as! NSFetchRequest<Message>
request.sortDescriptors = [NSSortDescriptor(key: "timeStamp",ascending: true)]
request.predicate = NSPredicate(format: "conversation == %@",conversation)
return request
}
static func getAllMessages() -> NSFetchRequest<Message> {
let request : NSFetchRequest<Message> = Message.fetchRequest() as! NSFetchRequest<Message>
request.sortDescriptors = [NSSortDescriptor(key: "timeStamp",ascending: true)]
return request
}
}
public class Person: NSManagedObject,Identifiable {
@NSManaged public var firstName: String
@NSManaged public var lastName: String
@NSManaged public var title: String
@NSManaged public var conversations: Set<Conversation>
@NSManaged public var messages: Set<Message>
var fullName : String{
"\(firstName) \(lastName)"
}
static func getAllPeopleWith(title: String) -> NSFetchRequest<Person> {
let request : NSFetchRequest<Person> = Person.fetchRequest() as! NSFetchRequest<Person>
request.sortDescriptors = [NSSortDescriptor(key: "firstName",ascending: false)]
request.predicate = NSPredicate(format: "title == %@",title)
return request
}
static func getAllPeople() -> NSFetchRequest<Person> {
let request : NSFetchRequest<Person> = Person.fetchRequest() as! NSFetchRequest<Person>
request.sortDescriptors = [NSSortDescriptor(key: "firstName",ascending: false)]
return request
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。