CHAPTER 12

image

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.)

9781484217658_Fig12-01.jpg

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.

Image 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.

Changing timeStamp to name

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.

9781484217658_Fig12-02.jpg

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.

9781484217658_Fig12-03.jpg

Figure 12-3. A table-based detail view for scores

Image 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:

  1. Create subclasses of NSObject for User and Score. (This was described in Chapter 9.)
  2. Modify MasterViewController to use the new subclasses.
  3. Replace the existing detail view controller in the storyboard with a table-view controller-based detail view controller.
  4. Modify the code in DetailViewController to work with the table view controller-based detail view controller.
  5. Modify MasterViewController to pass a reference to the current user to the new detail view controller so that its scores can be displayed.

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.)

9781484217658_Fig12-04.jpg

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.

Using the Subclasses

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).

Change detail to detailUser

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!).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.235.62