Using Protocols with Core Data Managed Objects in Swift


Swift's lack of dynamic dispatch precludes working with a Core Data NSManaged objects via a protocol type. Swift Beta 5 introduced a feature that resolves this problem. As a result, we are now able to enforce a protocol that spans Core Data objects and plain old objects.

The following code represents a Core Data Entity (Boy) that implements the protocol Kid and a class UnmanagedKid that implements the Kid protocol, but is not a managed object.

protocol Kid {
	var name: String {get}
 	var age: NSNumber {get}
}

class Boy: NSManagedObject, Kid {
	@NSManaged var name: String
 	@NSManaged var age: NSNumber
 	...
}

class UnmanagedKid: Kid {
	var name: String
 	var age: NSNumber
}

Fetching the boy records works as expected. However, accessing it's properties fails.
var boyRecord = ...
var aboy = boyRecord as Boy
var akid = boyRecord as Kid

println(aboy.name)	//works as expected
println(akid.name)	//EXC_BAD_ACCESS(code=1,address=0x0)

When I access the boy record via the Boy type, everything works as expected. However, if I try to access the same record via the Kid type, it fails and crashes the application.

The reason for the failure is the underlying "magic" that Core Data applies to an NSManagedObject. Essentially, it rewrites the class definition at runtime to pass @NSManaged property calls through proxy methods. However, it cannot make the same modifications to non-managed objects or protocols. In Objective-C, this is ok because Objective-C uses dynamic dispatch for all calls and references. Swift, however, seeks to improve performance by inlining references. As a result, the Swift protocol type (Kid) references the orginal memory locations for properties on the object. Unfortunately, after the Core Data "magic", these locations are no longer valid.

Swift Beta 5 provides a mechanism to fix this problem with the introduction of the dynamic keyword.

dynamic

Apply this modifier to any member of a class that can be represented by Objective-C. When you mark a member declaration with the dynamic modifier, access to that member is always dynamically dispatched using the Objective-C runtime. Access to that member is never inlined or devirtualized by the compiler.

So, to fix our code example, you would simply add the dynamic keyword to each property on the Boy class.
protocol Kid {
var name: String {get}
var age: NSNumber {get}
}

class Boy: NSManagedObject, Kid {
	@NSManaged dynamic var name: String
 	@NSManaged dynamic var age: NSNumber
 	...
}

class UnmanagedKid: Kid {
	var name: String
 	var age: NSNumber
}

With the dynamic keyword (boyRecord as Kid).name works!