Category: Core Data

Storing a Swift Enum in Core Data

You must do the following to store a Swift enum in Core Data:

  • Create an enum with a raw value with a type Core Data can store, such as an integer or a string.
  • Add an attribute for the enum to the Core Data entity. Set the attribute’s type to the raw value type.
  • Add a computed property to the entity to convert the enum to and from the attribute’s raw value.

Creating the Enum

Core Data has no way of knowing your enum. That’s why you must give your enum a raw value type. Supplying a raw value type lets Core Data store the enum value.

The following code shows an enum for issue priorities whose raw value type is an integer:

Core Data will save the integer values of the issue priorities.

Adding the Computed Property

Adding a computed property to the Core Data entity allows you to use the Swift enum in your code. The usual way to add a computed property is to create an extension for the Core Data entity type.

The computed property requires a getter and a setter. The getter creates an enum value from its raw value. The setter sets the raw value for the enum value.

The next example shows an Issue entity with a priorityLevel attribute of type Int64. The following code creates a computed property for the issue priority:

Now you can use the priority property to set issue priorities using the enum values.

When writing the setter make sure to use the same data type as the attribute in the Core Data model, Int64 in the example.

Saving Images in Core Data

A common question I see on Swift developer forums is how to save images in Core Data. You have to do the following to save an image in Core Data:

  • Add an attribute for the image with data type Binary Data.
  • Use external storage to save the image in a separate file.
  • Convert the image to a Data object to save it in Core Data.

Add Attribute of Type Binary Data

Core Data does not have a data type specifically for images. Binary data is the closest data type that Core Data supports.

Use External Storage

Images take a lot of space so it’s more efficient to save them in separate files instead of saving them in the persistent store where Core Data saves your app’s data.

To tell Core Data to save the image in its own file, select the attribute and open the data model inspector. Choose View > Inpsectors > Data Model to open the data model inspector.

CoreDataUseExtenralStorage

Select the Allows External Storage checkbox to save the image in a separate file.

Convert the Image to a Data Object

You must convert the image to a Data object to set the image attribute’s value in your code. Data is the underlying data type for a Core Data attribute of type Binary Data.

Swift apps generally use either UIImage or NSImage to work with images. UIImage has pngData and jpegData functions to convert an image to Data. NSImage has a tiffRepresentation function to convert to Data.

Storing an Array of Custom Structs in Core Data

A common question I see online about Core Data involves storing arrays. You have an array of a custom struct or class that you want to store in Core Data. Your initial instinct is to create an attribute for the array in your Core Data entity. But you discover that none of the available attribute types work. How do you store the array?

The Solution: Create a To-Many Relationship

There are two steps to storing an array of a custom struct or class in Core Data. The first step is to create a Core Data entity for your custom struct or class.

The second step is to add a to-many relationship in the Core Data entity where you want to store the array. The destination for the relationship is the entity you created for the custom struct or class. The to-many relationship holds the array items. Saving the main entity, the entity where you want to store the array, will save the array items.

Keep in mind that Core Data uses sets to store to-many relationships instead of arrays. Each item that Core Data stores must be unique. Each element of a set must also be unique, which is why Core Data uses sets.

A Simple Example

Let’s look at a simple example of creating a to-many relationship. Suppose you have a Note entity and you want to store a list of each note’s tags. Start by creating a Tag entity and adding the attributes you want a tag to have.

After creating the Tag entity, select the Note entity in Xcode’s data model editor and add a relationship. Name the relationship tags. Choose Tag as the destination.

When you select the relationship, the data model inspector should be visible on the right side of the project window. Choose To Many from the Type menu. The relationship must be to-many to store a set (array) of items.

SetToManyRelationship

Select the Ordered checkbox if you need the relationship items to be in a specific order.

After creating the relationship each note has a list of tags. With the right controls in the app, you can add and remove tags. When you save a note Core Data also saves the tags.

Core Data Code Generation

Starting with Xcode 8, you can let Xcode generate the code for your Core Data entities. Access an entity’s code generation options by selecting the entity and opening the data model inspector.

Xcode8CoreDataCodeGenMenu

The Codegen menu is where you specify the code generation for the entity. There are three code generation options.

  • Choosing Manual/None tells Xcode not to generate code for the entity. You must create the class for the entity by creating a new file or by choosing Editor > Create NSManagedObject Subclass.
  • Choosing Class Definition tells Xcode to create the entity’s class files when you build the project.
  • Choosing Category/Extension tells Xcode to create a Swift class extension for the entity when you build the project. The class extension has accessors for the entity’s attributes.

Where are the Source Code Files?

If you choose Class Definition or Category/Extension from the Codegen menu, you’ll notice there are no source code files for the entities in the project navigator. Where are the files?

The source code files are in the project’s derived data location. You’re not meant to access and edit the source code files Xcode creates for your Core Data entities. The point of Xcode creating the files is to automatically update the files when you make changes to the data model.

What do you do if you want to add methods to your Core Data entities? Create Swift class extension files for your entities and add your methods there.

Duplicate Classes

When you create a new Xcode project that uses Core Data, the code generation is initially set to Class Definition. If you manually add class files for your entities, your project won’t build because you have two versions of each class, the one you manually created and the one Xcode creates. This issue can be tough to find because you can’t see the Xcode-created class files in the project navigator.

There are two ways to deal with the duplicate class problem. The first way is to remove the class files you added to the project and let Xcode create the classes for you. The second way is to switch to manual code generation by choosing Manual/None from the Codegen menu.

Which Code Generation Option Should You Choose?

Choose manual code generation if you’re using version control in your project. By writing the entity’s code yourself, you can track the changes in your version control system and go back if you make a mistake.

Choose the Class Definition code generation option if you don’t want to worry about keeping the data model and code in sync. When you make changes to the data model, you don’t have to worry about updating the code if you choose Class Definition. Xcode updates the code for you.

Choose the Category/Extension code generation option if you need more control over the class hierarchy. If you choose the Class Definition code generation option or choose Editor > Create NSManagedObject Subclass to generate your classes manually, Xcode assumes the entity and class hierarchies are the same. If you want a different class hierarchy for your data model, choose the Category/Extension code generation option.