NSManagedObject is the class you interact with most when you work with Core Data. The class may appear as a glorified dictionary, but it is much more than that. In this tutorial, I show you three features of the NSManagedObject class you may not know about.

Setting Dynamic Defaults

You probably know that you can set a default value for an attribute of an entity. Even though this works fine, the default value is static. You cannot assign a localized string to an attribute, for example. And setting the current date to an attribute isn’t possible either.

Default Value of Attribute of Type String

Default Value of Attribute of Type Date

There is a solution to this problem, though. The NSManagedObject class defines the awakeFromInsert() method. This method is invoked when the managed object is inserted for the first time into a managed object context. The method is invoked once for each managed object. This means it is ideal for setting default values. Let me illustrate this with an example. This is what the User entity looks like.

User Entity

We could manually set the createdAt attribute of each User instance when it is created. But a better solution is to automatically set the value when a new User instance is inserted in its managed object context. This is easy to accomplish by overriding the awakeFromInsert() method as shown below.

import CoreData

extension User {

    override public func awakeFromInsert() {
        setPrimitiveValue(NSDate(), forKey: "createdAt")
    }

}

We create an extension for the User class, an NSManagedObject subclass, and override the awakeFromInsert() method.

By using setPrimitiveValue(_:forKey:), we bypass KVC. This is necessary if you don’t want the undo manager to track the changes we make to the User instance in the awakeFromInsert() method.

On Save

The User entity also defines an updatedAt attribute. It would be nice if the value of this attribute is automatically updated every time the changes of a User instance are pushed to the persistent store. We can accomplish this by overriding another instance method of the NSManagedObject class, willSave().

As the name suggests, this method is invoked when the managed object is about to be saved, that is, when the changes of the managed object context the managed object belongs to are going to be pushed to the persistent store. Take a look at the implementation below.

import CoreData

extension User {

    override public func willSave() {
        if let updatedAt = updatedAt {
            if updatedAt.timeIntervalSince(Date()) > 10.0 {
                self.updatedAt = NSDate()
            }

        } else {
            self.updatedAt = NSDate()
        }
    }

}

The implementation of the willSave() method may look complicated, but let me explain what is happening. We first check whether updatedAt has a value. If it doesn’t, we set updatedAt to the current date.

If updatedAt is not equal to nil, we make sure the time difference between the value of updateAt and the current date is bigger than ten seconds. Why is that important?

By setting the value of the updatedAt property, we invoke another save operation because Core Data detects a change was made. This means that the willSave() method is invoke again. This causes an infinite loop. Apple’s documentation emphasizes that this should be avoided at any cost.

The documentation also points out that there are two other options. We can bypass KVC by using setPrimitiveValue(_:forKey:) as we did earlier in this tutorial. Alternatively, we can update the updatedAt property of the User instance by listening for a NSManagedObjectContextWillSaveNotification notification. But know that these approaches also have side effects.

Entity

As of iOS 10 and macOS 10.12, it is possible to ask a NSManagedObject subclass for its entity description through the entity() class method. Before iOS 10 and macOS 10.12, developers implemented their own version of the entity() class method. That is no longer necessary as you can see below.

User.entity()

Remember that it is also possible to ask a NSManagedObject instance for its entity description. The NSManagedObject class also defines an entity property that returns an NSEntityDescription instance.

user.entity

Core Data is a mature and rich framework. That is what makes it fun and challenging to work with. If you find yourself wresting with Core Data, chances are that there is a better solution to the problem you are trying to solve.