Getting Started with Core Data Tutorial
Learn the basics of building the data layer of your iOS app in this getting started with Core Data tutorial! By Pietro Rea.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Getting Started with Core Data Tutorial
35 mins
Modeling Your Data
Now you know how to check for persistence, you can dive into Core Data. Your goal for the HitList app is simple: persist the names you enter so they’re available for viewing after a fresh app launch.
Up to this point, you’ve been using plain old Swift strings to store the names in memory. In this section, you’ll replace these strings with Core Data objects.
The first step is to create a managed object model, which describes the way Core Data represents data on disk.
By default, Core Data uses a SQLite database as the persistent store, so you can think of the Data Model as the database schema.
However, don’t assume all Core Data classes contain the word “managed”. Most don’t. For a comprehensive list of Core Data classes, check out the Core Data framework reference in the documentation browser.
NSManagedObjectContext
, chances are you are dealing with a Core Data class. “Managed” refers to Core Data’s management of the life cycle of Core Data objects.
However, don’t assume all Core Data classes contain the word “managed”. Most don’t. For a comprehensive list of Core Data classes, check out the Core Data framework reference in the documentation browser.
Since you’ve elected to use Core Data, Xcode automatically created a Data Model file for you and named it HitList.xcdatamodeld.
Open HitList.xcdatamodeld. As you can see, Xcode has a powerful Data Model editor:
The Data Model editor has a lot of features you can explore later. For now, let’s focus on creating a single Core Data entity.
Click on Add Entity on the lower-left to create a new entity. Double-click the new entity and change its name to Person, like so:
You may be wondering why the model editor uses the term Entity.
Weren’t you simply defining a new class? As you’ll see shortly, Core Data comes with its own vocabulary. Here’s a quick rundown of some terms you’ll commonly encounter:
-
An entity is a class definition in Core Data. The classic example is an
Employee
or aCompany
. In a relational database, an entity corresponds to a table. -
An attribute is a piece of information attached to a particular entity. For example, an
Employee
entity could have attributes for the employee’sname
,position
andsalary
. In a database, an attribute corresponds to a particular field in a table. -
A relationship is a link between multiple entities. In Core Data, relationships between two entities are called to-one relationships, while those between one and many entities are called to-many relationships. For example, a
Manager
can have a to-many relationship with a set of employees, whereas an individualEmployee
will usually have a to-one relationship with his manager.
Now you know what an attribute is, you can add an attribute to Person
object created earlier. Still in HitList.xcdatamodeld, select Person
on the left-hand side and click the plus sign (+) under Attributes.
Set the new attribute’s name to, er, name and change its type to String:
In Core Data, an attribute can be of one of several data types.
Saving to Core Data
Open ViewController.swift, add the following Core Data module import below the UIKit
import:
import CoreData
This import is all you need to start using the Core Data API in your code.
Next, replace the names
property definition with the following:
var people: [NSManagedObject] = []
You’ll store Person
entities rather than string names, so you rename the array serving as the table view’s data model to people
. It now holds instances of NSManagedObject
rather than simple strings.
NSManagedObject
represents a single object stored in Core Data; you must use it to create, edit, save and delete from your Core Data persistent store. As you’ll see shortly, NSManagedObject
is a shape-shifter. It can take the form of any entity in your Data Model, appropriating whatever attributes and relationships you defined.
Since you’re changing the table view’s model, you must also replace both data source methods implemented earlier. Replace your UITableViewDataSource
extension with the following:
// MARK: - UITableViewDataSource
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return people.count
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
let person = people[indexPath.row]
let cell =
tableView.dequeueReusableCell(withIdentifier: "Cell",
for: indexPath)
cell.textLabel?.text =
person.value(forKeyPath: "name") as? String
return cell
}
}
The most significant change to these methods occurs in tableView(_:cellForRowAt:)
. Instead of matching cells with the corresponding string in the model array, you now match cells with the corresponding NSManagedObject
.
Note how you grab the name
attribute from the NSManagedObject
. It happens here:
cell.textLabel?.text =
person.value(forKeyPath: "name") as? String
Why do you have to do this? As it turns out, NSManagedObject
doesn’t know about the name
attribute you defined in your Data Model, so there’s no way of accessing it directly with a property. The only way Core Data provides to read the value is key-value coding, commonly referred to as KVC.
Key-value coding is available to all classes inheriting from NSObject
, including NSManagedObject
. You can’t access properties using KVC on a Swift object that doesn’t descend from NSObject
.
NSMangedObject
behave somewhat like a dictionary at runtime.
Key-value coding is available to all classes inheriting from NSObject
, including NSManagedObject
. You can’t access properties using KVC on a Swift object that doesn’t descend from NSObject
.
Next, find addName(_:)
and replace the save
UIAlertAction
with the following:
let saveAction = UIAlertAction(title: "Save", style: .default) {
[unowned self] action in
guard let textField = alert.textFields?.first,
let nameToSave = textField.text else {
return
}
self.save(name: nameToSave)
self.tableView.reloadData()
}
This takes the text in the text field and passes it over to a new method named save(name:)
. Xcode complains because save(name:)
doesn’t exist yet. Add it below addName(_:)
:
func save(name: String) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
// 1
let managedContext =
appDelegate.persistentContainer.viewContext
// 2
let entity =
NSEntityDescription.entity(forEntityName: "Person",
in: managedContext)!
let person = NSManagedObject(entity: entity,
insertInto: managedContext)
// 3
person.setValue(name, forKeyPath: "name")
// 4
do {
try managedContext.save()
people.append(person)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
This is where Core Data kicks in! Here’s what the code does:
Think of saving a new managed object to Core Data as a two-step process: first, you insert a new managed object into a managed object context; once you’re happy, you “commit” the changes in your managed object context to save it to disk.
Xcode has already generated a managed object context as part of the new project’s template. Remember, this only happens if you check the Use Core Data checkbox at the beginning. This default managed object context lives as a property of the NSPersistentContainer
in the application delegate. To access it, you first get a reference to the app delegate.
You may be wondering what an NSEntityDescription
is all about. Recall earlier, NSManagedObject
was called a shape-shifter class because it can represent any entity. An entity description is the piece linking the entity definition from your Data Model with an instance of NSManagedObject
at runtime.
-
Before you can save or retrieve anything from your Core Data store, you first need to get your hands on an
NSManagedObjectContext
. You can consider a managed object context as an in-memory “scratchpad” for working with managed objects.Think of saving a new managed object to Core Data as a two-step process: first, you insert a new managed object into a managed object context; once you’re happy, you “commit” the changes in your managed object context to save it to disk.
Xcode has already generated a managed object context as part of the new project’s template. Remember, this only happens if you check the Use Core Data checkbox at the beginning. This default managed object context lives as a property of the
NSPersistentContainer
in the application delegate. To access it, you first get a reference to the app delegate. -
You create a new managed object and insert it into the managed object context. You can do this in one step with
NSManagedObject
’s static method:entity(forEntityName:in:)
.You may be wondering what an
NSEntityDescription
is all about. Recall earlier,NSManagedObject
was called a shape-shifter class because it can represent any entity. An entity description is the piece linking the entity definition from your Data Model with an instance ofNSManagedObject
at runtime.
-
With an
NSManagedObject
in hand, you set thename
attribute using key-value coding. You must spell the KVC key (name
in this case) exactly as it appears in your Data Model, otherwise, your app will crash at runtime. -
You commit your changes to
person
and save to disk by callingsave
on the managed object context. Notesave
can throw an error, which is why you call it using thetry
keyword within ado-catch
block. Finally, insert the new managed object into thepeople
array so it shows up when the table view reloads.
That’s a little more complicated than using an array of strings, but not too bad. Some of the code here, such as getting the managed object context and entity, could be done just once in your own init()
or viewDidLoad()
then reused later. For simplicity, you’re doing it all in the same method.
Build and run the app, and add a few names to the table view:
If the names are actually stored in Core Data, the HitList app should pass the persistence test. With the app in the foreground, go to the fast app switcher and then terminate it.
From Springboard, tap the HitList app to trigger a fresh launch. Wait, what happened? The table view is empty:
You saved to Core Data, but after a fresh app launch, the people
array is empty! That’s because the data is sitting on disk waiting for you, but you’re not showing it yet.