Head First iPhone and iPad Development (2013)

Just sit right back and you’ll hear a tale, a tale of a fateful trip. A challenge faced today is how to work with big data and make it presentable in a more appropriate format for mobile. There are lots of ways to do that, including manipulating the data and presenting it to the users in an easy to navigate and interpret way. TV presents one of those challenges because there are so many showings on the air. What’s a Gilligan fan to do?

This is your application

Gaming and reading apps aside, most applications on mobile are used to allow a user to sift efficiently through data on the go. Calorie tracking apps, calendar apps, mail, news readers, weather apps—they’re all some variation on that theme. Until now, we’ve worked with very basic storage, arrays, and plists.

Plists are great for just storing local data that is mostly numbers and strings. You want to get into storing photos or videos? Images? Plists are not a great solution. Performance starts to suffer and communicating data off the device is inefficient. Plists are XML files: they have to be loaded and saved in their entirety. Plists don’t support partial random access like a DB store.

This is your application on data

Most applications are driven by some more extensive use of data. There are limits to the relationships and size of data that can exist in a plist. To have more data manipulation capabilities, we need to move into a more complex data management structure.

THERE ARE NO DUMB QUESTIONS

Q:

Q: What’s the difference between a database and a plist?

A:

A: Both databases and plists can be used to store data. A plist is really an XML file that Apple uses for some basic and common data types. Storing some numbers or basic strings? No problem. When you move into storing data that you want to be able to access randomly or search or when your data is too big to store in memory all at once, then you want to look elsewhere.

Q:

Q: How do I know which to use?

A:

A: Plists are a great first pass. Once you get into a situation where you’re seeing performance issues or want to move into something that just can’t be supported, it’s time to get into using a database. And to use a database, you need some logic to run it.

Introducing Core Data

Loading and saving data, particularly lots of data, is a major part of most applications. But what if you wanted to sort that data in a bunch of different ways? Writing code to handle that kind of persistence gets really old, really quickly. Enter Core Data...

But wait, there’s more!

Core Data makes loading and saving your data a snap, but it doesn’t stop there. It’s a mature framework that Apple brought over from Mac OS X to iOS in version 3.0 and gives you:

§ The ability to load and save your objects

Core Data automatically loads and saves your objects based on entity descriptions. It can even handle relationships between objects, migrating between versions of your data, required and optional fields, and field validation.

§ Different ways to store your data

Core Data hides how your data is actually stored from your application. You could read and write to a SQLite database or a custom binary file by simply telling Core Data how you want it to save your stuff.

§ Memory management with undo and redo

Core Data can be extremely efficient about managing objects in memory and tracking changes to objects. You can use it for undo and redo, paging through huge databases of information, and more.

...and speaking of data

Head First Network has a great idea for a new app. But they’re running into some trouble.

HFN: HEAD FIRST NETWORK

Hi!

We have lots of viewers that really love their Gilligan’s Island reruns, and it’s our highest rated show (seriously!).

We’ve decided to put an app up on the App Store to allow our viewers to pull down the showtimes and make plans to watch or record their favorite episodes. We also want users to be able to enter more data into their app once it’s downloaded. The problem is that we can’t get our code to work with our data.

Can you help?

BRAIN POWER

What do you think the other developers did so far? Any thoughts on what you could change?

The Gilligizer app

The other developers have already gotten the views up and running, they just need some help with the data management. The basics of setting up the views are going fine, but they can’t get anything to display. It’s right where we got Spin City before the app was showing anything.

EXERCISE

Get and build Gilligizer. Just like before, this app is up on GitHub for you to download and get started.

1. Check out the existing code from GitHub.

Just like we did in Chapter 1, instead of creating a new project you need to check it out from a repository. From the “Welcome to Xcode” screen, select Checkout an Existing Project, then select the “Repository” option. You should have https://github.com/dpilone/Head-First-iPhone-iPad-Development-3rd-Edition.git from earlier in the book.

2. Select the right branch.

The starting code for each chapter is listed under that chapter’s branch. Go ahead and select the Chapter 7 branch and hit next. Finally, you need to select a saving location and name for the chapter (we’d suggest Chapter 7 or Gilligizer) and select the “Checkout” button.

3. Test.

Now that you have the project on your machine, go ahead and build and run what’s there. It should be working with the basics!

Now that you’ve got everything set up, let’s move on to the data!

Core Data starts with...data

Like most data management, we first need to build the data model to incorporate into the application. Since the detail view is already fleshed out, we can use that to figure out what data needs to be included. For now, the easiest way to enter data into the application is manually by the user. Don’t worry, we’ll get to a bigger database of shows to add to the application later.

DATA MAGNETS

Use what you learned from Spin City to fill in the blanks for our show data. For now, we just want an idea of the fields we’ll need.

Show

DATA MAGNETS SOLUTION

Here’s what we came up with for the data for this app.

Show

THERE ARE NO DUMB QUESTIONS

Q:

Q: Why is the episode ID anNSNumber,not an integer?

A:

A: If you jump on Wikipedia (not that we have done this!), the episode numbers are actually formatted with the season.episode number. It’s pretty typical actually, so it’s not an integer.

Core Data works with entities

Now that we know what the data needs to look like, we have to figure out how to map that to the way Core Data wants to talk about it. Core Data has some very specific terminology that it uses to describe the different parts of the data process. An entity is a representation of something you want to store in the database. In our case, entities are showings of Gilligan’s Island and they will be made up of attributes (title, description, etc.) and in the case of more complex data, relationships between entities.

We need to define the entity for Core Data

Entities are abstract, so they need to be represented in code so the application can interact with them (we’ll get to that in a bit); but first off they need to be represented in a way that Core Data can work with.

Not only can Core Data give us the OO-based view of our data that we want, it can even define our data graphically. There’s one snag though—out of the box, Core Data supports a specific set of data types, so as we define our entity it must be done using the types Core Data offers...

Core Data describes entities with a Managed Object Model

Entities controlled by Core Data are called Managed Objects. The way you capture your entity descriptions (properties, relationships, type information, etc.) for Core Data is through a Managed Object Model. Core Data looks at that Managed Object Model at runtime to figure out how to load and save data from its persistence store (e.g., a database). The Xcode split-view template using Core Data we used comes with an empty Managed Object Model to get us started.

Let’s go ahead and create our Show entity...

Build your Show entity

We need to create a Show entity in our Managed Object Model. Since our Show doesn’t have any relationships to other classes, we just need to add properties. Open up Gilligizer.xcdatamodeld to create the Show data type.

THERE ARE NO DUMB QUESTIONS

Q:

Q: What are the transient and indexed checkboxes for in Xcode when you create properties?

A:

A: The transient checkbox indicates that Core Data doesn’t need to load or save that property. Transient properties are typically used to hold values that you only want to calculate once for performance or convenience reasons, but can be calculated based on the other data you save in the entity. If you use transient properties, you typically implement a method named awakeFromFetch: that is called right after Core Data loads your entity. In that method, you can calculate the values of your transient properties and set them.

The indexed checkbox tells Core Data it should try to create an index on that property. Core Data can use indexes to speed up searching for items, so if you have a property that you use to look up your entities (customer IDs, account numbers, etc.), you can ask Core Data to index them for faster searching. Indexes take up space and can slow down inserting new data into the store, so only use them when they can actually improve search performance.

Q:

Q: I’ve seen constants declared with k’s in front of them. Are they different somehow?

A:

A: Nope. It’s just a naming convention. C and C++ programmers tend to use all caps, while Apple tends to use the lowercase “k” instead.

Q:

Q: What if I need to use a type that Core Data doesn’t support?

A:

A: The easiest way is obviously to try and make your data work with one of the built-in types. If that doesn’t work, you create custom types and implement methods to help Core Data load and save those values. Finally, you could stick your data into a binary type (binary data or BLOB) and write some code to encode and decode it at runtime.

Q:

Q: What other types of persistence does Core Data support?

A:

A: Core Data supports three types of persistence stores on iOS: binary files, SQLite DBs, and in-memory. The SQLite store is the most useful and what we’re using for Gilligizer. It’s also the default. Binary files are nice because they’re atomic, meaning either everything is successfully stored at once or nothing is. The problem with them is that in order to be atomic, the iPhone has to read and write the whole file whenever something changes. They’re not used too often in iOS. The in-memory persistence store is a type of store that isn’t actually ever saved on disk, but lets you use all of the searching, sorting, and undo-redo capabilities that Core Data offers with data you keep in-memory.

Q:

Q: What SQL datatypes/table structures does Core Data use when it writes to a SQLite database?

A:

A: The short answer is you don’t need to know. Even though it’s writing to a SQLite database, the format, types, and structures are not part of the public API and could potentially be changed by Apple. You’re supposed to treat the SQLite database as a blackbox and only access it through Core Data.

Q:

Q: So this comes with lots of default code, but I don’t see what this gets us over an array yet. It seems like a lot of work.

A:

A: We had to tell Core Data what kind of information we’re working with. Now that we’ve done that, we can start putting it to work.

WATCH IT!

Make sure your object model matches ours exactly!

When you’re writing your own apps, there are lots of ways to set up your data model, but since we’re going to give you a database for Gilligizer, your model must match ours exactly!

MANAGED OBJECT MODEL CONSTRUCTION

Finish building the Show entity in the Managed Object Model based on the episode information we want to store. Remember, Core Data types won’t match our Objective-C types exactly. Make sure you name your properties the same as we have in the Show diagram shown below.

MANAGED OBJECT MODEL CONSTRUCTION SOLUTION

We finished building the Show entity in the Managed Object Model based on the Show information we want to store. Remember, Core Data Types won’t match our Objective-C types exactly.

CORE DATA UP CLOSE

Core Data is about managing objects

So far we’ve talked about how to describe our objects to Core Data, but not how we’re actually going to do anything with them. In order to do that, we need to take a quick look inside Core Data.

Inside of Core Data is a stack of three critical pieces: the Managed Object Context, the Persistent Store Coordinator, and the Persistent Object Store.

Exactly!

The Managed Object Context is what interacts with your database to get out the data. That’s all set. What you need now is a class that can talk to the MOC and translate that into something usable for the application. We need a Show class....

But you need code. Core Data can do that for you.

SHARPEN YOUR PENCIL

Xcode can create a Show class from our Managed Object Model that we can use like any other class.

1. Select the Gilligizer .xcdatamodel and click on the Show entity.

You need to have a Core Data entity selected before you ask Xcode to generate a class for you.

2. Create a new Managed Object Class...

Select Editor →Create NSManagedObject Subclass... You will be prompted to select where the file should be saved. Because there is only one entity type in the file, you won’t be prompted to choose which type to save.

3. And generate the .h and .m.

Click Create and you should have a Show.h and a Show.m added to your project. Go ahead and drag these up to the /Gilligizer group if they aren’t there already.

SHARPEN YOUR PENCIL SOLUTION

Here’s how Xcode can create a Show class from our Managed Object Model that we can use like any other class.

1. Select the Gilligizer.xcdatamodel and click on the Show entity.

You need to have a Core Data entity selected before you ask Xcode to generate a class for you.

2. Create a new Managed Object Class...

Select Editor →Create NSManagedObject Subclass... You will be prompted to select where the file should be saved. Since there is only one entity type in the file you won’t be prompted to choose which type to save.

3. And generate the .h and .m

Click Create and you should have a Show.h and a Show.m added to your project. Go ahead and drag these up to the /Gilligizer group if they aren’t there already.

Our generated Show class matches our Managed Object Model

Xcode created two new files from our Show entity: a Show.h header file and a Show.m implementation file. Open up both files and let’s take a look at what was created.

THERE ARE NO DUMB QUESTIONS

Q:

Q: I don’t understand where those types came from. Why is theNSNumberfor both theepisodeIDandfirstRun?

A:

A: When we defined our attributes in our Managed Object Model, we used basic types (e.g., Integer32, Boolean) as the type. This works well for the data model, but when Core Data turns an entity into a Managed Object, we have to use objects to define data types. Since Objective-C supports both basic types and objects, Core Data wraps some basic types in the NSNumber class.

Right! The Core Data framework takes care of it.

The Show.m class is nearly empty, and instead of synthesizing the properties, they’re declared with a new directive, @dynamic.

THERE ARE NO DUMB QUESTIONS

Q:

Q: Hey, wait: if I have to regenerate the class, anything I customize gets rewritten. What do I do about that?

A:

A: That’s true right now. If you have custom code you’re worrried about, you can do a couple of things. You can create a category on the Managed Object or you can subclass it.

NSManagedObject also implements the properties

The new @dynamic directive tells the compiler not to worry about the getter and setter methods necessary for the properties. They need to come from somewhere, though, or else code is going to crash at runtime when someone tries to access those properties. This is whereNSManagedObject steps in again. Because NSManagedObject handles the memory for the fields backing the properties, it also provides runtime implementations for the getter and setter methods. By having NSManagedObject implement those methods, you get a number of other neat benefits:

§ The NSManagedObject knows when properties are changed, can validate new data, and can notify other classes when changes happen.

§ NSManagedObject can be lazy about fetching property information until someone asks for it. For example, it does this with relationships to other objects.

NOTE

You get all of this without writing a line of code!

§ NSManagedObject can keep track of changes to properties and provide undo-redo support.

Now it’s just a matter of asking Core Data to load a Show...

READY BAKE CODE

We snuck some extra code into your GitHub project to help along this part. If you download it directly, you’ll see we’ve commented the code to help you along. Since you’ve already populated a detail view once, we figured we’d give you this code.

READY BAKE CODE

Jim: Head First Network is getting anxious...

Frank: Well, we’ve got the model now...

Joe: ...and the view from GitHub.

Jim: There’s an Add button right? They want users to be able to add showings and look at them.

Frank: So what does that button do?

Jim: Just look in the code!

Joe: What about the insert new object method? I saw that in the MasterViewController.m file when we were adding that other code in.

You have an object...now present it.

We have everything we need to create a new Show object and add it to the database. But that doesn’t mean that anybody can see it! Now we need code to fetch that object back out and present it in the table view.

Present each entity in Gilligizer

In order to display a Show, we need to grab the right Show and pull the information we want to display. Our table view will let us know which Show we need by looking at the indexPath for the row in question. We can get the show from the FetchedResultsController and configure the cell based on Show properties.

THERE ARE NO DUMB QUESTIONS

Q:

Q: How is this data getting into the DB?

A:

A: Through the + button that you’re creating. Once the user hits that, we create a new instance of our Show entity and then save the updated ManagedObjectContext. For now, the user (and that’s you when you’re testing) is going to need to add shows manually. We’ll get into adding a database of shows to work with soon.

Q:

Q: How does Core Data know which object to create in our ‘insertNewObject:’ method?

A:

A: Remember how we talked about the differences between Entity and NSManagedObject? When we want to create a new Show object, we ask the class NSEntityDescription to insert a new object with the name “Show”. This class will look at our Managed Object Model for an entity named “Show”. When it finds one, it’ll handle initializing an instance of our NSManagedObject subclass that maps to the Show entity.

No problem! The app needs to fetch results from the DB.

The Core Data stack comes with lots of the code that you already need to use to fetch data out of the database. If you dig into the code you can find it...

READY BAKE CODE

READY BAKE CODE

READY BAKE CODE

TEST DRIVE

Go ahead and take it for a spin. Just to be on the safe side, make sure you uninstall the app from the simulator and also clean the build in Xcode (Shift+Command+K).

Fetch results is not trivial. But it’s coming...we’ll explain more soon!

CORE DATA CROSS

We've got some Core Data under our belts and some multiple views work. There's a lot of terminology here. Check it out!

Across

Down

3. When you need Core Data to load an object, the Managed Object ________ is asked.