73

Is it possible to use an Enum as a property for my model? I currently have a class like this:

class Checkin: RLMObject {
  dynamic var id: Int = 0
  dynamic var kind: String = "checked_in"
  var kindEnum: Kind = .CheckedIn {
    willSet { self.kind = newValue.rawValue }
  }

  enum Kind: String {
    case CheckedIn = "checked_in"
    case EnRoute = "en_route"
    case DroppedOff = "dropped_off"
  }
  ....
}

It works okay, but I'd like to be able to have the kind property be the Enum and have Realm automatically call .rawValue on the property when it is saving an object to the store. Is this possible in Realm or is there a feature request already out there for it?

Michael Alan Huff
  • 3,462
  • 3
  • 28
  • 44
Dave Long
  • 9,569
  • 14
  • 59
  • 89

8 Answers8

96

You should override your kindEnum's setter and getter for this case:

enum Kind: String {
  case CheckedIn
  case EnRoute
  case DroppedOff
}

class Checkin: Object {
  @objc dynamic var id = 0
  var kind = Kind.CheckedIn.rawValue
  var kindEnum: Kind {
    get {
      return Kind(rawValue: kind)!
    }
    set {
      kind = newValue.rawValue
    }
  }
}
Lepidopteron
  • 6,056
  • 5
  • 41
  • 53
jpsim
  • 14,329
  • 6
  • 51
  • 68
53

I've refined this model a little further.

enum Thing: String {
    case Thing1
    case Thing2
    case Thing3
}

then in my Realm class object:

class myClass : Object {
    private dynamic var privateThing = Thing.Thing1.rawValue
    var thing: Thing {
        get { return Thing(rawValue: privateThing)! }
        set { privateThing = newValue.rawValue }
    }
}

This allows us to write

myClassInstance.thing = .Thing1

(storing "Thing1" into privateThing), but prevents typing of

myClassInstance.privateThing = "Thing4"

which is not a valid value so preserving data integrity.

Victor Gomes
  • 463
  • 6
  • 15
Stephen Watson
  • 1,700
  • 16
  • 17
  • Update: I've edited my original code because the the = "Thing1" etc. is redundant as Swift uses a String literal equivalent of the case for its rawValue, which is even better and we can use the rawValue to set the default which is more reliable and cleaner. :-) – Stephen Watson Nov 02 '15 at 15:33
  • Thanks for this addition, I really like this answer. +1 – dehlen May 27 '16 at 08:16
33

Realm 10.0.0 introduces PersistableEnum Protocol that makes it easy to persist enum values without any third-party extensions.

enum TaskStatusEnum: String, PersistableEnum {
    case notStarted
    case inProgress
    case complete
}
// To use the enum:
class Task: Object {
    // Required enum property
    @Persisted var status = TaskStatusEnum.notStarted 
    // Optional enum property
    @Persisted var optionalTaskStatusEnumProperty: TaskStatusEnum? 
}
Mohamed Salah
  • 868
  • 1
  • 15
  • 34
20

Since Realm support Objective-C enums and they are representable by Int you can use this:

class Checkin: Object {
  dynamic var id: Int = 0
  dynamic var kind: Kind = .checkedIn

  @objc enum Kind: Int {
    case checkedIn
    case enRoute
    case droppedOff
  }
  ....
}

If you need to parse to/from String you can use a custom initializer for Kind and an toString function.

There is a discussion about this in GitHub

This works with Swift 3.0 and Realm 2.0.2

Diogo T
  • 2,591
  • 1
  • 29
  • 39
  • Nice solution if you have an `Int` enum, but it don't see how it can work with a `String` instead. `Kind` is converted to an Objective-C enum which doesn't support custom initializers or stored properties.. ? – Anders Friis Feb 06 '17 at 13:21
  • Indeed, I haven't found a solution that work with any type but `Int` – Diogo T Feb 07 '17 at 15:14
  • Thank you for this information. I changed my enum to conform to an int. If you need a string value you can make your own computed property in the enum and return the proper value for the different cases. – dan Apr 02 '18 at 20:45
  • 1
    Note that any migrations you do involving Int enums will need to assign the raw value: obj["propertyName"] = Kind.checkedIn.rawValue – Eli Burke Mar 20 '19 at 18:32
13

Diogo T's solution worked before recent updates of RealmSwift. Eventually we now have to conform to RealmEnum protocol to be able to be a managed property of Realm Object.

@objc enum MyEnum: Int, RealmEnum {
    ...
}

Or add below in some place:

extension MyEnum: RealmEnum { }

RealmSwift documentation for it

Pei
  • 11,452
  • 5
  • 41
  • 45
9

Clean & Easy solution

From Realm version >= 10.10.0 you can do this:

// Define the enum
enum StatusEnum: String, PersistableEnum {
    case notStarted
    case inProgress
    case complete
}

// To use the enum:
class Task: Object {
    @Persisted var status = StatusEnum.notStarted 
}
Haroun Hajem
  • 5,223
  • 3
  • 26
  • 39
0

Example with RealmEnum and RealmOptional

@objc enum MyEnum: Int, RealmEnum {
    case first
    case second
}

final class MyEntry: Object {
    //It should be let
    let someVariable = RealmOptional<CRUD>()
}

//using
myEntry.someVariable.value = MyEnum.first
yoAlex5
  • 29,217
  • 8
  • 193
  • 205
0

Alternate solution to avoid the force un-wrap get { return Thing(rawValue: privateThing)! }

enum Kind: String, CaseIterable {
      case CheckedIn
      case EnRoute
      case DroppedOff
    }

    @objc dynamic var kindRaw = Kind.CheckedIn.rawValue
    var kind: Kind {
        get {
            for kind in Kind.allCases where kindRaw == kind.rawValue {
                return kind
            }
            return .CheckedIn //default
        }
        set {
            kindRaw = newValue.rawValue
        }
    }
railwayparade
  • 5,154
  • 1
  • 39
  • 49