Post

Storing an Enum in CoreData

One of the things that I have taken a liking to in Swift is Enumerations (or enums for short.). From the documentation an “enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.” It allows you to easily a dataset that can be reused throughout your code and since they are first-class types, they adopt many features traditionally supported by classes, such as computed properties.

Using a enum in SwiftData is as easy as defining the enum and using it in the model. Using it in CoreData is a little bit more complicated. CoreData does not directly support enum as a data type for attributes, but if you create the enum with a raw value type that CoreData can handle you can then create a computed property that will store and retrieve the raw value from CoreData.

For my application, I had an enumeration for the days of the week so that you could choose which days you want to do a routine. I started by creating an enum that included the days of the week, and a few computed variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum Weekday: Int16, Identifiable, CaseIterable, Codable {
    case sunday = 1
    case monday = 2
    case tuesday = 3
    case wednesday = 4
    case thursday = 5
    case friday = 6
    case saturday = 7

    var id: Self { self }

    /// The capitalized name of the day of the week.
    public var name: String {
        "\(self)".capitalized
    }

    /// The first letter of the day of the week.
    public var abbreviation: String {
        "\(name.first ?? Character(""))"
    }
}

If you are just tracking a single value, all you need to do is and an attribute to your CoreData entity and create an extension for the entity and add a computed property to the entity.

1
2
3
4
5
6
7
8
9
10
extension Routine {
    var dayScheduled: Weekday {
        get {
            return Routine(rawValue: self.weekday)
        }
        set {
            self.weekday = Int16(newValue.rawValue)
        }
    }
}

For my application, you could have as few as one and as many as all the days, so I needed to do things a little differently. I started by creating an entity called WeekdayItem with a single attribute called value. Then I created a relationshsip between WeekDay and Routine. With Weekday.routine set to one and Routine.weekdays set to many. Then, in my Routine extension I created the computed variable WeekdayArray

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var weekdayArray: [Weekday] {
        get {
            let weekdayItems = weekdays?.allObjects as? [WeekdayItem] ?? []
            let sortedItems = weekdayItems.sorted { $0.value < $1.value }
            return sortedItems.compactMap { Weekday(rawValue: $0.value) }
        }
        set {
            let context = self.managedObjectContext!
            let newWeekdayItems = newValue.map { day in
                let item = WeekdayItem(context: context)
                item.value = Int16(day.rawValue)
                item.list = self
                return item
            }
            weekdays = NSSet(array: newWeekdayItems)
        }
    }

I do a little extra in my getter, sorting the days as well as just getting them, that way they always come out in order. The setter is a little more complicated as well since now it has to set multiple values in the WeekdayItem