Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data: should I be fetching objects from the parent context or does the child context have the same objects as the parent?

I am slightly confused about parent/child contexts for ManagedObjectContext.

When I setup a child context and set the parent context, does the child context contain all the objects of the parent context? I am using the stock Core Data methods that get created in the AppDelegate, but I changed the ConcurrencyQueue to main.

In my method that is supposed to update the db:

  • Create child context, set parent context
  • Perform block on child context
  • Fetch from parent context
  • Create or update object in child context
  • Call save on child context
  • Have Notification listener to handle child context saves
  • Save parent context

My issue is that it does not look I am saving anything to the child context. I am not getting my println messages of Update or Create ChatMessage. What am I doing wrong here?

AppDelegate Core Data methods

lazy var managedObjectContext: NSManagedObjectContext? = {
        // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
        let coordinator = self.persistentStoreCoordinator
        if coordinator == nil {
            return nil
        }

        var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = coordinator
        managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "contextDidSave:", name: NSManagedObjectContextDidSaveNotification, object: nil)

        return managedObjectContext
    }()

    func contextDidSave(notification: NSNotification) {
        let sender = notification.object as! NSManagedObjectContext
        if sender != managedObjectContext {
            managedObjectContext?.mergeChangesFromContextDidSaveNotification(notification)
            println("Core Data: merging changes from child context")
            saveContext()
        }
    }

Database class that handles the update

lazy var parentContext: NSManagedObjectContext? = {
        if let managedObjectContext = self.appDelegate.managedObjectContext {
            return managedObjectContext
        }
        else {
            return nil
        }
        }()

func updateMessage(chatMessage: ChatMessage) {
        if chatMessage.id.isEmpty { return }

        let childContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
        childContext.parentContext = parentContext
        childContext.performBlock({
            let objectIdDesc = NSExpressionDescription()
            objectIdDesc.name = "objectID"
            objectIdDesc.expression = NSExpression.expressionForEvaluatedObject()
            objectIdDesc.expressionResultType = NSAttributeType.ObjectIDAttributeType

            let fetchRequest = NSFetchRequest(entityName: "ChatMessage")
            fetchRequest.predicate = NSPredicate(format: "id == %@", chatMessage.id)
            fetchRequest.propertiesToFetch = [objectIdDesc]
            fetchRequest.resultType = .DictionaryResultType

            var error: NSError?
            if let results = self.parentContext!.executeFetchRequest(fetchRequest, error: &error) {
                if error == nil {
                    if !results.isEmpty {
                        if let objectId = results[0].valueForKey("objectID") as? NSManagedObjectID {
                            let fetched = childContext.objectWithID(objectId) as! ChatMessage
                            fetched.id = chatMessage.id
                            fetched.senderUserId = chatMessage.senderUserId
                            fetched.senderUsername = chatMessage.senderUsername
                            fetched.receiverUserId = chatMessage.receiverUserId
                            fetched.receiverUsername = chatMessage.receiverUsername
                            fetched.messageType = chatMessage.messageType
                            fetched.message = chatMessage.message
                            fetched.timestamp = chatMessage.timestamp
                            fetched.filepath = chatMessage.filepath
                            println("Updated ChatMessage: \(fetched.id)")
                        }
                        else {
                            var newMessage = NSEntityDescription.insertNewObjectForEntityForName("ChatMessage", inManagedObjectContext: childContext) as! ChatMessage
                            newMessage.id = chatMessage.id
                            newMessage.senderUserId = chatMessage.senderUserId
                            newMessage.senderUsername = chatMessage.senderUsername
                            newMessage.receiverUserId = chatMessage.receiverUserId
                            newMessage.receiverUsername = chatMessage.receiverUsername
                            newMessage.messageType = chatMessage.messageType
                            newMessage.message = chatMessage.message
                            newMessage.timestamp = chatMessage.timestamp
                            newMessage.filepath = chatMessage.filepath
                            println("Create ChatMessage: \(newMessage.id)")
                        }
                    }
                }
                else {
                    println("Fetch Message Object ID Error: \(error?.localizedDescription)")
                }
            }
        })
        childContext.save(nil)
    }
like image 428
The Nomad Avatar asked May 06 '15 23:05

The Nomad


People also ask

Will you ever pass a managed object from one context to another context?

You cannot pass NSManagedObjects between multiple contexts, but you can pass NSManagedObjectIDs and use them to query the appropriate context for the object represented by that ID.

Can we have multiple managed object context in Core Data?

Most apps need just a single managed object context. The default configuration in most Core Data apps is a single managed object context associated with the main queue. Multiple managed object contexts make your apps harder to debug; it's not something you'd use in every app, in every situation.

What is the purpose of managed object context NSManagedObjectContext in Objective C and how does it work?

A managed object context is an instance of NSManagedObjectContext . Its primary responsibility is to manage a collection of managed objects. These managed objects represent an internally consistent view of one or more persistent stores.


1 Answers

It does not seem to make too much sense to create a child context and then fetch from the parent context. I do not believe that this is the way child contexts' blocks were conceived to be used.

To clear up the confusion: after creating the child context from a parent context, that child context has the same "state" as the parent. Only if the two contexts do different things (create, modify, delete objects) the content of the two contexts will diverge.

So for your setup, proceed as follows:

  • create the child context
  • do the work you want to do (modifying or creating objects from the downloaded data),
  • save the child context

At this stage, nothing is saved to the persistent store yet. With the child save, the changes are just "pushed up" to the parent context. You can now

  • save the parent context

to write the new data to the persistent store. Then

  • update your UI,

best via notifications (e.g. the NSManagedObjectContextDidSaveNotification).

like image 80
Mundi Avatar answered Oct 27 '22 10:10

Mundi