Latest Posts:

Getting Started with Core Data Tutorial

iOS9_feast_CoreData
In this getting started with Core Data tutorial, you’ll write your very first Core Data app using Swift 2.0. You’ll see how easy it is to get started with all the resources provided in Xcode, from the starter code templates to the data model editor. By the end of the tutorial you’ll know how to:
  • model data you want to store in Core Data using Xcode’s model editor;
  • add new records to Core Data;
  • fetch a set of records from Core Data;
  • display the fetched results to the user in a table view.
You’ll also get a sense of what Core Data is doing behind the scenes, and how you can interact with the various moving pieces there. We’re getting ahead of ourselves though – it’s time to build an app!

Getting started

Open Xcode and create a new iPhone project based on the Single View Application template. Call the app HitList and make sure Use Core Data is checked:
CoreData2-2
Checking the Use Core Data box will cause Xcode to generate boilerplate code for what’s known as aCore Data stack in AppDelegate.swift.
The Core Data stack consists of a set of objects that facilitate saving and retrieving information from Core Data. There’s an object to manage the Core Data state as a whole, an object representing the data model, and so on.
Note: Not all Xcode templates under iOS/Application have the option to start with Core Data. In Xcode 7, only the Master-Detail Application and the Single View Application templates have the Use Core Data checkbox.
The idea for this sample app is simple. There will be a table view with a list of names for your very own “hit list”. You’ll be able to add names to this list and eventually, you’ll use Core Data to make sure the data is stored between sessions. We don’t condone violence on this site so you can think of this app as a “favorites list” to keep track of your friends too, of course! ;]
Click on Main.storyboard to open it in Interface Builder. Next, embed the initial view controller in a navigation controller. From Xcode’s Editor menu, select Embed In…\ Navigation Controller.
CoreData2-4
Back in Interface Builder, drag a Table View from the object library into the view controller so that it covers the view controller’s entire view.
If not already open, open Interface Builder’s document outline by selecting the icon located in the lower left corner of your canvas. Ctrl-drag from the Table View in the document outline to its parent view and select the Leading Space to Container Margin constraint:
CoreData2-5
Do this three more times, selecting the constraints Trailing Space to Container MarginVertical Spacing to Top Layout Guide and finally, Vertical Spacing to Bottom Layout Guide. If you’re familiar with Auto Layout, you’ll recognize that selecting those four constraints will constrain the size of the table view to the size of its parent view.
Next, drag a Bar Button Item and place it on the view controller’s navigation bar. Finally, double-click the bar button item to change its text to Add. Your canvas should now look similar to the following screenshot:
CoreData2-7
Every time you tap Add on the top-right, an alert containing a text field will appear on the screen. From there you’ll be able to type someone’s name into the text field. Dismissing the alert will save the name and refresh the table view with all the names you’ve saved up to that point.
Before you can do that, you need to make the view controller the table view’s data source. In the canvas, Ctrl-drag from the table view to the yellow view controller icon above the navigation bar, as shown below, and click on dataSource:
CoreData2-9
In case you were wondering, you don’t need to set up the table view’s delegate since tapping on the cells won’t trigger any action. It doesn’t get simpler than this!
Open the Assistant Editor by hitting Command-Option-Enter or by selecting the middle button on the Editor toolset on the Xcode bar. Ctrl-drag from the table view onto ViewController.swift, inside the class definition to insert an outlet:
CoreData2-10
Name the new IBOutlet property tableView, resulting in the following line:
@IBOutlet weak var tableView: UITableView!
Ctrl-drag from the Add bar button item onto ViewController.swift, but this time, create an action instead of an outlet and name the method addName:
@IBAction func addName(sender: AnyObject) {
 
}
You can now refer to the table view and the bar button item’s action in code. Next, set up the model for the table view. Add the following property to ViewController.swift:
//Insert below the tableView IBOutlet
var names = [String]()
names is a mutable Array to hold the strings for the table view to display.
Replace the implementation of viewDidLoad() with the following:
override func viewDidLoad() {
  super.viewDidLoad()
  title = "\"The List\""
  tableView.registerClass(UITableViewCell.self,
    forCellReuseIdentifier: "Cell")
}
This will set a title and register the UITableViewCell class with the table view. You do this so that when you dequeue a cell, the table view will return a cell of the correct type.
Still in ViewController.swift, declare that ViewController will conform to the UITableViewDataSourceprotocol by editing the class declaration:
//Add UITableViewDataSource to class declaration
class ViewController: UIViewController, UITableViewDataSource {
Immediately, Xcode will complain about ViewController not conforming to the protocol. BelowviewDidLoad(), implement the following data source methods to fix the error:
// MARK: UITableViewDataSource
func tableView(tableView: UITableView,
  numberOfRowsInSection section: Int) -> Int {
  return names.count
}
 
func tableView(tableView: UITableView,
  cellForRowAtIndexPath
  indexPath: NSIndexPath) -> UITableViewCell {
 
  let cell =
  tableView.dequeueReusableCellWithIdentifier("Cell")
 
  cell!.textLabel!.text = names[indexPath.row]
 
  return cell!
}
If you’ve ever worked with UITableView, this code should look very familiar. The first method says that the table view will have as many rows as the names array has strings.
The second method, tableView(_:cellForRowAtIndexPath:), dequeues table view cells and populates them with the corresponding string in the names array.
Don’t run the app just yet. First, you need a way to input names so the table view can display them.
Implement the addName IBAction method you Ctrl-dragged into your code earlier:
//Implement the addName IBAction
@IBAction func addName(sender: AnyObject) {
 
  let alert = UIAlertController(title: "New Name",
    message: "Add a new name",
    preferredStyle: .Alert)
 
  let saveAction = UIAlertAction(title: "Save",
    style: .Default,
    handler: { (action:UIAlertAction) -> Void in
 
      let textField = alert.textFields!.first
      self.names.append(textField!.text!)
      self.tableView.reloadData()
  })
 
  let cancelAction = UIAlertAction(title: "Cancel",
    style: .Default) { (action: UIAlertAction) -> Void in
  }
 
  alert.addTextFieldWithConfigurationHandler {
    (textField: UITextField) -> Void in
  }
 
  alert.addAction(saveAction)
  alert.addAction(cancelAction)
 
  presentViewController(alert,
    animated: true,
    completion: nil)
}
Every time you tap the Add bar button item, this method presents an UIAlertController with a text field and two buttons, Save and Cancel.
Save takes whatever text is currently in the text field, inserts it into the name array and reloads the table view. Since the names array is the model backing the table view, whatever you typed into the text field will appear in the table view.
Finally it’s time to build and run your app for the first time. Tap the Add bar button item. The alert controller will look like this:
CoreData2-12
Add four or five names to the list. You should wind up with something like this:
CoreData2-14
Your table view will display the data and your array will store the names, but the big thing missing here ispersistence. The array is in memory but if you force quit the app or reboot your device, your hit list will be wiped out.
Core Data provides persistence, meaning it can store data in a more durable state so that it can outlive an app re-launch or a device reboot.
You haven’t added any Core Data yet, so nothing should persist after you navigate away from the app. Let’s test this out. Press the Home button if you’re using a physical device or the equivalent (Shift+⌘+H) if you’re on the Simulator. This will take you back to the familiar app grid on the home screen:
CoreData2-15
From the home screen, tap the HitList icon to bring the app back to the foreground. The names are still on the screen. What happened?
When you tap the Home button, the app that’s currently in the foreground goes to the background. When this happens, the operating system flash-freezes everything currently in memory, including the strings in the names array. Similarly, when it’s time to wake up and return to the foreground, the operating system restores what used to be in memory as if you’d never left.
Apple introduced these advances in multitasking back in iOS 4. They create a seamless experience for iOS users but add a wrinkle to the definition of persistence for iOS developers. Are the names really persisted?
No, not really. If you had completely killed the app in the fast app switcher or turned off your phone, those names would be gone. You can verify this, as well. With the app in the foreground, double tap the Home button to enter the fast app switcher, like so:
CoreData2-17
From here, flick the HitList app snapshot upwards to terminate the app. There should be no trace of HitList in living memory (no pun intended). Verify that the names are gone by returning to the home screen and tapping on the HitList icon to trigger a fresh launch.
This difference between flash-freezing and persistence may be obvious if you’ve been working with iOS for some time and are familiar with the way multitasking works. In a user’s mind, however, there is no difference. The user doesn’t care if the names are “still there” because the app went into the background and came back, or because the app saved and reloaded them.
All that matters is that the names are still there when she comes back!
So the real test of persistence, the one you will use in this tutorial, is whether your data is still there after a fresh app launch.

Modeling your data

Now that you know how to check for persistence, let’s get started with Core Data. Your goal for the HitList app is simple: to 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 spells out the way Core Data represents data on disk. By default, Core Data uses an SQLite database as the persistent store, so you can think of the data model as the database schema.
Note: You’ll come across the word “managed” quite a bit in this tutorial. If you see “managed” in the name of a class, such as in 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 that all Core Data classes contain the word “managed”—actually, most don’t. For a comprehensive list of Core Data classes, check out the Objective-C umbrella headerCoreData/CoreData.h.
Since you elected to use Core Data when you created the HitList project, Xcode automatically created a data model file for you and named it HitList.xcdatamodeld.
CoreData2-19
Click on HitList.xcdatamodeld to open it. As you can see, Xcode has a powerful data model editor that looks like this:
CoreData2-20
The data model editor has a lot of features. 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 on the new entity and change its name to Person, like so:
CoreData2-21
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 of the terms you’ll commonly encounter:
    • An entity is a class definition in Core Data. The classic example is an Employee or a Company. 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’s name, position and salary. In a database, an attribute corresponds to a particular field in a table.
    • 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 calledto-many relationships. For example, a Manager can have a to-many relationship with a set of employees, whereas an individual Employee will have a to-one relationship with his manager.
Note: As you’ve probably noticed, entities sound a lot like a classes. Likewise, attributes/relationships sound a lot like properties. What’s the difference? You can think of a Core Data entity as a class “definition” and the managed object as an instance of that class.
Now that you know what an attribute is, go back to the model editor and add an attribute to Person. Select Person on the left-hand side and click the plus sign (+) under Attributes.
Set the new attribute’s name to, well, name and change its type to String:
CoreData2-22
In Core Data, an attribute can be of one of several data types — one of them is the string type.

Saving to Core Data

Import the Core Data module at the top of ViewController.swift:
//Add below "import UIKit"
import CoreData
You may have had to link frameworks manually in your project’s Build Phases if you’ve worked with Objective-C frameworks. In Swift, a simple import statement is all you need to start using Core Data APIs in your code.
Next, replace the table view’s model with the following:
//Change “names” to “people” and [String] to [NSManagedObject]
var people = [NSManagedObject]()
You’ll be storing Person entities rather than just names, so you rename the Array that serves as the table view’s data model to people. It now holds instances of NSManagedObject rather than simple Swift 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 you implemented earlier with the following to reflect these changes:
//Replace both UITableViewDataSource methods
func tableView(tableView: UITableView,
  numberOfRowsInSection section: Int) -> Int {
    return people.count
}
 
func tableView(tableView: UITableView,
  cellForRowAtIndexPath
  indexPath: NSIndexPath) -> UITableViewCell {
 
    let cell =
    tableView.dequeueReusableCellWithIdentifier("Cell")
 
    let person = people[indexPath.row]
 
    cell!.textLabel!.text =
      person.valueForKey("name") as? String
 
    return cell!
}
The most significant change to these methods occurs in cellForRowAtIndexPath. Instead of matching cells with the corresponding string in the model array, you now match cells with the correspondingNSManagedObject.
Note how you grab the name attribute from the NSManagedObject. It happens here:
cell!.textLabel!.text = person.valueForKey("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.
Note: If you’re new to iOS development, you may not be familiar with key-value coding or KVC.
KVC is a mechanism in Cocoa and Cocoa Touch for accessing an object’s properties indirectly using strings to identify properties. In this case, KVC makes NSMangedObject behave more or less like a dictionary.
Key-value coding is available to all classes that descend from NSObject, including NSMangedObject. You wouldn’t be able to access properties using KVC on a Swift object that doesn’t descend fromNSObject.
Next, replace the save action in the addName @IBAction method with the following:
let saveAction = UIAlertAction(title: "Save",
  style: .Default,
  handler: { (action:UIAlertAction) -> Void in
 
    let textField = alert.textFields!.first
    self.saveName(textField!.text!)
    self.tableView.reloadData()
})
This takes the text in the text field and passes it over to a new method called saveName. Add saveName toViewController.swift, as shown below:
func saveName(name: String) {
  //1
  let appDelegate =
  UIApplication.sharedApplication().delegate as! AppDelegate
 
  let managedContext = appDelegate.managedObjectContext
 
  //2
  let entity =  NSEntityDescription.entityForName("Person",
    inManagedObjectContext:managedContext)
 
  let person = NSManagedObject(entity: entity!,
    insertIntoManagedObjectContext: managedContext)
 
  //3
  person.setValue(name, forKey: "name")
 
  //4
  do {
    try managedContext.save()
  //5
    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:
      1. Before you can save or retrieve anything from your Core Data store, you first need to get your hands on an NSManagedObjectContext. You can think of 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; then, after you’re happy with your shiny new managed object, 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 application delegate. To access it, you first get a reference to the app delegate.
      2. You create a new managed object and insert it into the managed object context. You can do this in one step with NSManagedObject’s designated initializer:init(entity:insertIntoManagedObjectContext:).You may be wondering what anNSEntityDescription is all about. Recall that earlier, I called NSManagedObject a “shape-shifter” class because it can represent any entity. An entity description is the piece that links the entity definition from your data model with an instance of NSManagedObject at runtime.
      3. With an NSManagedObject in hand, you set the name attribute using key-value coding. You have to spell the KVC key (“name” in this case) exactly as it appears on your data model, otherwise your app will crash at runtime.
      4. You commit your changes to person and save to disk by calling save on the managed object context. Note that save can throw an error, which is why you call it using the try keyword and within a do block.
      5. Congratulations! Your new managed object is now safely ensconced in your Core Data persistent store. Still within the do block, insert the new managed object into the people array so that it shows up in the table view when it reloads.
That’s a little more complicated than an array of strings, but not too bad. Some of the code here—getting the managed object context and entity¬—could be done just once in your own init() or viewDidLoad()and then reused later. For simplicity, you’re doing it all at once in one method.
Build and run the app, and add a few names to the table view:
CoreData2-24
If the names are actually stored in Core Data, the HitList app should pass the persistence test. Double-tap the Home button to bring up the fast app switcher. Terminate the HitList app by flicking it upwards.
From Springboard, tap the HitList app to trigger a fresh launch. Wait, what happened? The table view is empty:
CoreData2-26
You saved to Core Data, but after a fresh app launch, the people array is empty! The data is actually sitting there waiting, but you’re not showing it yet.

Fetching from Core Data

To get data from your persistent store and into the managed object context, you have to fetch it. Add the following method to ViewController.swift:
override func viewWillAppear(animated: Bool) {
  super.viewWillAppear(animated)
 
  //1
  let appDelegate =
  UIApplication.sharedApplication().delegate as! AppDelegate
 
  let managedContext = appDelegate.managedObjectContext
 
  //2
  let fetchRequest = NSFetchRequest(entityName: "Person")
 
  //3
  do {
    let results =
      try managedContext.executeFetchRequest(fetchRequest)
    people = results as! [NSManagedObject]
  } catch let error as NSError {
    print("Could not fetch \(error), \(error.userInfo)")
  }
}
Step by step, this is what the code does:
      1. As mentioned in the previous section, before you can do anything with Core Data, you need a managed object context. Fetching is no different! You pull up the application delegate and grab a reference to its managed object context.
      2. As the name suggests, NSFetchRequest is the class responsible for fetching from Core Data. Fetch requests are both powerful and flexible. You can use requests to fetch a set of objects that meet particular criteria (e.g., “give me all employees that live in Wisconsin and have been with the company at least three years”), individual values (e.g., “give me the longest name in the database”) and more.Fetch requests have several qualifiers that refine the set of results they return. For now, you should know that NSEntityDescription is one of these qualifiers (one that is required!).Setting a fetch request’s entity property, or alternatively initializing it with init(entityName:), fetches all objects of a particular entity. This is what you do here to fetch all Person entities.
      3. You hand the fetch request over to the managed object context to do the heavy lifting.executeFetchRequest() returns an array of managed objects that meets the criteria specified by the fetch request.
Note: Like save()executeFetchRequest() also throws an error so you have to use it within a doblock. If an error occurred during the fetch, you can inspect the NSError inside the catch block and respond appropriately.
Build and run the application once again. Immediately, you should see the list of names you added earlier:
CoreData2-28
Great! They’re back from the dead. Add a few more names to the list and restart the app to verify that saving and fetching are working properly. Short of deleting the app, resetting the Simulator or throwing your phone off a tall building, the names will appear in the table view no matter what.
CoreData2-29

Where to go from here?

In this getting started with Core Data tutorial you experienced several fundamental Core Data concepts: data models, entities, attributes, managed objects, managed object contexts and fetch requests. Here is the completed HitList project, fully integrated with Core Data.
There were a few rough edges in HitList: you had to get the managed object context from the app delegate each time, and you used KVC to access the attributes rather than a more natural object-styleperson.name. As you can see, Core Data is large and extensive topic. There’s a lot more to learn!
If you’d like to learn more about creating Core Data applications using Swift 2.0, check out our the second version of our book Core Data by Tutorials where you can go deeper into more advanced Core Data topics such as iCloud/Core Data integration, versioning and migration. The second version of the book is up to date with iOS 9 and Swift 2.0 in mind.
If you have any questions or comments on this tutorial, please join the forum discussion below!
Share on Google Plus

About Divya

This is a short description in the author block about the author. You edit it by entering text in the "Biographical Info" field in the user admin panel.
    Blogger Comment
    Facebook Comment

0 comments:

Post a Comment