La mejor forma de implementar Enums con Core Data

¿Cuál es la mejor manera de vincular las entidades de datos principales a los valores enum para que pueda asignar una propiedad de tipo a la entidad? En otras palabras, tengo una entidad llamada Item con una propiedad itemType que quiero vincular a una enumeración, ¿cuál es la mejor manera de hacerlo?

Tendrá que crear accesores personalizados si desea restringir los valores a una enumeración. Entonces, primero declararías una enumeración, así:

 typedef enum { kPaymentFrequencyOneOff = 0, kPaymentFrequencyYearly = 1, kPaymentFrequencyMonthly = 2, kPaymentFrequencyWeekly = 3 } PaymentFrequency; 

Luego, declare getters y setters para su propiedad. Es una mala idea anular las existentes, ya que los usuarios de acceso estándar esperan un objeto NSNumber en lugar de un tipo escalar, y se encontrará con problemas si algo en los enlaces o sistemas KVO intentan acceder a su valor.

 - (PaymentFrequency)itemTypeRaw { return (PaymentFrequency)[[self itemType] intValue]; } - (void)setItemTypeRaw:(PaymentFrequency)type { [self setItemType:[NSNumber numberWithInt:type]]; } 

Finalmente, debe implementar + keyPathsForValuesAffecting para que pueda recibir notificaciones de KVO para itemTypeRaw cuando itemType cambie.

 + (NSSet *)keyPathsForValuesAffectingItemTypeRaw { return [NSSet setWithObject:@"itemType"]; } 

Puedes hacerlo de esta manera, mucho más simple:

 typedef enum Types_e : int16_t { TypeA = 0, TypeB = 1, } Types_t; @property (nonatomic) Types_t itemType; 

Y en su modelo, configure itemType para que sea un número de 16 bits. Todo listo. No se requiere código adicional. Solo pon tu habitual

 @dynamic itemType; 

Si está utilizando Xcode para crear su subclase NSManagedObject , asegúrese de que esté marcada la configuración ” usar propiedades escalares para tipos de datos primitivos “.

Un enfoque alternativo que estoy considerando no es declarar una enumeración en absoluto, sino declarar los valores como métodos de categoría en NSNumber.

Si está utilizando mogenerator, eche un vistazo a esto: https://github.com/rentzsch/mogenerator/wiki/Using-enums-as-types . Puede tener un atributo Integer 16 llamado itemType , con un valor itemType de Item en la información del usuario. Luego, en la información del usuario para su entidad, establezca additionalHeaderFileName en el nombre del encabezado en el que está definida la Enum del Item . Al generar sus archivos de encabezado, mogenerator automáticamente hará que la propiedad tenga el tipo de Item .

Establecí el tipo de atributo como un entero de 16 bits y luego uso esto:

 #import  enum { LDDirtyTypeRecord = 0, LDDirtyTypeAttachment }; typedef int16_t LDDirtyType; enum { LDDirtyActionInsert = 0, LDDirtyActionDelete }; typedef int16_t LDDirtyAction; @interface LDDirty : NSManagedObject @property (nonatomic, strong) NSString* identifier; @property (nonatomic) LDDirtyType type; @property (nonatomic) LDDirtyAction action; @end 

 #import "LDDirty.h" @implementation LDDirty @dynamic identifier; @dynamic type; @dynamic action; @end 

Como las enumeraciones están respaldadas por un resumen estándar, tampoco puede usar el contenedor NSNumber y establecer la propiedad directamente como un valor escalar. Asegúrese de establecer el tipo de datos en el modelo de datos básicos como “Integer 32”.

MyEntity.h

 typedef enum { kEnumThing, /* 0 is implied */ kEnumWidget, /* 1 is implied */ } MyThingAMaBobs; @interface myEntity : NSManagedObject @property (nonatomic) int32_t coreDataEnumStorage; 

En otro lugar en el código

 myEntityInstance.coreDataEnumStorage = kEnumThing; 

O analizando desde una cadena JSON o cargando desde un archivo

 myEntityInstance.coreDataEnumStorage = [myStringOfAnInteger intValue]; 

El código pegado debajo funciona para mí, y lo he agregado como un ejemplo de trabajo completo. Me gustaría escuchar opiniones sobre este enfoque, ya que planeo usarlo extensamente en todas mis aplicaciones.

  • Dejé el @dynamic en su lugar, ya que luego lo satisfizo el getter / setter nombrado en la propiedad.

  • Según la respuesta de iKenndac, no he anulado los nombres predeterminados de getter / setter.

  • He incluido una comprobación de rango a través de un NSAssert en los valores válidos typedef.

  • También agregué un método para obtener un valor de cadena para el typedef dado.

  • Prefijo las constantes con “c” en lugar de “k”. Sé el razonamiento detrás de “k” (orígenes matemáticos, históricos), pero parece que estoy leyendo el código ESL con él, así que uso “c”. Solo una cosa personal.

Aquí hay una pregunta similar: typedef como un tipo de datos Core

Agradecería cualquier aporte sobre este enfoque.

 Word.h #import  #import  typedef enum { cPresent = 0, cFuturProche = 1, cPasseCompose = 2, cImparfait = 3, cFuturSimple = 4, cImperatif = 5 } TenseTypeEnum; @class Word; @interface Word : NSManagedObject @property (nonatomic, retain) NSString * word; @property (nonatomic, getter = tenseRaw, setter = setTenseRaw:) TenseTypeEnum tense; // custom getter & setter methods -(void)setTenseRaw:(TenseTypeEnum)newValue; -(TenseTypeEnum)tenseRaw; - (NSString *)textForTenseType:(TenseTypeEnum)tenseType; @end Word.m #import "Word.h" @implementation Word @dynamic word; @dynamic tense; // custom getter & setter methods -(void)setTenseRaw:(TenseTypeEnum)newValue { NSNumber *numberValue = [NSNumber numberWithInt:newValue]; [self willChangeValueForKey:@"tense"]; [self setPrimitiveValue:numberValue forKey:@"tense"]; [self didChangeValueForKey:@"tense"]; } -(TenseTypeEnum)tenseRaw { [self willAccessValueForKey:@"tense"]; NSNumber *numberValue = [self primitiveValueForKey:@"tense"]; [self didAccessValueForKey:@"tense"]; int intValue = [numberValue intValue]; NSAssert(intValue >= 0 && intValue < = 5, @"unsupported tense type"); return (TenseTypeEnum) intValue; } - (NSString *)textForTenseType:(TenseTypeEnum)tenseType { NSString *tenseText = [[NSString alloc] init]; switch(tenseType){ case cPresent: tenseText = @"présent"; break; case cFuturProche: tenseText = @"futur proche"; break; case cPasseCompose: tenseText = @"passé composé"; break; case cImparfait: tenseText = @"imparfait"; break; case cFuturSimple: tenseText = @"futur simple"; break; case cImperatif: tenseText = @"impératif"; break; } return tenseText; } @end 

He hecho esto mucho y encuentro el siguiente formulario para ser útil:

 // accountType public var account:AccountType { get { willAccessValueForKey(Field.Account.rawValue) defer { didAccessValueForKey(Field.Account.rawValue) } return primitiveAccountType.flatMap { AccountType(rawValue: $0) } ?? .New } set { willChangeValueForKey(Field.Account.rawValue) defer { didChangeValueForKey(Field.Account.rawValue) } primitiveAccountType = newValue.rawValue }} @NSManaged private var primitiveAccountType: String? 

En este caso, la enumeración es bastante simple:

 public enum AccountType: String { case New = "new" case Registered = "full" } 

y lo llamo pedante, pero yo uso enumeraciones para nombres de campos, como este:

 public enum Field:String { case Account = "account" } 

Como esto puede ser laborioso para modelos de datos complejos, escribí un generador de código que consume MOM / entidades para escupir todas las asignaciones. Mis entradas terminan siendo un diccionario de Table / Row a Enum type. Mientras estaba en eso, también genere código de serialización JSON. He hecho esto para modelos muy complejos y ha resultado ser un gran ahorro de tiempo.