Using the Simple Database with a Core Data/iOS App
This chapter will show you how to use Core Data to build an iOS app that re-implements the PHP pages shown in Chapter 11. The basics of this app from the Core Data side of things will apply to OS X as well as to iOS, but the Master-Detail Application template is specifically designed to take advantage of the features of iOS. OS X has a separate feature, Bindings, that also very conveniently works together with Core Data. However, it’s different from the table-based interface that’s used on iOS.
For now it’s time to move forward to implement the PHP code in Chapter 11 with Core Data/iOS.
The Story Continues…
Chapter 8 provides a guide to working with the Xcode Core Data model editor. You see the steps involved in creating a new project from the Master-Detail Application template that’s part of Xcode and then converting it to a more complex data structure. At the end of the chapter, you have two entities and two relationships connecting them. Figure 12-1 shows the database in the Core Data model editor as it is at the end of Chapter 8. (Note that both Score and User are selected in the sidebar, so the attributes and relationships shown in the editor are those for both of those entities—the union of the attributes and relationships to use database terminology.)
Figure 12-1. Start from the database from Chapter 9
If you want to follow along with this chapter, you can either make a copy of that database or download the example file for this chapter. (For this chapter, a new project called Scores has been created but the name of the project is totally up to you.)
The two entities in the Core Data model, User and Score, are the same tables that you’ve used throughout. One of the important parts of Core Data is that it manages primary and foreign keys for you. SQLite does create a primary key for you (rowid) unless you explicitly don’t want to use it. Core Data with SQLite does that, too, but it happens behind the scenes. If you open a Core Data SQLite file with a utility, you’ll see the keys in the database.
Caution You can look at the SQLite databases, but remember that Core Data manages them. Don’t make changes to them. In the best of cases, you’ll immediately break your Core Data project. In the worst cases, you’ll create a time bomb that will go off when you (and your users) least expect it and have the least amount of time to deal with it. If you already have a SQLite database and want to import it to Core Data, the simplest way is to export the data (perhaps to a neutral format like CSV—or comma-separated values) and then to import it into your Core Data app.
Adjusting the Data Model and Template for Core Data
The data model needs to be revised so that it no longer relies on explicit keys (Core Data uses them in the background), and there a few tweaks to the existing code are necessary so that it runs properly.
Getting Rid of Keys and Revising the Data Model
The data model used in Chapter 8 is designed to match basic data that is used in various projects in this book. In this chapter, it’s possible to modify that data model to get rid of some excess baggage that Core Data doesn’t need. Specifically, the major change you can make is to get rid of the primary keys. Core Data manages its own primary keys, so there’s no reason to duplicate that.
“But wait!” you may think. “How do I relate a score to a user without a primary key?”
This question is very common when database-savvy developers first use Core Data. For years (decades, for many) we’ve relied on those primary keys. Getting rid of them is difficult—it’s almost a physical hurt. What’s important to remember is that your objective is to relate two entities—in this case a specific score to a specific user. The primary key on one side and the foreign key on the other side make that possible, but they’re an implementation issue: what you want to do is relate a specific score to a specific user.
The data model from Chapter 8 (shown here as Figure 12-1) omits those keys—both the primary and foreign keys. The absence of those keys created no difficulty in Chapter 8, and there’s no problem now. The table views of the database that have been used until now have been designed to be implemented on various platforms, but for building a Core Data app, they can be revised to omit the no-longer-needed keys.
If you’ve modified the Chapter 9 version of the code or the Xcode Master-Detail Application template, you have probably changed the Event entity to User and replaced the timeStamp date attribute with a name string attribute. It’s not necessary to do this in this order, but it’s what you may have done. Then, you may have created a new entity, Score.
It’s also possible to change Event to Score and create User as a new entity. In another scenario, you can delete Event and create new entities for both User and Score. Either way, you should wind up with the data model shown in Figure 12-1.
Use the find navigator in Xcode to search your project for timeStamp. If you’ve already changed the data model, the only reference you will find is the initialization code in MasterViewController’s insertNewObject function. If you have changed timeStamp to name, you’ll wind up with the following line of code:
newManagedObject.setValue(NSDate(), forKey: "name")
(This code started out with forKey: "timeStamp").
The new attribute is a string, not a date. You can get around this problem quite easily: simply use the description function of most NSObject instances (and their descendants). That function returns a string value of whatever the instance is. Thus, change the line to read
newManagedObject.setValue(NSDate().description, forKey: "name")
It’s still going to set the current date, but this time it will be used as a string (description) rather than a NSDate instance.
Create a New Database on Your Device or Simulator
Remove the app from the simulator or from your device. You’ll get a warning that you will remove all its data: that’s exactly what you want to do. You’ve changed the data model, and data that was created with a previous data model won’t work. (There are migration routines in Core Data, but they are beyond the scope of Introducing SQLite.)
Add the Score Table and Interface to the App
The first step is to see what you have now. Then you can move on to adding Score detail information.
Making Sure You Can Add New Users with + in the Master View Controller
If you’re not seeing timestamps when you click + at the top left of the master view controller, you may have replaced the Event entity with Score. The code that controls which entity you create with the + is in fetchedResultController in MasterViewController.swift.
Following is the relevant line of code:
let entity = NSEntityDescription.entityForName
("User", inManagedObjectContext: self.managedObjectContext!)
This started out in the Xcode template as:
let entity = NSEntityDescription.entityForName
("Event", inManagedObjectContext: self.managedObjectContext!)
With your revisions, you may have wound up with:
let entity = NSEntityDescription.entityForName
("Score", inManagedObjectContext: self.managedObjectContext!)
You want to create User entities, so make sure that you revise the code if necessary.
Working with the Detail View
When you run the app now, you’ll be able to add new items to the list of users (formerly the list of events but all you’ve done is change the name). When you add a new user, you can click/tap on it to see its details in the Detail View Controller as shown in Figure 12-2.
Figure 12-2. See detail information in the detail view (right) with the master view at the left
Make certain that what you see matches Figure 12-2 (at least mostly—this is a landscape-oriented iPhone 6 Plus. Once you’ve done that, you can move on.
Working with the Detail View for Score
The goal here is to replace the existing detail view with a table controller that displays all the scores for a given user and gives you the ability to add new ones. Figure 12-3 shows what you’re aiming for.
Figure 12-3. A table-based detail view for scores
Note The values for scores are UNIX timestamps (seconds since January 1, 1970). They’re easily gotten from NSDate in Cocoa and Cocoa Touch, and they provide different numerical values so you can test your code. The Master View Controller continues to use a timestamp that is displayed as a timestamp.
This will entail three steps:
These can be done in any order after Step 1.
Use NSManagedObject Subclasses
The code in MasterViewController creates a new User entity as described in the previous section. Once you have subclasses available, you should use them. In insertNewObject in ManagedObjectController, find the following line of code:
let newObject = NSEntityDescription.insertNewObjectForEntityForName
(entity.name!, inManagedObjectContext: context)
Change it to
let newUser = NSEntityDescription.insertNewObjectForEntityForName
(entity.name!, inManagedObjectContext: context) as! User
In the line that follows, use the subclass instead of key-value coding. Instead of
newManagedObject.setValue(NSDate().description, forKey: "name")
Use the new subclass with the following line:
newUser.name = NSDate().description
Use a Table View Controller for DetailViewController
The easiest way to do this is to remove the current DetailViewController from the storyboard. (It is shown in Figure 12-4.)
Figure 12-4. Locate the Detail View Controller in the storyboard and remove it
Then drag a table view controller from the object library at the right of the window into the storyboard. Place it where the detail view controller was, and control-drag from the navigation controller to the new table view controller. For the relationship, choose root view controller (you’ll be asked as soon as you release the mouse button on the control-drag).
Modify DetailViewController Code for DetailViewController
There are a few changes to DetailViewController.
You need to modify the code so that DetailViewController is a subclass of UITableViewController rather than UIViewController.
Change this line at the top of the file from
class DetailViewController: UIViewController {
to the following:
class DetailViewController: UITableViewController {
Also change the storyboard so that the new DetailViewController’s type is DetailViewController (it might not be).
Remove references to the label from DetailViewController (it’s called DetailDescriptionLabel).
There are a few housekeeping chores to tidy up the code. Change the variable declaration for detail to detailUser (at the top of the file).
var detailUser: User? {
didSet {
// Update the view.
self.configureView
}
}
Change configureCell to Use the Subclass
Here is what configureCell should look like now that it uses the Score subclass. Note that there’s a bit of conversion going on as the NSSet of scores for a given user (a relationship) is converted to an array (so that the nth element can be found for the table). Then, the nth element (which is a Score) is examined to find its score value (Score.score).
func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) {
if let scores = detailUser?.scores {
let scoresArray = scores.allObjects as NSArray
let selectedRowArrayElement = scoresArray [indexPath.row] as! Score
let scoreForSelectedRow = selectedRowArrayElement.score
cell.textLabel!.text = "Score: " + scoreForSelectedRow.description
}
}
Modify MasterViewController to Pass the User to DetailViewController
By default, the object selected in the master detail view list is passed to DetailItem in DetailViewController. It’s declared as AnyObject? You’ll need to change it to use the new detailUser variable you’ve created. In prepareForSegue, change the line
controller.detailItem = object
to the following:
controller.detailUser = (object as! User)
You now need to connect the table and support the relevant protocols. This code is actually in MasterViewController because you need to manage a table view controller there. You’ll just need to copy it to DetailViewController.
And you’ll need to import Core Data at the top of DetailViewController.
import CoreData
Summary
You now have the basics of an app that uses the built-in SQLite database, the Xcode Core Data model editor and Core Data framework to work with the database, and the Cocoa Touch framework for iOS to bring it all together.
What you’ve seen in working with PHP and Core Data and in the summary of Android is that there are languages (PHP and others) as well as frameworks (Cocoa Touch and Cocoa) and application programming interfaces (APIs) (Android) that build on SQLite. The degree to which the SQL syntax for SQLite is exposed varies from very little (Core Data) to a great deal (PHP) and various middle roads such as Android.
What is important is that you understand the principles and functionality. Remember, too, that in building any database-driven app, the more that you can put into your database the less code you have to write. Take advantage of validation options, GROUP BY clauses, and everything else that you can find. The less code you write the less time it will take (to write and to debug!).
18.191.235.62