Allan  Macatingrao
Allan Macatingrao

Reputation: 2101

Swift: Custom Setter For CoreData NSManagedObject

How to implement custom setter for NSManagedObject in Swift. I need to do task before setting the NSMangedObject Property.

Upvotes: 10

Views: 5138

Answers (3)

Pranav Kasetti
Pranav Kasetti

Reputation: 9925

TL;DR

I would recommend overriding awakeFromInsert instead of the init because it doesn't require KVO and the object properties can be accessed safely. Overriding any of the init methods is risky and unnecessary since the object + its properties may not be ready to be accessed (faults).

Explanation

Overriding any of the init methods is risky and unnecessary since the object + its properties may not be ready to be accessed (faults). However, it’s very useful to be able to prepare an NSManagedObject before it starts accepting data. Perhaps we want to set up some logical defaults or assign some relationships before handing the object to the user. In these situations, we use awakeFromInsert. As the name implies, this method is called right after the NSManagedObject is created from an insert call.

This method is called before any values are set and is a perfect opportunity to set default values, initialize transient properties, and perform other tasks that we would normally handle in the init method. This method is called exactly once in the entire lifetime of an object. It won’t be called on the next execution of the application, and it won’t be called when an object is read in from the persistent store. Therefore, we don’t need to worry about overriding values that have been set previously. When we override this method, we should be sure to call super.awakeFromInsert() at the very beginning of our implementation to allow the NSManagedObject to finish anything it needs to before we begin our code.

Handy ObjC Shortcut in Xcode Snippets (Xcode 12)

Xcode Snippet for awakeFromInsert1

NOTE: It's very easy to add your own Swift snippet for this manually. You can also try some GH repo. e.g for Swift 4 snippets.

Upvotes: 0

Mundi
Mundi

Reputation: 80273

My recommendation would be to use KVC. Maybe not the most elegant solution, but conceptionally a logical application of KVC.

Observe a change of the attribute. Register for the change in init(entity:insertIntoManagedObjectContext:) or maybe better in awakeFromFetch and awakeFromInsert, and remove the observer in willTurnIntoFault.

init(entity: NSEntityDescription!, insertIntoManagedObjectContext context: NSManagedObjectContext!) {
    super.init(entity: entity, insertIntoManagedObjectContext: context)
    addObserver(self, forKeyPath: "attribute", options: NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Old, context: nil)
}


override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: NSDictionary!, context: CMutableVoidPointer) {
    if (keyPath == "attribute") {
           // do what you need to do
    }

}

Updated for Swift 3:

init(entity: NSEntityDescription!, insertIntoManagedObjectContext context: NSManagedObjectContext!) {
    super.init(entity: entity, insertIntoManagedObjectContext: context)
    addObserver(self, forKeyPath: "attribute", options: [.old, .new], context: nil)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "attribute" {
           // do what you need to do
    }
}

Upvotes: 9

Miroslav Kovac
Miroslav Kovac

Reputation: 1208

There is even a simpler way how to do it without managing KVO subscription. It can be done simply by overriding didChangeValueForKey: like this:

 override func didChangeValueForKey(key: String) {
    super.didChangeValueForKey(key)

    if key == "propertyName" {
        // do something now when propertyName changed
    }
}

Upvotes: 7

Related Questions