IOS 8 Databases in Swift using Core Data
Previous | Table of Contents | Next |
An Example SQLite based iOS 8 Application using Swift and FMDB | An iOS 8 Swift Core Data Tutorial |
<google>BUY_IOS8</google>
The preceding chapters covered the concepts of database storage using the SQLite database. In these chapters the assumption was made that the iOS application code would directly manipulate the database using SQLite C API calls to construct and execute SQL statements. Whilst this is a perfectly good approach for working with SQLite in many cases, it does require knowledge of SQL and can lead to some complexity in terms of writing code and maintaining the database structure. This complexity is further compounded by the non-object-oriented nature of the SQLite C API functions. In recognition of these shortcomings, Apple introduced the Core Data Framework. Core Data is essentially a framework that places a wrapper around the SQLite database (and other storage environments) enabling the developer to work with data in terms of Swift objects without requiring any knowledge of the underlying database technology.
We will begin this chapter by defining some of the concepts that comprise the Core Data model before providing an overview of the steps involved in working with this framework. Once these topics have been covered, the next chapter will work through An iOS 8 Swift Core Data Tutorial.
The Core Data Stack
Core Data consists of a number of framework objects that integrate to provide the data storage functionality. This stack can be visually represented as illustrated in Figure 43 1.
As we can see from Figure 43-1, the iOS based application sits on top of the stack and interacts with the managed data objects handled by the managed object context. Of particular significance in this diagram is the fact that although the lower levels in the stack perform a considerable amount of the work involved in providing Core Data functionality, the application code does not interact with them directly.
Figure 43-1
Before moving on to the more practical areas of working with Core Data it is important to spend some time explaining the elements that comprise the Core Data stack in a little more detail.
Managed Objects
Managed objects are the objects that are created by your application code to store data. A managed object may be thought of as a row or a record in a relational database table. For each new record to be added, a new managed object must be created to store the data. Similarly, retrieved data will be returned in the form of managed objects, one for each record matching the defined retrieval criteria. Managed objects are actually instances of the NSManagedObject class, or a subclass thereof. These objects are contained and maintained by the managed object context.
Managed Object Context
Core Data based applications never interact directly with the persistent store. Instead, the application code interacts with the managed objects contained in the managed object context layer of the Core Data stack. The context maintains the status of the objects in relation to the underlying data store and manages the relationships between managed objects defined by the managed object model. All interactions with the underlying database are held temporarily within the context until the context is instructed to save the changes, at which point the changes are passed down through the Core Data stack and written to the persistent store.
Managed Object Model
So far we have focused on the management of data objects but have not yet looked at how the data models are defined. This is the task of the Managed Object Model which defines a concept referred to as entities.
Much as a class description defines a blueprint for an object instance, entities define the data model for managed objects. In essence, an entity is analogous to the schema that defines a table in a relational database. As such, each entity has a set of attributes associated with it that define the data to be stored in managed objects derived from that entity. For example, a Contacts entity might contain name, address and phone number attributes.
In addition to attributes, entities can also contain relationships, fetched properties and fetch requests:
- Relationships – In the context of Core Data, relationships are the same as those in other relational database systems in that they refer to how one data object relates to another. Core Data relationships can be one-to-one, one-to-many or many-to-many.
- Fetched property – This provides an alternative to defining relationships. Fetched properties allow properties of one data object to be accessed from another data object as though a relationship had been defined between those entities. Fetched properties lack the flexibility of relationships and are referred to by Apple’s Core Data documentation as “weak, one way relationships” best suited to “loosely coupled relationships”.
- Fetch request – A predefined query that can be referenced to retrieve data objects based on defined predicates. For example, a fetch request can be configured into an entity to retrieve all contact objects where the name field matches “John Smith”.
Persistent Store Coordinator
The persistent store coordinator is responsible for coordinating access to multiple persistent object stores. As an iOS developer you will never directly interact with the persistence store coordinator and, in fact, will very rarely need to develop an application that requires more than one persistent object store. When multiple stores are required, the coordinator presents these stores to the upper layers of the Core Data stack as a single store.
Persistent Object Store
The term persistent object store refers to the underlying storage environment in which data are stored when using Core Data. Core Data supports three disk-based and one memory-based persistent store. Disk based options consist of SQLite, XML and binary. By default, the iOS SDK will use SQLite as the persistent store. In practice, the type of store being used is transparent to you as the developer. Regardless of your choice of persistent store, your code will make the same calls to the same Core Data APIs to manage the data objects required by your application.
Defining an Entity Description
Entity descriptions may be defined from within the Xcode environment. When a new project is created with the option to include Core Data, a template file will be created named <projectname>.xcdatamodeld. Selecting this file in the Xcode project navigator panel will load the model into the entity editing environment as illustrated in Figure 43-2:
Figure 43-2
Create a new entity by clicking on the Add Entity button located in the bottom panel. The new entity will appear as a text box in the Entities list. By default this will be named Entity. Double click on this name to change it.
To add attributes to the entity, click on the Add Attribute button located in the bottom panel, or use the + button located beneath the Attributes section. In the Attributes panel, name the attribute and specify the type and any other options that are required.
Repeat the above steps to add more attributes and additional entities.
The Xcode entity environment also allows relationships to be established between entities. Assume, for example, two entities named Contacts and Sales. In order to establish a relationship between the two tables select the Contacts entity and click on the + button beneath the Relationships panel. In the detail panel, name the relationship, specify the destination as the Sales entity and any other options that are required for the relationship. Once the relationship has been established it is, perhaps, best viewed graphically by selecting the Table, Graph option in the Editor Style control located in the bottom panel:
Figure 43-3
As demonstrated, Xcode makes the process of entity description creation fairly straightforward. Whilst a detailed overview of the process is beyond the scope of this book there are many other resources available that are dedicated to the subject.
<google>BUY_IOS8</google>
Obtaining the Managed Object Context
Since many of the Core Data methods require the managed object context as an argument, the next step after defining entity descriptions often involves obtaining a reference to the context. This is achieved by first identifying the application delegate and then calling the delegate object’s managedContextObject method:
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
Getting an Entity Description
Before managed objects can be created and manipulated in code, the corresponding entity description must first be loaded. This is achieved by calling the entityForName(_:inManagedObjectContext:) method of the NSEntityDescription class, passing through the name of the required entity and the context as arguments. The following code fragment obtains the description for an entity with the name Contacts:
let entityDescription = NSEntityDescription.entityForName("Contacts", inManagedObjectContext: managedObjectContext!)
Generating a Managed Object Subclass
As previously outlined, the data to be stored using Core Data is packaged up into Managed Object instances. Once an entity has been defined within the Xcode entity editor, Xcode can be instructed to generate a new NSManagedObject subclass for that entity. To generate a managed object class, select the entity in the entity editor and choose the Editor -> Create NSManagedObject Subclass… menu option. The following Swift code, for example, is a managed object class generated by Xcode for an entity named Contacts containing three string attributes:
import Foundation import CoreData class Contacts: NSManagedObject { @NSManaged var name: String @NSManaged var address: String @NSManaged var phone: String }
Having obtained the managed context, and created a Managed Object class for an entity, a new managed object instance conforming to a specified entity description can be created. The following code, for example, creates a new managed object instance for the above Contacts entity class and assigns it to the managed object context so that it is ready to be saved once the object has been configured with the data to be stored:
let contact = Contacts(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext)
Setting the Attributes of a Managed Object
As previously discussed, entities and the managed objects from which they are instantiated contain data in the form of attributes. Once a managed object instance has been created as outlined above, those attribute values can be used to store the data before the object is saved. Assuming a managed object named contact with attributes named name, address and phone respectively, the values of these attributes may be set as follows prior to the object being saved to storage:
contact.name = "John Smith" contact.address = "1 Infinite Loop" contact.phone = "555-564-0980"
Saving a Managed Object
Once a managed object instance has been created and configured with the data to be stored it can be saved to storage using the save method of the managed object context as follows:
var error: NSError? managedObjectContext?.save(&error) if let err = error { // handle error }
Fetching Managed Objects
Once managed objects are saved into the persistent object store it is highly likely that those objects and the data they contain will need to be retrieved. Objects are retrieved by executing a fetch request and are returned in the form of an array. The following code assumes that both the context and entity description have been obtained prior to making the fetch request:
let request = NSFetchRequest() request.entity = entityDescription var error: NSError? var results = managedObjectContext?.executeFetchRequest(request, error: &error)
Upon execution, the results array will contain all the managed objects retrieved by the request.
Retrieving Managed Objects based on Criteria
The preceding example retrieved all of the managed objects from the persistent object store for a specified entity. More often than not only managed objects that match specified criteria are required during a retrieval operation. This is performed by defining a predicate that dictates criteria that a managed object must meet in order to be eligible for retrieval. For example, the following code implements a predicate in order to extract only those managed objects where the name attribute matches “John Smith”:
let request = NSFetchRequest() request.entity = entityDescription let pred = NSPredicate(format: "(name = %@)", "John Smith") request.predicate = pred var error: NSError? var results = managedObjectContext?.executeFetchRequest(request, error: &error)
Accessing the Data in a Retrieved Managed Object
Once results have been returned from a fetch request, the data within the returned objects may be accessed using keys to reference the stored values. The following code, for example, accesses the first result from a fetch operation results array and extracts the values for the name, address and phone keys from that managed object:
var results = managedObjectContext?.executeFetchRequest(request, error: &error) let match = results[0] as NSManagedObject let nameString = match.valueForKey("name") as String let addressString = match.valueForKey("address") as String let phoneString = match.valueForKey("phone") as String
Summary
The Core Data Framework stack provides a flexible alternative to directly managing data using SQLite or other data storage mechanisms. By providing an object oriented abstraction layer on top of the data the task of managing data storage is made significantly easier for the iOS application developer. Now that the basics of Core Data have been covered the next chapter entitled An iOS 8 Swift Core Data Tutorial will work through the creation of an example application.
<google>BUY_IOS8</google>
Previous | Table of Contents | Next |
An Example SQLite based iOS 8 Application using Swift and FMDB | An iOS 8 Swift Core Data Tutorial |