Best way to implement Enums with Core Data

21,695

Solution 1

You'll have to create custom accessors if you want to restrict the values to an enum. So, first you'd declare an enum, like so:

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

Then, declare getters and setters for your property. It's a bad idea to override the existing ones, since the standard accessors expect an NSNumber object rather than a scalar type, and you'll run into trouble if anything in the bindings or KVO systems try and access your value.

- (PaymentFrequency)itemTypeRaw {
    return (PaymentFrequency)[[self itemType] intValue];
}

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

Finally, you should implement + keyPathsForValuesAffecting<Key> so you get KVO notifications for itemTypeRaw when itemType changes.

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

Solution 2

You can do this way, way simpler:

typedef enum Types_e : int16_t {
    TypeA = 0,
    TypeB = 1,
} Types_t;

@property (nonatomic) Types_t itemType;

And in your model, set itemType to be a 16 bit number. All done. No additional code needed. Just put in your usual

@dynamic itemType;

If you're using Xcode to create your NSManagedObject subclass, make sure that the "use scalar properties for primitive data types" setting is checked.

Solution 3

An alternative approach I'm considering is not to declare an enum at all, but to instead declare the values as category methods on NSNumber.

Solution 4

If you're using mogenerator, have a look at this: https://github.com/rentzsch/mogenerator/wiki/Using-enums-as-types. You can have an Integer 16 attribute called itemType, with a attributeValueScalarType value of Item in the user info. Then, in the user info for your entity, set additionalHeaderFileName to the name of the header that the Item enum is defined in. When generating your header files, mogenerator will automatically make the property have the Item type.

Solution 5

I set the attribute type as 16 bit integer then use this:

#import <CoreData/CoreData.h>

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
Share:
21,695

Related videos on Youtube

Michael Gaylord
Author by

Michael Gaylord

iOS Engineer at Civic Inc. Co-founder of Storie Inc. Founding engineer at Gyft Inc.

Updated on July 08, 2022

Comments

  • Michael Gaylord
    Michael Gaylord almost 2 years

    What is the best way to bind Core Data entities to enum values so that I am able to assign a type property to the entity? In other words, I have an entity called Item with an itemType property that I want to be bound to an enum, what is the best way of going about this.

  • Michael Gaylord
    Michael Gaylord over 14 years
    Interesting. It definitely seems doable.
  • TheLearner
    TheLearner over 12 years
    brilliant idea! so much easier than creating tables in the db, unless your db is filled from a web service then its probably best to use a db table!
  • Constantino Tsarouhas
    Constantino Tsarouhas almost 12 years
    Thank you — too bad Core Data doesn't support this natively. I mean: Xcode generates class files, why not enums?
  • port5432
    port5432 over 11 years
  • Anonymous White
    Anonymous White over 11 years
    The last code is if you want to observe item itemTypeRaw. However, you can simply observe item itemType instead of itemTypeRaw right?
  • Daniel Eggert
    Daniel Eggert over 11 years
    With Xcode 4.5 you don't need any of this. Take a look at my answer. You just need to define the enum as an int16_t and you're set.
  • ArtOfWarfare
    ArtOfWarfare over 11 years
    Are there any limits with which iOS / OS X versions this will work with?
  • Daniel Eggert
    Daniel Eggert over 11 years
    It’s been working for as long as I remember, but I can't make any promises. I remember using this on iOS 5, but I don't know why it wouldn't work with iOS 4...
  • Ivan Vučica
    Ivan Vučica about 11 years
    Note, this is a C++11 feature called "enum classes". cprogramming.com/c++11/…
  • Daniel Eggert
    Daniel Eggert about 11 years
    No, this has nothing to do with C++11. It's part of clang 3.3 supporting Enumerations with a fixed underlying type for ObjC. C.f. clang.llvm.org/docs/…
  • Rob
    Rob about 11 years
    How do you avoid losing this code every time you regenerate the model class? I have been using Categories so that the core domain entities can be regenerated.
  • Jeff
    Jeff about 11 years
    Since this doesn't have the retain keyword, will it still get stored in the database?
  • Daniel Eggert
    Daniel Eggert about 11 years
    The retain is related to memory management, not whether it gets stored into the database or not.
  • Kyle Redfearn
    Kyle Redfearn about 11 years
    I agree with Rob. I don't want this to have to be regenerated over and over again. I prefer the category.
  • malhal
    malhal almost 11 years
    Sure looks like C++ with all the underscores
  • DonnaLea
    DonnaLea over 10 years
    @Rob To avoid the enum being overwritten every time the model class is regenerated, I wrote my typedef in a separate .h header file. So only two things need changing when the file is regenerated, the property type to the enum and re-including the header file with the enum. Not perfect, but the best I could come up with if following this pattern.
  • DonnaLea
    DonnaLea over 10 years
    I like it. I'm going to use this approach in my project. I like that I can also contain all my other meta information about the meta data within the NSNumber category. (i.e. linking strings to the enum values)
  • Gregarious
    Gregarious about 10 years
    Really great idea! Very useful for associating string identifiers, using directly in JSON, Core Data, etc.
  • tapmonkey
    tapmonkey about 10 years
    @Rob Categories is a way to do it, but instead you could also use mogenerator: github.com/rentzsch/mogenerator. Mogenerator will generate 2 classes per entity, where one class will always be overwritten on data model changes and the other subclasses that class for custom stuff and never gets overwritten.
  • malhal
    malhal over 9 years
    I'm getting a bad access with this
  • Wizard of Kneup
    Wizard of Kneup over 9 years
    How can you do this in Swift? If I am correct then this "trick" is not reproducible in Swift. See as well my question here: stackoverflow.com/questions/27656219/…
  • Daniel Eggert
    Daniel Eggert over 9 years
    Swift enum is something very, very different from a C / ObjC enum. I'm not sure if there's any good way to fudge it…
  • p0lAris
    p0lAris about 9 years
    I have been using this in my code but this throws me errors every time: CoreData: error: Property <> is a scalar type on class <> that does not match its Entity's property's scalar type. Dynamically generated accessors do not support implicit type coercion. Cannot generate a setter method for it. I just want to use the accepted answer after trying so much.
  • Gagan Joshi
    Gagan Joshi almost 9 years
    @Daniel Eggert , i try to use this but i am getting an error, Declaration of 'int16_t" must be imported from module . Can you help me on that. Many thanks in advance
  • Daniel Eggert
    Daniel Eggert almost 9 years
    In ObjC int16_t is already defined if you import Foundation or CoreData. Are you using Swift? You need to make sure that your scalar type in the entity definition matches the int16_t -> 16 bit.
  • malhal
    malhal almost 9 years
    I was using this technique until I discovered a major problem that doing it this way prevents changedValues from containing the key.
  • LolaRun
    LolaRun almost 9 years
    @p0lAris I've been having the same error , because i thought i was doing the same implementation, but there's one thing you and I didn't notice at first. "typedef enum Types_e : int16_t {" this line, you probably have something like that instead "typedef enum {"
  • malhal
    malhal over 8 years
    Any chance you could edit Types_e and Types_t to something clearer? thanks
  • robmathers
    robmathers about 8 years
    If you're getting BAD_ACCESS errors or similar, be sure that the proper type (i.e. int16_t) is set in your model file. If you're using Xcode to create your NSManagedObject subclass, make sure that the "use scalar properties for primitive data types" setting is checked.
  • trapper
    trapper over 6 years
    @malhal is right! this seems to mess up CoreData change notifications