http://www.appcoda.com/introduction-to-core-data/
Editor’s note: After we published the tutorial about saving data in plist file, some readers asked about Core Data and how we can use it to save persistent information. This week, we work with Ziad Tamim,
an independent iOS developer, to give you an introduction of Core Data
and work with you to build a sample app using Core Data.
This tutorial talks about persistence on iPhone (or other iOS
devices). What I mean by persistence is to make data that’s in your apps
stay around between application launches. Persistence lets users store
persistent data and also retrieve it, so that users don’t have to
reenter all their data each time they use their applications. There are
multiple ways to store data in iOS devices but most of them aren’t good
enough to store a complicated data. They are usually used to save
settings or to preload some data such as “Property List” and “Archiving
Objects”. So that’s why we’ll go through Core Data to see how you can
utilize it to manage data in database.
The focus of the tutorial is to provide a practical introduction of
Core Data framework. I expect you’ve already gone through our tutorials
about
Storyboard and
UITableView. I will not give in-depth explanation about how to create view controller in Storyboard but you can always refer to the
earlier tutorials to gain better understanding.
Core Data is not a Database
When we talk about persistent data, people probably think of
database. If you are familiar with Oracle or MySQL, you know that
relational database stores data in the form of table, row and column,
and it usually facilitates access through what-so-called SQL query.
However, don’t mix up Core Data with database. Though
SQLite
database is the default persistent store for Core Data on iPhone, Core
Data is not a relational database. It is actually a framework that lets
developers store (or retrieve) data in database in an object-oriented
way. With Core Data, you can easily map the objects in your apps to the
table records in the database without even knowing any SQL.
To illustrate the concept, let’s begin and create your first app using Core Data. This app is called
My Store. It is a very simple app that stores all devices you have by collecting the
name,
version,
company.
MyStore App using Core Data
Creating a Sample App with Core Data
First let’s create a project with Core Data. Open Xcode and create a new Project, choose the template
Empty Application as shown below.
Create a New Project with Empty Application Template
At the next screen, enter
MyStore as a name of the project, select
iPhone in Devices family and don’t forget to select the options
Use Storyboards, Use Core Data, Use Automatic Reference Counting. Press next and create.
Set up Xcode Project Options – Remember to select Use Core Data
Core Data Stack
Before we start working on the project, you first have to understand the Core Data Stack:
Managed Object Model – It describes the schema that
you use in the app. If you have a database background, think of this as
the database schema. However, the schema is represented by a collection
of objects (also known as entities). In Xcode, the Managed Object Model
is defined in a file with the extension .
xcdatamodeld. You can use the visual editor to define the entities and their attributes, as well as, relationships.
Persistent Store Coordinator – SQLite is the default
persistent store in iOS. However, Core Data allows developers to setup
multiple stores containing different entities. The Persistent Store
Coordinator is the party responsible to manage different persistent
object stores and save the objects to the stores. Forget about it you
don’t understand what it is. You’ll not interact with Persistent Store
Coordinator directly when using Core Data.
Managed Object Context – Think of it as a “scratch
pad” containing objects that interacts with data in persistent store.
Its job is to manage objects created and returned using Core Data. Among
the components in the Core Data Stack, the Managed Object Context is
the one you’ll work with for most of the time. In general, whenever you
need to fetch and save objects in persistent store, the context is the
first component you’ll talk to.
The below illustration can probably give you a better idea about the Core Data Stack:
Core Data Stack
Defining Managed Object Model
Let’s move on to build the app. The first step is to open the Data Model named
MyStore.xcdatamodeld
and define the object model. Here we’ll define a Device entity that
will be used to store the device information to database. To create an
entity, click the + button in the bottom-left of the editor view and
name the entity as
Device.
Add Device entity in the model
Once you create a new entity, you need to add attributes to it. Click on the
+ button in the attributes section to do that. Add three attributes including name, version and company. Set the type as String.
Add 3 Attributes (company, name and version) to the Device entity
Designing the User Interface
Note: While we encourage you to build the user interface, you can also skip the procedures and
download the project template from here.
The template already comes with the Storyboard and set up all the view
controller classes for you. This gives you a good starting point to work
on Core Data. If you use the template, you can skip this section and go
directly to the “Diving Core Data” section.
The next thing we need to do is to create the Storyboard that defines
the views of our app. Navigate to File > New > New File and
choose Storyboard in the
User Interface template. Click next and select the iPhone device family, click create.
Creating the Storyboard
Once created, make sure to set the “Storyboard” you’ve just created as the main storyboard in the project setting.
Set the Storyboard you just created as the Main Storyboard
Also don’t forget to delete all the generated code in the method
-(BOOL)application:application didFinishLaunchingWithOptions:launchOptions inside the
AppDelegate file. The method should be as simple as this:
1 2 3 4
|
- (BOOL)application :(UIApplication *)application didFinishLaunchingWithOptions :(NSDictionary *)launchOptions
{
return YES;
}
|
Go to Storyboard and create the user interface like below:
MyStore App – Storyboard
First, drag a Table View Controller and embed it in a Navigation
Controller. Drag a button to the top-right part of navigation bar and
set the identifier as “Add”. This will automatically change the button
to a “+” button. Next, select the prototype cell and change its style to
“Right Detail”.
Creating the Table View Controller
Drag a View Controller to the Storyboard and add a Navigation Bar to
the top of the screen. Next, drag two buttons into the navigation bar.
Name one as “Cancel” and the other one as “Save”. In the content view,
add three text fields and name the placeholder attributes as “Name”,
“Version” and “Company”.
This detail view will be shown when user taps the “+” button in the
table view controller. So finally, press and hold the Control key, click
the “+” button and drag towards the detail view controller. Select
“Modal” as the Segue action to connect the table view controller and
detail view controller.
Designing the Detail View Controller
Creating View Controller Classes
Create a new class by right-clicking on the
MyStore folder > New File > Objective-C class, and name the class as
DeviceViewController. Make it as a subclass of
UITableViewController. Navigate to the Storyboard, select the Table View Controller and associate it with the DeviceViewController class.
Set the Custom Class as DeviceViewController
Once done, do the same steps to create a new class named
DeviceDetailViewControllerUIViewController. Again, go to Storyboard and set the custom class of the detail view controller as the “DeviceDetailViewController”.
Lastly, wire up the UITextFields to the
DeviceDetailViewController header file and create two action methods for the save and cancel buttons respectively.
Creating IBOutlet and Action Methods
Your code should like this:
1 2 3 4 5 6
|
@property (weak, nonatomic) IBOutlet UITextField *nameTextField;
@property (weak, nonatomic) IBOutlet UITextField *versionTextField;
@property (weak, nonatomic) IBOutlet UITextField *companyTextField;
- (IBAction)cancel:(id)sender;
- (IBAction)save:(id)sender;
|
Diving into Core Data
With the user interface, it’s time to go into the details of Core
Data. Apparently, there are a couple of areas we have to implement:
- Save device information in the Detail View Controller
- Fetch device information from persistent store (i.e. SQLite database) and populate the data into Table View Controller
We’ll look into the implementation one by one.
Saving Device Information
First, we need to implement the
DeviceDetailViewController to let user add the devices to the database. Open up the
DeviceDetailViewController.m file and add the following code after @implementation DeviceDetailViewController:
1 2 3 4 5 6 7 8
|
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication ] delegate ];
if ([delegate performSelector :@selector(managedObjectContext )]) {
context = [delegate managedObjectContext ];
}
return context;
}
|
Recalled that we’ve selected the Core Data option when creating the
project, Xcode automatically defines a managed object context in
AppDelegate. This method allows us to retrieve the managed object
context from the AppDelegate. Later we’ll use the context to save the
device data.
Next, we’ll implement the “save” and “cancel”, add the necessary code to look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
- (IBAction )cancel :(id)sender {
[self dismissViewControllerAnimated :YES completion :nil];
}
- (IBAction )save :(id)sender {
NSManagedObjectContext *context = [self managedObjectContext ];
// Create a new managed object
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName :@"Device" inManagedObjectContext :context ];
[newDevice setValue :self.nameTextField.text forKey :@"name"];
[newDevice setValue :self.versionTextField.text forKey :@"version"];
[newDevice setValue :self.companyTextField.text forKey :@"company"];
NSError *error = nil;
// Save the object to persistent store
if (![context save :&error ]) {
NSLog (@"Can't Save! %@ %@", error, [error localizedDescription ]);
}
[self dismissViewControllerAnimated :YES completion :nil];
}
|
When user taps the “Cancel” button, we expect the app to close the
detail view controller. Line 2 of the above code invokes the
dismissViewControllerAnimated method to dismiss the current view
controller with animation.
For the “save” method, we first grab the managed object context.
Every object that Core Data stores is inherited from NSManagedObject. So
we first create a new instance of NSManagedObject for the “Device”
entity that we’ve defined in the object model. NSEntityDescription class
provides a method named “insertNewObjectForEntityForName” for developer
to create a managed object. Once you created the managed object (i.e.
newDevice), you can set the attributes (name, version, company) using
the user input. Lastly, we call up the “save:” method of the context to
save the object into database.
You can now hit the Run button to try out your app. Tap the “+”
button to bring up the Detail View and save a new device. However, the
new device is not yet displayed in the table. Let’s move on to see how
you can fetch the device information from database.
Fetching Device Information
Open
DeviceViewController.m, add a “devices” property to it so we can save all the devices received.
1 2 3 4
|
@interface DeviceViewController ()
@property (strong ) NSMutableArray *devices;
@end
|
Again, add the following code after “@implementation DeviceViewController” for grabbing the managed object context:
1 2 3 4 5 6 7 8 9
|
- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication ] delegate ];
if ([delegate performSelector :@selector(managedObjectContext )]) {
context = [delegate managedObjectContext ];
}
return context;
}
|
Next, add a viewDidAppear method:
1 2 3 4 5 6 7 8 9 10 11
|
- (void)viewDidAppear :(BOOL)animated
{
[super viewDidAppear :animated ];
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext ];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc ] initWithEntityName :@"Device"];
self.devices = [[managedObjectContext executeFetchRequest :fetchRequest error :nil] mutableCopy ];
[self.tableView reloadData ];
}
|
Like what we’ve done in the Detail View Controller, we first grab the
managed object context. To fetch device information from database, the
code above creates a new instance of
NSFetchRequest and set the entity
Device
and invokes “executeFetchRequest” method to retrieve all the devices
from the database. If you are familiar with relational databases, this
instance works like the SELECT clause.
Note:
If you’re new to viewDidAppear method, it is a method that will be
called automatically every time a view is displayed. It’s unlike the
viewDidLoad method that is invoked once when the controller is loaded.
Populating Device Information into Table View
As we would like to display these data into the table view we need to
implement the data source of it, to do that add the below code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
- (NSInteger )numberOfSectionsInTableView :(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger )tableView :(UITableView *)tableView numberOfRowsInSection :(NSInteger )section
{
// Return the number of rows in the section.
return self.devices.count;
}
- (UITableViewCell *)tableView :(UITableView *)tableView cellForRowAtIndexPath :(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier :CellIdentifier forIndexPath :indexPath ];
// Configure the cell...
NSManagedObject *device = [self.devices objectAtIndex :indexPath.row ];
[cell.textLabel setText :[NSString stringWithFormat :@"%@ %@", [device valueForKey :@"name"], [device valueForKey :@"version"]]];
[cell.detailTextLabel setText :[device valueForKey :@"company"]];
return cell;
}
|
If you have used UITableViewController before, the code above is the
simple way to display data into the table view. If you check the code
you will notice the
NSMangedObject is pretty much like
NSDictionary. It gathers all the attributes of the entity (i.e. Device)
and you can simply use the “valueForKey” method to grab the attribute
value.
That’s it. Let’s try to run the app and test it. If everything is
okay, your app should like this. Try to add some devices and the device
information should be populated automatically in the table view.
MyStore App using Core Data
What’s Coming Next
It’s a lengthy tutorial but we try to elaborate the implementation as
detail as possible so everyone can follow. As you can see, with Core
Data, you don’t have to care about SQL to save and retrieve data from
database. Everything is done behind the scene. This tutorial kicks off
the first part of Core Data series. Later we’ll talk more about object
relationship and
Lastly, let me end the tutorial with an exercise. Try to complete the
app by adding the functions that let user update and delete an existing
device by selecting a row in the table view.
Hope you enjoy the tutorial and feel to leave us comment.
Update: Check out
part 2 of the Core Data tutorial series!