Saving UIColor and Array in CoreData Using Transformable

Samuel Folledo
Level Up Coding
Published in
5 min readJun 29, 2020

--

Oh so you have been coding in Swift for a while and you feel like the Hulk with all your knowledge? You probably have experience persisting data using UserDefaults, Keychain, or flex worthy persistent storage like FileSystem. Then you start picking up CoreData, should be easy right? Then you find out you are Hulk trying to pick up Thor’s hammer.

Enough with Marvel references, let’s get started.

Getting Started

Start by creating a project. For full source code click here

xcdatamodeld

Xcode comes with a beautiful visualization for our CoreData file called xcodeProjectName.xcdatamodeld . Now let’s add a Person entity by clicking on the + and add the Attributes exactly like below.

Also, click Person entity → Data Model Inspector → Codegen, and change it to Manual/None. As we will customize this ourselves which is a very common practice.

Transformable

Core Data allows us to store integers, booleans, strings, UUID, date, etc. but sometimes we want to store a specific data type like UIColor, UIImage, our own class, struct, or enum, and even arrays, but that is simply not an option in Attribute’s Type.

Say hello to Transformable property type which allows us to store custom data types as an object of the declared attribute for a record of an Entity. The only requirement is that our custom type should conform to NSCoding or we need to provide a custom value transformer.

When you declare a property as Transformable Core Data converts your custom data type into binary Data when it is saved to the persistent store and converts it back to your custom data type when fetched from the store. It does this through a value transformer.

This is a very useful feature, but we must use it carefully, especially if we use mutable types as the value of the transformable attributes. For more information read this article.

You can optionally specify your Attribute’s Custom Class here, or it will default to NSObject, which of course we can change once we create our own NSManagedObject.

Creating NSManagedObject

Let’s create our NSManagedObject by clicking the tab Editor → Create NSManagedObject Subclass…

Make sure our DataModel is selected and click Next, then make sure our Person entity is selected and click Next. Specify the Group directory you want to store these new files to and hit Create.

This will create two files for us Person+CoreDataClass.swift and Person+CoreDataProperties.swift . Now go to Person+CoreDataProperties.swift file and you will see that favoriteColor is of type UIColor because we specified it, but colors is of type NSObject. Make sure your Person+CoreDataProperties.swift file looks like this…

import Foundation
import CoreData
import UIKit.UIColor //add this
extension Person {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Person> {
return NSFetchRequest<Person>(entityName: “Person”)
}
@NSManaged public var colors: [UIColor]? //update NSObject? to [UIColor]?
@NSManaged public var favoriteColor: UIColor?
@NSManaged public var name: String?
}

Like I previously mentioned, we can update these by changing color’s data type to [UIColor] and don’t forget to import UIKit’s UIColor.

CoreDataStack

Copy this simple CoreDataStack

import Foundation
import CoreData
class CoreDataStack {
private let modelName: String
init(modelName: String) {
self.modelName = modelName
}
lazy var mainContext: NSManagedObjectContext = {
return self.storeContainer.viewContext
}()
private lazy var storeContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: self.modelName)
container.loadPersistentStores { (storeDescription, error) in
if
let error = error as NSError? {
print("Unresolved error \(error), \(error.userInfo)")
}
}
return container
}()
}
// MARK: Internal
extension
CoreDataStack {
func saveContext () {
guard mainContext.hasChanges else { return }
do {
try mainContext.save()
} catch let nserror as NSError {
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}

Lastly, load and save like we normally do…

func loadPerson() {
let personFetchRequest: NSFetchRequest<Person> = Person.fetchRequest() //make a Person Entity fetch request
do {
let results = try coreDataStack.mainContext.fetch(personFetchRequest)
if results.count > 0 { //found a person
person = results.first
} else { //no person found
person = Person(context: coreDataStack.mainContext)
coreDataStack.saveContext()
}
} catch let error as NSError {
print("Error: \(error) description: \(error.userInfo)")
}
}
func handleSavePerson() {
guard let name = nameTextField.text, !name.isEmpty else { return }
person!.name = name
person!.colors = [.red, .orange, .yellow, .green, .blue, .purple]
person!.favoriteColor = person!.favoriteColor
//save the managed object context
do {
try coreDataStack.mainContext.save()
} catch let error as NSError {
print("Error: \(error), description: \(error.userInfo)")
}
}

That’s it! The background should be white at first time running the app. Additionally, putting a name, selecting a color, and clicking the Save will update the Person’s properties in our CoreData. Rerun the app and you will see your name on the textfield and the background color persisted.

As an extra challenge, try storing a UIImage in CoreData as Transformable of course. You can find the source code here.

Enjoy coding! 😁

External Links

--

--