When an instance of my class is initialized using NSCoding
, I want to replace it with an existing object in the Core Data database instead of calling:
super.init(entity: ..., insertIntoManagedObjectContext: ...)
as that would insert a new object into the database.
class MyClass: NSManagedObject, NSCoding {
required init(coder aDecoder: NSCoder) {
// Find a preexisting object in the Core Data database
var ctx = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
var fs = NSFetchRequest(entityName: "MyClass")
// ... Configure the fetch request based on values in the decoder
var err: NSErrorPointer = nil
var results = ctx.executeFetchRequest(fs, error: err)
// ... Error handling, etc
newObject = results[0] as! MyClass
// Attempt to replace self with the new object
self = newObject // ERROR: "Cannot assign to 'self' in a method"
}
func encodeWithCoder(aCoder: NSCoder) {
// Encode some identifying property for this object
}
}
This was a fairly common Objective-C pattern, but I can't figure out how to replicate this in Swift 1.2, since assigning another object to self
yields a compile error:
Cannot assign to 'self' in a method
and even if it did succeed, it seems Swift requires that I call NSManagedObject
's designated initializer which would insert a new object into the database.
You can use awakeAfterUsingCoder(_:)
for replacing self
:
You can use this method to eliminate redundant objects created by the coder. For example, if after decoding an object you discover that an equivalent object already exists, you can return the existing object. If a replacement is returned, your overriding method is responsible for releasing the receiver.
class Item: NSManagedObject, NSCoding {
@NSManaged var uuid: String
@NSManaged var name: String?
override init(entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?) {
super.init(entity: entity, insertIntoManagedObjectContext: context)
}
required init(coder aDecoder: NSCoder) {
let ctx = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
let entity = NSEntityDescription.entityForName("Item", inManagedObjectContext: ctx)!
// Note: pass `nil` to `insertIntoManagedObjectContext`
super.init(entity: entity, insertIntoManagedObjectContext: nil)
self.uuid = aDecoder.decodeObjectForKey("uuid") as! String
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(self.uuid, forKey: "uuid")
}
override func awakeAfterUsingCoder(aDecoder: NSCoder) -> AnyObject? {
let ctx = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
let fetch = NSFetchRequest(entityName: "Item")
fetch.predicate = NSPredicate(format: "uuid == %@", self.uuid)
if let obj = ctx.executeFetchRequest(fetch, error: nil)?.first as? Item {
// OK, the object is still in the storage.
return obj
}
else {
// The object has already been deleted. so insert and use `self`.
ctx.insertObject(self)
return self
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With