Sunday, January 11, 2015

Using Swift Enums with Core Data

Xcode does not yet provide a direct way to use Swift enumerations as a property type in a Core Data Model.

The basic pattern to do this is to store a 'raw' value of type NSNumber in the Core Data model and extend your generated NSManagedObject class to add an additional property.

For example if your generated class file is:

import Foundation
import CoreData

@objc(MyEntity)
class MyEntity: NSManagedObject {

    @NSManaged var myPropertyRaw: NSNumber

}

You can create a separate Swift file to extend this generated class with an additional property that provides the conversion to your enumeration:

import Foundation

enum MyEnumeration:Int {
    case Value0 = 0
    case Value1 = 1
    case Value2 = 2
}

extension MyEntity {
    var myProperty: MyEnumeration {
        get {
            return MyEnumeration(rawValue: myPropertyRaw.integerValue)!
        }
        set {
            myPropertyRaw = NSNumber(integer: newValue.rawValue)
        }

    }
}

For this to work your enumeration must have an integer rawType. In the example this is just Int. Alternatively you could define it to use an integer type with a specific size - Int8, Int16, Int32 or Int64.
If you do use one of these types you will have to amend your property definition to use the right parameters:

        // Int8
        get {
            return  MyEnumeration(rawValue: dateTypeRaw.charValue)!
        }
        set {
            myPropertyRaw = NSNumber(char: newValue.rawValue)
        }

        // Int16
        get {
            return  MyEnumeration(rawValue: dateTypeRaw.shortValue)!
        }
        set {
            myPropertyRaw = NSNumber(short: newValue.rawValue)
        }

        // Int32
        get {
            return  MyEnumeration(rawValue: dateTypeRaw.intValue)!
        }
        set {
            myPropertyRaw = NSNumber(int: newValue.rawValue)
        }

        // Int64
        get {
            return  MyEnumeration(rawValue: dateTypeRaw.longLongValue)!
        }
        set {
            myPropertyRaw = NSNumber(longLong: newValue.rawValue)
        }

The ! at the end of the get's return statement unwraps the value returned by the MyEnumeration's failable initializer. This assumes you are performing some validation on the raw value so that this get method will never encounter invalid values.

You have to put the extension in a separate file because you will have to regenerate the MyEntity.Swift file anytime you make changes to your MyEntity definition in your data model.

BTW you currently have to add the @objc(MyEntity) line to the generated file manually to enable Swift to work with the generated class.