Skip to main content
Core Data - customising our data model

When you are using Core Data, and let's say that you have a requirement to have an attribute where the options available should be a defined list.  Such as "Not Started", "Pending", "In Development" and "Completed".  How do you create this as an option in Core Data?  

Thinking out loud, the practical solution would be to use an enum.  But how do you use a enum in this instance?  As you might have noticed, unfortunately, it can’t be created inside the .xcdatamodeld file itself.  However, by creating and designing the Core Data entity a corresponding class can be created under the hood.  This class is accessible and modifiable by clicking on the relevant entity, going to the Xcode toolbar and selecting Editor > Create NSObjectManaged Subclass

By way of example and to make this easier reading the name of the entity in Core Data will be named Product.  After creating the subclass, Xcode generated two files for us. The Product+CoreDataClass.swift file holds the class itself, and the Product+CoreDataProperties.swift contains its properties inside an extension. 

After creating the data model’s subclass, you now need to inform Xcode that the data model is no longer defined by the visual builder in our .xcdatamodeld file only, instead manually defined by the corresponding subclass was just created.  This is achieved by opening the .xcdatamodeld file, click on the Product entity and open the data model inspector.  Change the Codegen mode to Manual/None.

At this point, we can remove the question marks from the String-type properties since we don’t want them to be Optionals. Xcode should also create another extension adopting the Identifiable protocol (this will make it easier for us to use Product instances inside the ContentView’s List later). Since we declared an id property, we already conform to this protocol.

extension Product: Identifiable {

   @nonobjc public class func fetchRequest() -> NSFetchRequest<Product> {
      return NSFetchRequest<Product>(entityName: "Product")
   }    

   @NSManaged public var id: Int32
   @NSManaged public var name: String
   @NSManaged public var overview: String
   @NSManaged public var timestamp: Date?
   @NSManaged public var status: String
}

extension Product : Identifiable {

}

Below the Product extension declare the Status enum with the different cases.

enum Status: String {
    case notstarted = "Not Started"
    case pending = "Pending"
    case indevelopment = "In Development"
    case completed = "Completed"
}

As it currently stands using the Status enum as the status’ data type, we will get an error.  @NSManagedObject properties can’t be used with enums directly.  So how do we save the status of a product in Core Data?  Yes you guessed it, there is a workaround.  Using our NSManaged status property but not of our Status type.  Instead, it should be a String again.  Don't worry, we are moving forwards.  Now add another regular variable called productStatus.  As it’s not an NSManaged property, it can be of the type Status.  The you able to assign a setter and getter to our productStatus.  When this property is set, it will also set the NSManaged property accordingly.  Using a getter, we try to convert the status string to a Status case when retrieving it.

extension Product {
    //...
    @NSManaged public var status: String
    var productStatus: Status {
        set {
            status = newValue.rawValue
        }
        get {
            Status(rawValue: status) ?? .pending
        }
    }
}

 

Related articles

Andrew Fletcher12 Aug 2022
Using SwiftUI URLComponent to change a URL's scheme
The challenge I was facing, I had written a script to scan barcodes and use Google book API to view the contents. &nbsp;However, a snippet of the JSON response { "contentVersion": "0.2.0.0.preview.0", "panelizationSummary": { "containsEpubBubbles": false, ...
Andrew Fletcher12 Mar 2021
SwiftUI - custom navigation bar title
When managing a navigation title in Swift, you will have trodden down the path. &nbsp;Previously you have entered something like .navigationTitle("Title") If&nbsp;you wanted to alter the font used for the navigation area, alter the init() in the view: struct YourView: View { //...