Moving Homepwner to Core Data

Your Homepwner application currently uses archiving to save and reload its data. For a moderately sized object model (say, under 1000 objects), this is fine. As your object model gets larger, however, you will want to be able to do incremental fetches and updates, and Core Data can do this.

The very first step is to add the Core Data framework to your project. Select the Homepwner target and under Build Phases, open the Link Binary With Libraries build phase. Click the + button to add the Core Data framework.

Figure 17.1  Add Core Data framework

Add Core Data framework

The model file

In a relational database, we have something called a table. A table represents some type; You can have a table of people, a table of a credit card purchases, or a table of real-estate listings. Each table has a number of columns to hold a piece of information about that thing. A table that represents people might have a column for the person’s name, social security number, height, and age. Every row in the table represents a single person.

Figure 17.2  Role of Core Data

Role of Core Data

This organization translates well to Objective-C. Every table is like an Objective-C class. Every column is one of the class’ instance variables. Every row is one of the instances of that class. Thus, Core Data’s job is to move data to and from these two organizations (Figure 17.2).

Core Data uses different terminology to describe these ideas of table/class, column/instance variable and row/object. A table is called a entity. The instance variables are called attributes. A Core Data model file is the description of every entity and their attributes in your application. Therefore, you will describe a Possession entity in a model file and give it attributes like possessionName, serialNumber and valueInDollars.

From the File menu, create a new file. Select Core Data and create a new Data Model. Name it Homepwner.xcdatamodeld.

Figure 17.3  Create the model File

Create the model File

This will create a Homepwner.xcdatamodeld file and add it to your project. Open this file. The editor area will now show the user interface for manipulating a Core Data model file.

Click the Add Entity button near the bottom of the window. A new Entity will appear in the Entities list. Double-click this entity and change its name to Possession (Figure 17.4).

Figure 17.4  Create the Possession entity

Create the Possession entity

Now you will add attributes to the Possession entity, remembering that these will be the instance variables of the Possession class. For each attribute, click the + button in the Attributes section and edit the Attribute and Type values for each one:

  • possessionName is a String
  • serialNumber is a String
  • valueInDollars is an Integer 32
  • dateCreated is a Date
  • imageKey is a String
  • thumbnailData is a Binary Data
  • thumbnail is an Undefined (It’s a UIImage, but that isn’t one of the possibilities.)

Select thumbnail from the Attributes list and show the data model inspector. Check the box for Transient (Figure 17.5). This means it will not be saved and loaded from the file. You will create it at runtime from the thumbnailData instead.

Figure 17.5  Add attributes to the Possession entity

Add attributes to the Possession entity

There is one more attribute to add. In Homepwner, users can order their possessions by moving them around in the table view. Archiving possessions in an array naturally respects this order. However, relational tables don’t order their rows. Instead, when you fetch a set of rows, you specify their order using one of the attributes (Fetch me all the Employee objects ordered by lastName.). To maintain the order of possessions, you need to create an attribute to record each possession’s position in the table view. Then when you fetch possessions, you can ask for them to be ordered by this attribute. (You’ll also need to update that attribute when the possessions are reordered.) Create this final attribute: name it orderingValue and make it a Double.

At this point, your model file is sufficient to save and load possessions. However, one of the benefits to using Core Data is that entities can be related to one another. In this exercise, you will add a new entity called AssetType, which describes a category for the possessions. For example, a painting might be of the Art asset type. Of course, AssetType will be an entity in the model file, and each row of that table will be mapped to an Objective-C object at runtime. Every Possession will have a pointer to its AssetType object, and every AssetType will have a list of the Possessions that fall into its category.

Figure 17.6  Entities

Entities

Create a new entity called AssetType.

Figure 17.7  Create the AssetType entity

Create the AssetType entity

Add an attribute called label of type String to AssetType. This will be the name of the category the AssetType represents.

Now, you need to establish the relationship between AssetType and Possession. There are two kinds of relationships: to-many and to-one. When an entity has a to-one relationship, each instance of that entity will have a pointer to an instance in the entity it has a relationship to. For example, the Possession entity will have a to-one relationship to the AssetType entity. Thus, a Possession instance will have a pointer to its AssetType instance.

The AssetType entity, on the other hand, will have a to-many relationship to Possession since many Possessions can be of the same AssetType type. With these relationships, we can ask an AssetType object for a set of all of the Possessions that fall into its category, and we can ask a Possession which AssetType it falls under.

Let’s add these relationships. Select the AssetType entity and then click the + button in the Relationships section. Name the relationship possessions in the Relationship column. Then, select Possession from the Destination column. In the data model inspector, check the box for To-Many Relationship (Figure 17.8).

Figure 17.8  Create the possessions relationship

Create the possessions relationship

Now go back to the Possession entity. Add a relationship named assetType and pick AssetType as its destination. In the Inverse column, select possessions (Figure 17.9).

Figure 17.9  Create the assetType relationship

Create the assetType relationship

One final note on terminology: In the language of entity-relationship modeling, the attributes and relationships of an entity are collectively known as its properties.

NSManagedObject and subclasses

When an object is fetched with Core Data, its class is, by default, NSManagedObject. NSManagedObject is a subclass of NSObject that knows how to cooperate with the rest of Core Data. Therefore, when you fetch an AssetType or a Possession from Core Data, the object that you get back is an instance of NSManagedObject. This can be confusing, since these two entities have different attributes and therefore the classes that represent these entities should have different instance variables. However, NSManagedObject works a bit like a dictionary in that it holds arbitrary key-value pairs. Thus, an NSManagedObject holds a key-value pair for every property in the entity.

NSManagedObject is little more than a data container. Sometimes, you would like your model objects to do something in addition to holding data. For example, the Possession class knows how to create a thumbnail from an image. When an objects of a particular entity need to perform custom behavior, you must create a subclass of NSManagedObject. Then, in your model file, you must specify that this entity is represented by instances of this subclass, not the standard NSManagedObject.

Select the Possession entity. Show the data model inspector and change the Class field to Possession, as shown in Figure 17.10. Now, when a Possession entity is fetched with Core Data, the type of this object will be Possession. (AssetType instances will still be of type NSManagedObject.)

Figure 17.10  Changing the class of an entity

Changing the class of an entity

There is one problem: you already have a Possession class, and it does not inherit from NSManagedObject. Changing the superclass of Possession to NSManagedObject will require considerable changes. Thus, the easiest solution is to copy the behavior methods from your current Possession class, have Core Data generate a new Possession class, and then add your behavior methods back in to the new class files.

In Finder, drag both Possession.h and Possession.m to your desktop. Then, in Xcode, delete these two files from the project navigator.

Then, create a new file. Under Core Data, select the NSManagedObject subclass option and hit Next. On the next pane, select the Homepwner data model. On the following pane, select the Possession entity.

Xcode will generate two new files, Possession.h and Possession.m. In Possession.h, change the type of the thumbnail property to UIImage and add two method declarations that you had previously implemented in the earlier Possession.

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​
#​i​m​p​o​r​t​ ​<​C​o​r​e​D​a​t​a​/​C​o​r​e​D​a​t​a​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​P​o​s​s​e​s​s​i​o​n​ ​:​ ​N​S​M​a​n​a​g​e​d​O​b​j​e​c​t​ ​{​
@​p​r​i​v​a​t​e​
}​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​N​S​S​t​r​i​n​g​ ​*​s​e​r​i​a​l​N​u​m​b​e​r​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​N​S​N​u​m​b​e​r​ ​*​v​a​l​u​e​I​n​D​o​l​l​a​r​s​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​U​I​I​m​a​g​e​ ​*​t​h​u​m​b​n​a​i​l​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​N​S​S​t​r​i​n​g​ ​*​i​m​a​g​e​K​e​y​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​N​S​D​a​t​e​ ​*​d​a​t​e​C​r​e​a​t​e​d​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​N​S​S​t​r​i​n​g​ ​*​p​o​s​s​e​s​s​i​o​n​N​a​m​e​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​N​S​D​a​t​a​ ​*​t​h​u​m​b​n​a​i​l​D​a​t​a​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​N​S​N​u​m​b​e​r​ ​*​o​r​d​e​r​i​n​g​V​a​l​u​e​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​N​S​M​a​n​a​g​e​d​O​b​j​e​c​t​ ​*​a​s​s​e​t​T​y​p​e​;​

-​ ​(​v​o​i​d​)​s​e​t​T​h​u​m​b​n​a​i​l​D​a​t​a​F​r​o​m​I​m​a​g​e​:​(​U​I​I​m​a​g​e​ ​*​)​i​m​a​g​e​;​
+​ ​(​C​G​S​i​z​e​)​t​h​u​m​b​n​a​i​l​S​i​z​e​;​


@​e​n​d​

Next, in Possession.m, copy the setThumbnailDataFromImage: and thumbnailSize methods from your old Possession.m to the new one:

-​ ​(​v​o​i​d​)​s​e​t​T​h​u​m​b​n​a​i​l​D​a​t​a​F​r​o​m​I​m​a​g​e​:​(​U​I​I​m​a​g​e​ ​*​)​i​m​a​g​e​
{​
 ​ ​ ​ ​C​G​S​i​z​e​ ​o​r​i​g​I​m​a​g​e​S​i​z​e​ ​=​ ​[​i​m​a​g​e​ ​s​i​z​e​]​;​

 ​ ​ ​ ​C​G​R​e​c​t​ ​n​e​w​R​e​c​t​;​
 ​ ​ ​ ​n​e​w​R​e​c​t​.​o​r​i​g​i​n​ ​=​ ​C​G​P​o​i​n​t​Z​e​r​o​;​
 ​ ​ ​ ​n​e​w​R​e​c​t​.​s​i​z​e​ ​=​ ​[​[​s​e​l​f​ ​c​l​a​s​s​]​ ​t​h​u​m​b​n​a​i​l​S​i​z​e​]​;​

 ​ ​ ​ ​f​l​o​a​t​ ​r​a​t​i​o​ ​=​ ​M​A​X​(​n​e​w​R​e​c​t​.​s​i​z​e​.​w​i​d​t​h​/​o​r​i​g​I​m​a​g​e​S​i​z​e​.​w​i​d​t​h​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​n​e​w​R​e​c​t​.​s​i​z​e​.​h​e​i​g​h​t​/​o​r​i​g​I​m​a​g​e​S​i​z​e​.​h​e​i​g​h​t​)​;​

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​a​ ​b​i​t​m​a​p​ ​i​m​a​g​e​ ​c​o​n​t​e​x​t​
 ​ ​ ​ ​U​I​G​r​a​p​h​i​c​s​B​e​g​i​n​I​m​a​g​e​C​o​n​t​e​x​t​(​n​e​w​R​e​c​t​.​s​i​z​e​)​;​

 ​ ​ ​ ​/​/​ ​R​o​u​n​d​ ​t​h​e​ ​c​o​r​n​e​r​s​
 ​ ​ ​ ​U​I​B​e​z​i​e​r​P​a​t​h​ ​*​p​a​t​h​ ​=​ ​[​U​I​B​e​z​i​e​r​P​a​t​h​ ​b​e​z​i​e​r​P​a​t​h​W​i​t​h​R​o​u​n​d​e​d​R​e​c​t​:​n​e​w​R​e​c​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​r​n​e​r​R​a​d​i​u​s​:​5​.​0​]​;​
 ​ ​ ​ ​[​p​a​t​h​ ​a​d​d​C​l​i​p​]​;​

 ​ ​ ​ ​/​/​ ​I​n​t​o​ ​w​h​a​t​ ​r​e​c​t​a​n​g​l​e​ ​s​h​a​l​l​ ​I​ ​c​o​m​p​o​s​i​t​e​ ​t​h​e​ ​i​m​a​g​e​?​
 ​ ​ ​ ​C​G​R​e​c​t​ ​p​r​o​j​e​c​t​R​e​c​t​;​
 ​ ​ ​ ​p​r​o​j​e​c​t​R​e​c​t​.​s​i​z​e​.​w​i​d​t​h​ ​=​ ​r​a​t​i​o​ ​*​ ​o​r​i​g​I​m​a​g​e​S​i​z​e​.​w​i​d​t​h​;​
 ​ ​ ​ ​p​r​o​j​e​c​t​R​e​c​t​.​s​i​z​e​.​h​e​i​g​h​t​ ​=​ ​r​a​t​i​o​ ​*​ ​o​r​i​g​I​m​a​g​e​S​i​z​e​.​h​e​i​g​h​t​;​
 ​ ​ ​ ​p​r​o​j​e​c​t​R​e​c​t​.​o​r​i​g​i​n​.​x​ ​=​ ​(​n​e​w​R​e​c​t​.​s​i​z​e​.​w​i​d​t​h​ ​-​ ​p​r​o​j​e​c​t​R​e​c​t​.​s​i​z​e​.​w​i​d​t​h​)​ ​/​ ​2​.​0​;​
 ​ ​ ​ ​p​r​o​j​e​c​t​R​e​c​t​.​o​r​i​g​i​n​.​y​ ​=​ ​(​n​e​w​R​e​c​t​.​s​i​z​e​.​h​e​i​g​h​t​ ​-​ ​p​r​o​j​e​c​t​R​e​c​t​.​s​i​z​e​.​h​e​i​g​h​t​)​ ​/​ ​2​.​0​;​

 ​ ​ ​ ​/​/​ ​D​r​a​w​ ​t​h​e​ ​i​m​a​g​e​ ​o​n​ ​i​t​
 ​ ​ ​ ​[​i​m​a​g​e​ ​d​r​a​w​I​n​R​e​c​t​:​p​r​o​j​e​c​t​R​e​c​t​]​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​i​m​a​g​e​ ​f​r​o​m​ ​t​h​e​ ​i​m​a​g​e​ ​c​o​n​t​e​x​t​,​ ​r​e​t​a​i​n​ ​i​t​ ​a​s​ ​o​u​r​ ​t​h​u​m​b​n​a​i​l​
 ​ ​ ​ ​U​I​I​m​a​g​e​ ​*​s​m​a​l​l​ ​=​ ​U​I​G​r​a​p​h​i​c​s​G​e​t​I​m​a​g​e​F​r​o​m​C​u​r​r​e​n​t​I​m​a​g​e​C​o​n​t​e​x​t​(​)​;​
 ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​T​h​u​m​b​n​a​i​l​:​s​m​a​l​l​]​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​t​h​e​ ​i​m​a​g​e​ ​a​s​ ​a​ ​P​N​G​ ​d​a​t​a​
 ​ ​ ​ ​N​S​D​a​t​a​ ​*​d​a​t​a​ ​=​ ​U​I​I​m​a​g​e​P​N​G​R​e​p​r​e​s​e​n​t​a​t​i​o​n​(​s​m​a​l​l​)​;​
 ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​T​h​u​m​b​n​a​i​l​D​a​t​a​:​d​a​t​a​]​;​

 ​ ​ ​ ​/​/​ ​C​l​e​a​n​u​p​ ​i​m​a​g​e​ ​c​o​n​t​e​x​ ​r​e​s​o​u​r​c​e​s​,​ ​w​e​'​r​e​ ​d​o​n​e​
 ​ ​ ​ ​U​I​G​r​a​p​h​i​c​s​E​n​d​I​m​a​g​e​C​o​n​t​e​x​t​(​)​;​
}​

+​ ​(​C​G​S​i​z​e​)​t​h​u​m​b​n​a​i​l​S​i​z​e​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​C​G​S​i​z​e​M​a​k​e​(​4​0​,​ ​4​0​)​;​
}​

The thumbnail attribute is not going to be saved – it is a transient attribute. You’ll need to update thumbnail from the thumbnailData when the object first emerges from the filesystem. When Homepwner used keyed archiving, we did this in initWithCoder:. Now that we’re using Core Data, objects are initialized by another Core Data object that handles creating, updating, and deleting NSManagedObjects. (You will meet this object in a moment.) Thus, you do not implement init methods for NSManagedObject subclasses. Instead, if you want to configure an object after it has been created, you override the method awakeFromFetch. Implement this method in Possession.m to set the thumbnail from the thumbnailData (which is saved).

-​ ​(​v​o​i​d​)​a​w​a​k​e​F​r​o​m​F​e​t​c​h​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​a​w​a​k​e​F​r​o​m​F​e​t​c​h​]​;​

 ​ ​ ​ ​U​I​I​m​a​g​e​ ​*​t​n​ ​=​ ​[​U​I​I​m​a​g​e​ ​i​m​a​g​e​W​i​t​h​D​a​t​a​:​[​s​e​l​f​ ​t​h​u​m​b​n​a​i​l​D​a​t​a​]​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​P​r​i​m​i​t​i​v​e​V​a​l​u​e​:​t​n​ ​f​o​r​K​e​y​:​@​"​t​h​u​m​b​n​a​i​l​"​]​;​
}​

As mentioned earlier, an instance of NSManagedObject works like a dictionary – it can hold a large number of key-value pairs. The method setPrimitiveValue:forKey: works like setObject:forKey: of NSMutableDictionary. One of the amazing and delightful things about Core Data is that instances of NSManagedObject can dynamically create accessor methods as they are called. It will also automatically create methods for primitive access. For example, the following also would have worked:

-​ ​(​v​o​i​d​)​a​w​a​k​e​F​r​o​m​F​e​t​c​h​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​a​w​a​k​e​F​r​o​m​F​e​t​c​h​]​;​

 ​ ​ ​ ​U​I​I​m​a​g​e​ ​*​t​n​ ​=​ ​[​U​I​I​m​a​g​e​ ​i​m​a​g​e​W​i​t​h​D​a​t​a​:​[​s​e​l​f​ ​t​h​u​m​b​n​a​i​l​D​a​t​a​]​]​;​

 ​ ​ ​ ​/​/​ ​A​t​ ​r​u​n​t​i​m​e​,​ ​s​e​t​P​r​i​m​i​t​i​v​e​T​h​u​m​b​n​a​i​l​:​ ​w​i​l​l​ ​b​e​ ​c​r​e​a​t​e​d​ ​w​h​e​n​ ​f​i​r​s​t​ ​c​a​l​l​e​d​
 ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​P​r​i​m​i​t​i​v​e​T​h​u​m​b​n​a​i​l​:​t​n​]​;​
}​

However, we would have received a warning from the compiler because it has no declaration of this method.

Of course, when you first launch an application, there are no saved Possessions or AssetTypes. When the user creates a new Possession instance, it will be added to the database. When objects are added to the database, they are sent the message awakeFromInsert. Here is where you will set the dateCreated instance variable of a Possession. Implement this method in Possession.m.

-​ ​(​v​o​i​d​)​a​w​a​k​e​F​r​o​m​I​n​s​e​r​t​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​a​w​a​k​e​F​r​o​m​I​n​s​e​r​t​]​;​
 ​ ​ ​ ​[​s​e​l​f​ ​s​e​t​D​a​t​e​C​r​e​a​t​e​d​:​[​N​S​D​a​t​e​ ​d​a​t​e​]​]​;​
}​

Updating PossessionStore

The portal through which you talk to the database is the NSManagedObjectContext. The NSManagedObjectContext uses an NSPersistentStoreCoordinator. You ask the persistent store coordinator to open a SQLite database at a particular filename. The persistent store coordinator uses the model file in the form of an instance of NSManagedObjectModel.

Figure 17.11  PossessionStore and NSManagedObjectContext

PossessionStore and NSManagedObjectContext

In PossessionStore.h, add instance variables and import Core Data. Also, create a method for getting all the AssetType objects:

#​i​m​p​o​r​t​ ​<​C​o​r​e​D​a​t​a​/​C​o​r​e​D​a​t​a​.​h​>​

@​c​l​a​s​s​ ​P​o​s​s​e​s​s​i​o​n​;​

@​i​n​t​e​r​f​a​c​e​ ​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​:​ ​N​S​O​b​j​e​c​t​
{​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​a​l​l​P​o​s​s​e​s​s​i​o​n​s​;​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​a​l​l​A​s​s​e​t​T​y​p​e​s​;​
 ​ ​ ​ ​N​S​M​a​n​a​g​e​d​O​b​j​e​c​t​C​o​n​t​e​x​t​ ​*​c​o​n​t​e​x​t​;​
 ​ ​ ​ ​N​S​M​a​n​a​g​e​d​O​b​j​e​c​t​M​o​d​e​l​ ​*​m​o​d​e​l​;​
}​

+​ ​(​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​*​)​d​e​f​a​u​l​t​S​t​o​r​e​;​
-​ ​(​B​O​O​L​)​s​a​v​e​C​h​a​n​g​e​s​;​

#​p​r​a​g​m​a​ ​m​a​r​k​ ​P​o​s​s​e​s​s​i​o​n​s​
-​ ​(​N​S​A​r​r​a​y​ ​*​)​a​l​l​P​o​s​s​e​s​s​i​o​n​s​;​
-​ ​(​P​o​s​s​e​s​s​i​o​n​ ​*​)​c​r​e​a​t​e​P​o​s​s​e​s​s​i​o​n​;​
-​ ​(​v​o​i​d​)​r​e​m​o​v​e​P​o​s​s​e​s​s​i​o​n​:​(​P​o​s​s​e​s​s​i​o​n​ ​*​)​p​;​
-​ ​(​v​o​i​d​)​m​o​v​e​P​o​s​s​e​s​s​i​o​n​A​t​I​n​d​e​x​:​(​i​n​t​)​f​r​o​m​ ​t​o​I​n​d​e​x​:​(​i​n​t​)​t​o​;​

#​p​r​a​g​m​a​ ​m​a​r​k​ ​A​s​s​e​t​ ​t​y​p​e​s​
-​ ​(​N​S​A​r​r​a​y​ ​*​)​a​l​l​A​s​s​e​t​T​y​p​e​s​;​

@​e​n​d​

This is the first time you’ve seen the #pragma mark construct. Objective-C programmers often use this to group methods. It isn’t used by the compiler at all – rather, Xcode notes it when it creates the pop-up at the top of the editor (Figure 17.12).

Figure 17.12  #pragma mark in popup

#pragma mark in popup

When the PossessionStore is initialized, it needs to set up the NSManagedObjectContext and NSPersistentStoreCoordinator. The persistent store coordinator needs to know two things: What are all of my entities and their attributes and relationships? and Where am I saving and loading data from? Therefore, you will create an instance of NSManagedObjectModel to hold the entity information from Homepwner.xcdatamodeld and initialize the persistent store coordinator with this object. Then, you will create the instance of NSManagedObjectContext and specify that it use this persistent store coordinator to save and load objects. Update init in PossessionStore.m.

-​ ​(​i​d​)​i​n​i​t​
{​
 ​ ​ ​ ​i​f​ ​(​d​e​f​a​u​l​t​S​t​o​r​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​d​e​f​a​u​l​t​S​t​o​r​e​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​;​

 ​ ​ ​ ​/​/​ ​R​e​a​d​ ​i​n​ ​H​o​m​e​p​w​n​e​r​.​x​c​d​a​t​a​m​o​d​e​l​d​
 ​ ​ ​ ​m​o​d​e​l​ ​=​ ​[​[​N​S​M​a​n​a​g​e​d​O​b​j​e​c​t​M​o​d​e​l​ ​m​e​r​g​e​d​M​o​d​e​l​F​r​o​m​B​u​n​d​l​e​s​:​n​i​l​]​ ​r​e​t​a​i​n​]​;​
 ​ ​ ​ ​/​/​ ​N​S​L​o​g​(​@​"​m​o​d​e​l​ ​=​ ​%​@​"​,​ ​m​o​d​e​l​)​;​

 ​ ​ ​ ​N​S​P​e​r​s​i​s​t​e​n​t​S​t​o​r​e​C​o​o​r​d​i​n​a​t​o​r​ ​*​p​s​c​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​N​S​P​e​r​s​i​s​t​e​n​t​S​t​o​r​e​C​o​o​r​d​i​n​a​t​o​r​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​M​a​n​a​g​e​d​O​b​j​e​c​t​M​o​d​e​l​:​m​o​d​e​l​]​;​

 ​ ​ ​ ​/​/​ ​W​h​e​r​e​ ​d​o​e​s​ ​t​h​e​ ​S​Q​L​i​t​e​ ​f​i​l​e​ ​g​o​?​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​p​a​t​h​I​n​D​o​c​u​m​e​n​t​D​i​r​e​c​t​o​r​y​(​@​"​s​t​o​r​e​.​d​a​t​a​"​)​;​
 ​ ​ ​ ​N​S​U​R​L​ ​*​s​t​o​r​e​U​R​L​ ​=​ ​[​N​S​U​R​L​ ​f​i​l​e​U​R​L​W​i​t​h​P​a​t​h​:​p​a​t​h​]​;​

 ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​ ​=​ ​n​i​l​;​

 ​ ​ ​ ​i​f​ ​(​!​[​p​s​c​ ​a​d​d​P​e​r​s​i​s​t​e​n​t​S​t​o​r​e​W​i​t​h​T​y​p​e​:​N​S​S​Q​L​i​t​e​S​t​o​r​e​T​y​p​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​:​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​U​R​L​:​s​t​o​r​e​U​R​L​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​p​t​i​o​n​s​:​n​i​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​r​r​o​r​:​&​e​r​r​o​r​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​N​S​E​x​c​e​p​t​i​o​n​ ​r​a​i​s​e​:​@​"​O​p​e​n​ ​f​a​i​l​e​d​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​m​a​t​:​@​"​R​e​a​s​o​n​:​ ​%​@​"​,​ ​[​e​r​r​o​r​ ​l​o​c​a​l​i​z​e​d​D​e​s​c​r​i​p​t​i​o​n​]​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​t​h​e​ ​m​a​n​a​g​e​d​ ​o​b​j​e​c​t​ ​c​o​n​t​e​x​t​
 ​ ​ ​ ​c​o​n​t​e​x​t​ ​=​ ​[​[​N​S​M​a​n​a​g​e​d​O​b​j​e​c​t​C​o​n​t​e​x​t​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​[​c​o​n​t​e​x​t​ ​s​e​t​P​e​r​s​i​s​t​e​n​t​S​t​o​r​e​C​o​o​r​d​i​n​a​t​o​r​:​p​s​c​]​;​
 ​ ​ ​ ​[​p​s​c​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​m​a​n​a​g​e​d​ ​o​b​j​e​c​t​ ​c​o​n​t​e​x​t​ ​c​a​n​ ​m​a​n​a​g​e​ ​u​n​d​o​,​ ​b​u​t​ ​w​e​ ​d​o​n​'​t​ ​n​e​e​d​ ​i​t​
 ​ ​ ​ ​[​c​o​n​t​e​x​t​ ​s​e​t​U​n​d​o​M​a​n​a​g​e​r​:​n​i​l​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

Before, PossessionStore would write out the entire NSMutableArray of Possessions when you asked it to save using keyed archiving. Now, you will have it send the message save: to the NSManagedObjectContext. The context will update all of the records in store.data with any changes since the last time it was saved. Change this method in PossessionStore.m.

-​ ​(​B​O​O​L​)​s​a​v​e​C​h​a​n​g​e​s​
{​
 ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​ ​=​ ​n​i​l​;​
 ​ ​ ​ ​B​O​O​L​ ​s​u​c​c​e​s​s​f​u​l​ ​=​ ​[​c​o​n​t​e​x​t​ ​s​a​v​e​:​&​e​r​r​]​;​
 ​ ​ ​ ​i​f​ ​(​!​s​u​c​c​e​s​s​f​u​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​E​r​r​o​r​ ​s​a​v​i​n​g​:​ ​%​@​"​,​ ​[​e​r​r​ ​l​o​c​a​l​i​z​e​d​D​e​s​c​r​i​p​t​i​o​n​]​)​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​u​c​c​e​s​s​f​u​l​;​
}​

Note that this method is already called when the application is moved to the background.

In this application, we will fetch all of the Possessions in store.data the first time we need to use them. In order to get objects back from the NSManagedObjectContext, you must prepare and execute an NSFetchRequest. After a fetch request is executed, you will get an array of all the objects that match the parameters of that request.

A fetch request needs an entity description that defines which entity you want to get objects from. In order to fetch Possession instances, you specify the Possession entity. You can also set the request’s sort descriptors, which specify the order of the objects in the array. A sort descriptor has a key that maps to an attribute of the entity and a BOOL that indicates if the order should be ascending or descending. You will sort the returned Possessions by their orderingValue in ascending order. Replace the fetchPossessionsIfNecessary method in PossessionStore.m.

-​ ​(​v​o​i​d​)​f​e​t​c​h​P​o​s​s​e​s​s​i​o​n​s​I​f​N​e​c​e​s​s​a​r​y​
{​
 ​ ​ ​ ​i​f​ ​(​!​a​l​l​P​o​s​s​e​s​s​i​o​n​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​F​e​t​c​h​R​e​q​u​e​s​t​ ​*​r​e​q​u​e​s​t​ ​=​ ​[​[​[​N​S​F​e​t​c​h​R​e​q​u​e​s​t​ ​a​l​l​o​c​]​ ​i​n​i​t​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​E​n​t​i​t​y​D​e​s​c​r​i​p​t​i​o​n​ ​*​e​ ​=​ ​[​[​m​o​d​e​l​ ​e​n​t​i​t​i​e​s​B​y​N​a​m​e​]​ ​o​b​j​e​c​t​F​o​r​K​e​y​:​@​"​P​o​s​s​e​s​s​i​o​n​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​r​e​q​u​e​s​t​ ​s​e​t​E​n​t​i​t​y​:​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​o​r​t​D​e​s​c​r​i​p​t​o​r​ ​*​s​d​ ​=​ ​[​N​S​S​o​r​t​D​e​s​c​r​i​p​t​o​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​o​r​t​D​e​s​c​r​i​p​t​o​r​W​i​t​h​K​e​y​:​@​"​o​r​d​e​r​i​n​g​V​a​l​u​e​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​s​c​e​n​d​i​n​g​:​Y​E​S​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​r​e​q​u​e​s​t​ ​s​e​t​S​o​r​t​D​e​s​c​r​i​p​t​o​r​s​:​[​N​S​A​r​r​a​y​ ​a​r​r​a​y​W​i​t​h​O​b​j​e​c​t​:​s​d​]​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​r​e​s​u​l​t​ ​=​ ​[​c​o​n​t​e​x​t​ ​e​x​e​c​u​t​e​F​e​t​c​h​R​e​q​u​e​s​t​:​r​e​q​u​e​s​t​ ​e​r​r​o​r​:​&​e​r​r​o​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​r​e​s​u​l​t​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​N​S​E​x​c​e​p​t​i​o​n​ ​r​a​i​s​e​:​@​"​F​e​t​c​h​ ​f​a​i​l​e​d​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​m​a​t​:​@​"​R​e​a​s​o​n​:​ ​%​@​"​,​ ​[​e​r​r​o​r​ ​l​o​c​a​l​i​z​e​d​D​e​s​c​r​i​p​t​i​o​n​]​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​A​r​r​a​y​:​r​e​s​u​l​t​]​;​
 ​ ​ ​ ​}​
}​

In this application, you immediately fetched all the instances of the Possession entity. This is a relatively simple case. In an application with a much larger data set, you would carefully fetch just the instances you needed. You would add a predicate (an NSPredicate) to your fetch request, and only the objects that satisfied the predicate would be returned.

A predicate contains a condition which can be true or false. For example, if you only wanted the possessions worth more than $50, you would create a predicate and add it to the fetch request like this:

N​S​P​r​e​d​i​c​a​t​e​ ​*​p​ ​=​ ​[​N​S​P​r​e​d​i​c​a​t​e​ ​p​r​e​d​i​c​a​t​e​W​i​t​h​F​o​r​m​a​t​:​@​"​v​a​l​u​e​I​n​D​o​l​l​a​r​s​ ​>​ ​5​0​"​]​;​
[​r​e​q​u​e​s​t​ ​s​e​t​P​r​e​d​i​c​a​t​e​:​p​]​;​

The format string for a predicate can be very long and complex. Apple’s Predicate Programming Guide is a complete discussion of what is possible.

Predicates can also be used to filter the contents of an array. So, even if you had already fetched the allPossessions array, you could still use a predicate:

N​S​A​r​r​a​y​ ​*​e​x​p​e​n​s​i​v​e​S​t​u​f​f​ ​=​ ​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​f​i​l​t​e​r​e​d​A​r​r​a​y​U​s​i​n​g​P​r​e​d​i​c​a​t​e​:​p​]​;​

This handles saving and loading, but what about adding and deleting? When the user wants to create a new Possession, you will not allocate and initialize this new Possession. Instead, you will ask the NSManagedObjectContext to insert a new object from the Possession entity. It will then return an instance of Possession. In PossessionStore.m, edit the createPossession method.

-​ ​(​P​o​s​s​e​s​s​i​o​n​ ​*​)​c​r​e​a​t​e​P​o​s​s​e​s​s​i​o​n​
{​
 ​ ​ ​ ​/​/​ ​E​n​s​u​r​e​ ​t​h​e​ ​a​r​r​a​y​ ​i​s​ ​i​n​i​t​i​a​l​i​z​e​d​
 ​ ​ ​ ​[​s​e​l​f​ ​f​e​t​c​h​P​o​s​s​e​s​s​i​o​n​s​I​f​N​e​c​e​s​s​a​r​y​]​;​

 ​ ​ ​ ​d​o​u​b​l​e​ ​o​r​d​e​r​;​
 ​ ​ ​ ​i​f​ ​(​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​c​o​u​n​t​]​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​o​r​d​e​r​ ​=​ ​1​.​0​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​o​r​d​e​r​ ​=​ ​[​[​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​l​a​s​t​O​b​j​e​c​t​]​ ​o​r​d​e​r​i​n​g​V​a​l​u​e​]​ ​d​o​u​b​l​e​V​a​l​u​e​]​ ​+​ ​1​.​0​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​N​S​L​o​g​(​@​"​A​d​d​i​n​g​ ​a​f​t​e​r​ ​%​d​ ​i​t​e​m​s​,​ ​o​r​d​e​r​ ​=​ ​%​.​2​f​"​,​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​c​o​u​n​t​]​,​ ​o​r​d​e​r​)​;​

 ​ ​ ​ ​P​o​s​s​e​s​s​i​o​n​ ​*​p​ ​=​ ​[​N​S​E​n​t​i​t​y​D​e​s​c​r​i​p​t​i​o​n​ ​i​n​s​e​r​t​N​e​w​O​b​j​e​c​t​F​o​r​E​n​t​i​t​y​F​o​r​N​a​m​e​:​@​"​P​o​s​s​e​s​s​i​o​n​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​M​a​n​a​g​e​d​O​b​j​e​c​t​C​o​n​t​e​x​t​:​c​o​n​t​e​x​t​]​;​

 ​ ​ ​ ​[​p​ ​s​e​t​O​r​d​e​r​i​n​g​V​a​l​u​e​:​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​D​o​u​b​l​e​:​o​r​d​e​r​]​]​;​

 ​ ​ ​ ​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​a​d​d​O​b​j​e​c​t​:​p​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​p​;​
}​

When a user deletes a Possession, you must inform the context so that it is removed from the database when saved. Add the following code to PossessionStore.m’s removePossession: method.

-​ ​(​v​o​i​d​)​r​e​m​o​v​e​P​o​s​s​e​s​s​i​o​n​:​(​P​o​s​s​e​s​s​i​o​n​ ​*​)​p​
{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​k​e​y​ ​=​ ​[​p​ ​i​m​a​g​e​K​e​y​]​;​
 ​ ​ ​ ​[​[​I​m​a​g​e​S​t​o​r​e​ ​d​e​f​a​u​l​t​I​m​a​g​e​S​t​o​r​e​]​ ​d​e​l​e​t​e​I​m​a​g​e​F​o​r​K​e​y​:​k​e​y​]​;​
 ​ ​ ​ ​[​c​o​n​t​e​x​t​ ​d​e​l​e​t​e​O​b​j​e​c​t​:​p​]​;​
 ​ ​ ​ ​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​r​e​m​o​v​e​O​b​j​e​c​t​I​d​e​n​t​i​c​a​l​T​o​:​p​]​;​
}​

The last bit of functionality you need to replace for Possession is the ability to re-order Possessions in the PossessionStore. Because Core Data will not handle ordering automatically, we must update a Possession’s orderingValue every time it is moved in the table view.

This would get rather complicated if the orderingValue was an integer: every time a Possession was placed in a new index, we would have to change the orderingValue’s of other Possessions. Instead, orderingValue is a double. We can then take the orderingValues of the Possession that will be before and after the moving possession, add them together, and divide by two. Thus, the new orderingValue will fall directly in between the values of the Possessions that surround it. Modify movePossessionAtIndex:toIndex: in PossessionStore.m.

-​ ​(​v​o​i​d​)​m​o​v​e​P​o​s​s​e​s​s​i​o​n​A​t​I​n​d​e​x​:​(​i​n​t​)​f​r​o​m​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​o​I​n​d​e​x​:​(​i​n​t​)​t​o​
{​
 ​ ​ ​ ​i​f​ ​(​f​r​o​m​ ​=​=​ ​t​o​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​/​/​ ​G​e​t​ ​p​o​i​n​t​e​r​ ​t​o​ ​o​b​j​e​c​t​ ​b​e​i​n​g​ ​m​o​v​e​d​
 ​ ​ ​ ​P​o​s​s​e​s​s​i​o​n​ ​*​p​ ​=​ ​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​f​r​o​m​]​;​

 ​ ​ ​ ​/​/​ ​R​e​t​a​i​n​ ​i​t​ ​s​o​ ​i​t​ ​d​o​e​s​n​'​t​ ​g​e​t​ ​d​e​a​l​l​o​c​e​d​ ​w​h​i​l​e​ ​o​u​t​ ​o​f​ ​a​r​r​a​y​
 ​ ​ ​ ​[​p​ ​r​e​t​a​i​n​]​;​

 ​ ​ ​ ​/​/​ ​R​e​m​o​v​e​ ​p​ ​f​r​o​m​ ​o​u​r​ ​a​r​r​a​y​
 ​ ​ ​ ​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​r​e​m​o​v​e​O​b​j​e​c​t​A​t​I​n​d​e​x​:​f​r​o​m​]​;​

 ​ ​ ​ ​/​/​ ​R​e​-​i​n​s​e​r​t​ ​p​ ​i​n​t​o​ ​a​r​r​a​y​ ​a​t​ ​n​e​w​ ​l​o​c​a​t​i​o​n​
 ​ ​ ​ ​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​i​n​s​e​r​t​O​b​j​e​c​t​:​p​ ​a​t​I​n​d​e​x​:​t​o​]​;​

 ​ ​ ​ ​/​/​ ​C​o​m​p​u​t​i​n​g​ ​a​ ​n​e​w​ ​o​r​d​e​r​V​a​l​u​e​ ​f​o​r​ ​t​h​e​ ​o​b​j​e​c​t​ ​t​h​a​t​ ​w​a​s​ ​m​o​v​e​d​
 ​ ​ ​ ​d​o​u​b​l​e​ ​l​o​w​e​r​B​o​u​n​d​ ​=​ ​0​.​0​;​

 ​ ​ ​ ​/​/​ ​I​s​ ​t​h​e​r​e​ ​a​n​ ​o​b​j​e​c​t​ ​b​e​f​o​r​e​ ​i​t​ ​i​n​ ​t​h​e​ ​a​r​r​a​y​?​
 ​ ​ ​ ​i​f​ ​(​t​o​ ​>​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​l​o​w​e​r​B​o​u​n​d​ ​=​ ​[​[​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​t​o​ ​-​ ​1​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​r​d​e​r​i​n​g​V​a​l​u​e​]​ ​d​o​u​b​l​e​V​a​l​u​e​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​l​o​w​e​r​B​o​u​n​d​ ​=​ ​[​[​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​1​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​r​d​e​r​i​n​g​V​a​l​u​e​]​ ​d​o​u​b​l​e​V​a​l​u​e​]​ ​-​ ​2​.​0​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​d​o​u​b​l​e​ ​u​p​p​e​r​B​o​u​n​d​ ​=​ ​0​.​0​;​

 ​ ​ ​ ​/​/​ ​I​s​ ​t​h​e​r​e​ ​a​n​ ​o​b​j​e​c​t​ ​a​f​t​e​r​ ​i​t​ ​i​n​ ​t​h​e​ ​a​r​r​a​y​?​
 ​ ​ ​ ​i​f​ ​(​t​o​ ​<​ ​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​c​o​u​n​t​]​ ​-​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​u​p​p​e​r​B​o​u​n​d​ ​=​ ​[​[​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​t​o​ ​+​ ​1​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​r​d​e​r​i​n​g​V​a​l​u​e​]​ ​d​o​u​b​l​e​V​a​l​u​e​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​u​p​p​e​r​B​o​u​n​d​ ​=​ ​[​[​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​t​o​ ​-​ ​1​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​r​d​e​r​i​n​g​V​a​l​u​e​]​ ​d​o​u​b​l​e​V​a​l​u​e​]​ ​+​ ​2​.​0​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​T​h​e​ ​o​r​d​e​r​ ​v​a​l​u​e​ ​w​i​l​l​ ​b​e​ ​t​h​e​ ​m​i​d​p​o​i​n​t​ ​b​e​t​w​e​e​n​ ​t​h​e​ ​l​o​w​e​r​ ​a​n​d​ ​u​p​p​e​r​ ​b​o​u​n​d​s​
 ​ ​ ​ ​N​S​N​u​m​b​e​r​ ​*​n​ ​=​ ​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​D​o​u​b​l​e​:​(​l​o​w​e​r​B​o​u​n​d​ ​+​ ​u​p​p​e​r​B​o​u​n​d​)​/​2​.​0​]​;​

 ​ ​ ​ ​N​S​L​o​g​(​@​"​m​o​v​i​n​g​ ​t​o​ ​o​r​d​e​r​ ​%​@​"​,​ ​n​)​;​
 ​ ​ ​ ​[​p​ ​s​e​t​O​r​d​e​r​i​n​g​V​a​l​u​e​:​n​]​;​

 ​ ​ ​ ​/​/​ ​R​e​l​e​a​s​e​ ​p​ ​(​r​e​t​a​i​n​ ​c​o​u​n​t​ ​=​ ​1​,​ ​o​n​l​y​ ​o​w​n​e​r​ ​i​s​ ​n​o​w​ ​a​r​r​a​y​)​
 ​ ​ ​ ​[​p​ ​r​e​l​e​a​s​e​]​;​

}​

One last detail. When we moved Possession to a subclass of NSManagedObject, the valueInDollars property became an instance of NSNumber instead of an int. There are three places we still use this instance variable. Update the code in the viewWillAppear: method of ItemDetailViewController.m.

-​ ​(​v​o​i​d​)​v​i​e​w​W​i​l​l​A​p​p​e​a​r​:​(​B​O​O​L​)​a​n​i​m​a​t​e​d​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​v​i​e​w​W​i​l​l​A​p​p​e​a​r​:​a​n​i​m​a​t​e​d​]​;​

 ​ ​ ​ ​[​n​a​m​e​F​i​e​l​d​ ​s​e​t​T​e​x​t​:​[​p​o​s​s​e​s​s​i​o​n​ ​p​o​s​s​e​s​s​i​o​n​N​a​m​e​]​]​;​
 ​ ​ ​ ​[​s​e​r​i​a​l​N​u​m​b​e​r​F​i​e​l​d​ ​s​e​t​T​e​x​t​:​[​p​o​s​s​e​s​s​i​o​n​ ​s​e​r​i​a​l​N​u​m​b​e​r​]​]​;​

 ​ ​ ​ ​i​f​(​[​p​o​s​s​e​s​s​i​o​n​ ​v​a​l​u​e​I​n​D​o​l​l​a​r​s​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​N​o​t​i​c​e​ ​t​h​a​t​ ​t​h​e​ ​f​o​r​m​a​t​ ​s​t​r​i​n​g​ ​c​h​a​n​g​e​d​
 ​ ​ ​ ​ ​ ​ ​ ​[​v​a​l​u​e​F​i​e​l​d​ ​s​e​t​T​e​x​t​:​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​@​"​%​@​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​v​a​l​u​e​I​n​D​o​l​l​a​r​s​]​]​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​v​a​l​u​e​F​i​e​l​d​ ​s​e​t​T​e​x​t​:​@​"​0​"​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​ ​*​d​a​t​e​F​o​r​m​a​t​t​e​r​ ​=​ ​[​[​[​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​

In the same file, update viewWillDisappear:.

-​ ​(​v​o​i​d​)​v​i​e​w​W​i​l​l​D​i​s​a​p​p​e​a​r​:​(​B​O​O​L​)​a​n​i​m​a​t​e​d​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​v​i​e​w​W​i​l​l​D​i​s​a​p​p​e​a​r​:​a​n​i​m​a​t​e​d​]​;​

 ​ ​ ​ ​/​/​ ​E​n​d​ ​a​n​y​ ​i​n​c​o​m​p​l​e​t​e​ ​e​d​i​t​i​n​g​
 ​ ​ ​ ​[​[​s​e​l​f​ ​v​i​e​w​]​ ​e​n​d​E​d​i​t​i​n​g​:​Y​E​S​]​;​

 ​ ​ ​ ​/​/​ ​"​S​a​v​e​"​ ​c​h​a​n​g​e​s​ ​t​o​ ​p​o​s​s​e​s​s​i​o​n​
 ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​s​e​t​P​o​s​s​e​s​s​i​o​n​N​a​m​e​:​[​n​a​m​e​F​i​e​l​d​ ​t​e​x​t​]​]​;​
 ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​s​e​t​S​e​r​i​a​l​N​u​m​b​e​r​:​[​s​e​r​i​a​l​N​u​m​b​e​r​F​i​e​l​d​ ​t​e​x​t​]​]​;​
 ​ ​ ​ ​N​S​N​u​m​b​e​r​ ​*​v​a​l​u​e​N​u​m​ ​=​ ​[​N​S​N​u​m​b​e​r​ ​n​u​m​b​e​r​W​i​t​h​I​n​t​:​[​[​v​a​l​u​e​F​i​e​l​d​ ​t​e​x​t​]​ ​i​n​t​V​a​l​u​e​]​]​;​
 ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​s​e​t​V​a​l​u​e​I​n​D​o​l​l​a​r​s​:​v​a​l​u​e​N​u​m​]​;​
}​

And the third and final spot, in HomepwnerItemCell.m:

-​ ​(​v​o​i​d​)​s​e​t​P​o​s​s​e​s​s​i​o​n​:​(​P​o​s​s​e​s​s​i​o​n​ ​*​)​p​o​s​s​e​s​s​i​o​n​
{​
 ​ ​ ​ ​/​/​ ​T​h​e​ ​f​o​r​m​a​t​ ​s​t​r​i​n​g​ ​c​h​a​n​g​e​s​ ​a​g​a​i​n​
 ​ ​ ​ ​[​v​a​l​u​e​L​a​b​e​l​ ​s​e​t​T​e​x​t​:​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​@​"​$​%​@​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​v​a​l​u​e​I​n​D​o​l​l​a​r​s​]​]​]​;​
 ​ ​ ​ ​[​n​a​m​e​L​a​b​e​l​ ​s​e​t​T​e​x​t​:​[​p​o​s​s​e​s​s​i​o​n​ ​p​o​s​s​e​s​s​i​o​n​N​a​m​e​]​]​;​
 ​ ​ ​ ​[​i​m​a​g​e​V​i​e​w​ ​s​e​t​I​m​a​g​e​:​[​p​o​s​s​e​s​s​i​o​n​ ​t​h​u​m​b​n​a​i​l​]​]​;​
}​

Finally, you can build and run your application. Of course, the behavior is the same as it always was, but it is now using Core Data. Now, you need to take care of the new asset type functionality.

Adding AssetTypes to Homepwner

In the model file, you described a new entity, AssetType, that every possession will have a to-one relationship to. You need a way for the user to set the AssetType of Possessions and create new AssetTypes. Also, the PossessionStore will need a way to fetch the AssetTypes. (Creating new AssetTypes is left as a challenge.)

In PossessionStore.h, declare a new method.

-​ ​(​N​S​A​r​r​a​y​ ​*​)​a​l​l​A​s​s​e​t​T​y​p​e​s​;​

In PossessionStore.m, define this method. If this is the first time the application is being run – and therefore there are no AssetTypes in the store – create three default types.

-​ ​(​N​S​A​r​r​a​y​ ​*​)​a​l​l​A​s​s​e​t​T​y​p​e​s​
{​
 ​ ​ ​ ​i​f​ ​(​!​a​l​l​A​s​s​e​t​T​y​p​e​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​F​e​t​c​h​R​e​q​u​e​s​t​ ​*​r​e​q​u​e​s​t​ ​=​ ​[​[​[​N​S​F​e​t​c​h​R​e​q​u​e​s​t​ ​a​l​l​o​c​]​ ​i​n​i​t​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​E​n​t​i​t​y​D​e​s​c​r​i​p​t​i​o​n​ ​*​e​ ​=​ ​[​[​m​o​d​e​l​ ​e​n​t​i​t​i​e​s​B​y​N​a​m​e​]​ ​o​b​j​e​c​t​F​o​r​K​e​y​:​@​"​A​s​s​e​t​T​y​p​e​"​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​[​r​e​q​u​e​s​t​ ​s​e​t​E​n​t​i​t​y​:​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​r​e​s​u​l​t​ ​=​ ​[​c​o​n​t​e​x​t​ ​e​x​e​c​u​t​e​F​e​t​c​h​R​e​q​u​e​s​t​:​r​e​q​u​e​s​t​ ​e​r​r​o​r​:​&​e​r​r​o​r​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​r​e​s​u​l​t​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​N​S​E​x​c​e​p​t​i​o​n​ ​r​a​i​s​e​:​@​"​F​e​t​c​h​ ​f​a​i​l​e​d​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​m​a​t​:​@​"​R​e​a​s​o​n​:​ ​%​@​"​,​ ​[​e​r​r​o​r​ ​l​o​c​a​l​i​z​e​d​D​e​s​c​r​i​p​t​i​o​n​]​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​ ​ ​ ​ ​a​l​l​A​s​s​e​t​T​y​p​e​s​ ​=​ ​[​r​e​s​u​l​t​ ​m​u​t​a​b​l​e​C​o​p​y​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​/​/​ ​I​s​ ​t​h​i​s​ ​t​h​e​ ​f​i​r​s​t​ ​t​i​m​e​ ​t​h​e​ ​p​r​o​g​r​a​m​ ​i​s​ ​b​e​i​n​g​ ​r​u​n​?​
 ​ ​ ​ ​i​f​ ​(​[​a​l​l​A​s​s​e​t​T​y​p​e​s​ ​c​o​u​n​t​]​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​M​a​n​a​g​e​d​O​b​j​e​c​t​ ​*​t​y​p​e​;​

 ​ ​ ​ ​ ​ ​ ​ ​t​y​p​e​ ​=​ ​[​N​S​E​n​t​i​t​y​D​e​s​c​r​i​p​t​i​o​n​ ​i​n​s​e​r​t​N​e​w​O​b​j​e​c​t​F​o​r​E​n​t​i​t​y​F​o​r​N​a​m​e​:​@​"​A​s​s​e​t​T​y​p​e​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​M​a​n​a​g​e​d​O​b​j​e​c​t​C​o​n​t​e​x​t​:​c​o​n​t​e​x​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​y​p​e​ ​s​e​t​V​a​l​u​e​:​@​"​F​u​r​n​i​t​u​r​e​"​ ​f​o​r​K​e​y​:​@​"​l​a​b​e​l​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​a​l​l​A​s​s​e​t​T​y​p​e​s​ ​a​d​d​O​b​j​e​c​t​:​t​y​p​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​t​y​p​e​ ​=​ ​[​N​S​E​n​t​i​t​y​D​e​s​c​r​i​p​t​i​o​n​ ​i​n​s​e​r​t​N​e​w​O​b​j​e​c​t​F​o​r​E​n​t​i​t​y​F​o​r​N​a​m​e​:​@​"​A​s​s​e​t​T​y​p​e​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​M​a​n​a​g​e​d​O​b​j​e​c​t​C​o​n​t​e​x​t​:​c​o​n​t​e​x​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​y​p​e​ ​s​e​t​V​a​l​u​e​:​@​"​J​e​w​e​l​r​y​"​ ​f​o​r​K​e​y​:​@​"​l​a​b​e​l​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​a​l​l​A​s​s​e​t​T​y​p​e​s​ ​a​d​d​O​b​j​e​c​t​:​t​y​p​e​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​t​y​p​e​ ​=​ ​[​N​S​E​n​t​i​t​y​D​e​s​c​r​i​p​t​i​o​n​ ​i​n​s​e​r​t​N​e​w​O​b​j​e​c​t​F​o​r​E​n​t​i​t​y​F​o​r​N​a​m​e​:​@​"​A​s​s​e​t​T​y​p​e​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​M​a​n​a​g​e​d​O​b​j​e​c​t​C​o​n​t​e​x​t​:​c​o​n​t​e​x​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​y​p​e​ ​s​e​t​V​a​l​u​e​:​@​"​E​l​e​c​t​r​o​n​i​c​s​"​ ​f​o​r​K​e​y​:​@​"​l​a​b​e​l​"​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​[​a​l​l​A​s​s​e​t​T​y​p​e​s​ ​a​d​d​O​b​j​e​c​t​:​t​y​p​e​]​;​

 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​a​l​l​A​s​s​e​t​T​y​p​e​s​;​
}​

Now, you need change the user interface so that the user can see the AssetType of the Possession in the ItemDetailViewController and change it.

Figure 17.13  Interface for AssetType

Interface for AssetType

Create a new Objective-C class template file and choose NSObject as the superclass. Name this class AssetTypePicker.

In AssetTypePicker.h, change the superclass to UITableViewController and give it a Possession property.

#​i​m​p​o​r​t​ ​<​U​I​K​i​t​/​U​I​K​i​t​.​h​>​
@​c​l​a​s​s​ ​P​o​s​s​e​s​s​i​o​n​;​

@​i​n​t​e​r​f​a​c​e​ ​A​s​s​e​t​T​y​p​e​P​i​c​k​e​r​ ​:​ ​U​I​T​a​b​l​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​
{​
 ​ ​ ​ ​P​o​s​s​e​s​s​i​o​n​ ​*​p​o​s​s​e​s​s​i​o​n​;​
}​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​P​o​s​s​e​s​s​i​o​n​ ​*​p​o​s​s​e​s​s​i​o​n​;​
@​e​n​d​

This table view controller will show a list of the available AssetTypes. Tapping a button on the ItemDetailViewController’s view will display it (in a popover on the iPad and in a navigation controller on the iPhone). Implement the data source methods and import the appropriate header files in AssetTypePicker.m.

#​i​m​p​o​r​t​ ​"​A​s​s​e​t​T​y​p​e​P​i​c​k​e​r​.​h​"​
#​i​m​p​o​r​t​ ​"​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​.​h​"​
#​i​m​p​o​r​t​ ​"​P​o​s​s​e​s​s​i​o​n​.​h​"​


@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​A​s​s​e​t​T​y​p​e​P​i​c​k​e​r​

@​s​y​n​t​h​e​s​i​z​e​ ​p​o​s​s​e​s​s​i​o​n​;​

-​ ​(​i​d​)​i​n​i​t​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​S​t​y​l​e​:​U​I​T​a​b​l​e​V​i​e​w​S​t​y​l​e​G​r​o​u​p​e​d​]​;​
}​
-​ ​(​i​d​)​i​n​i​t​W​i​t​h​S​t​y​l​e​:​(​U​I​T​a​b​l​e​V​i​e​w​S​t​y​l​e​)​s​t​y​l​e​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​s​e​l​f​ ​i​n​i​t​]​;​
}​
-​ ​(​v​o​i​d​)​d​e​a​l​l​o​c​
{​
 ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​

-​ ​(​N​S​I​n​t​e​g​e​r​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​t​a​b​l​e​V​i​e​w​
 ​n​u​m​b​e​r​O​f​R​o​w​s​I​n​S​e​c​t​i​o​n​:​(​N​S​I​n​t​e​g​e​r​)​s​e​c​t​i​o​n​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​[​[​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​d​e​f​a​u​l​t​S​t​o​r​e​]​ ​a​l​l​A​s​s​e​t​T​y​p​e​s​]​ ​c​o​u​n​t​]​;​
}​

-​ ​(​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​ ​*​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​t​a​b​l​e​V​i​e​w​
 ​ ​ ​ ​ ​ ​ ​ ​ ​c​e​l​l​F​o​r​R​o​w​A​t​I​n​d​e​x​P​a​t​h​:​(​N​S​I​n​d​e​x​P​a​t​h​ ​*​)​i​p​
{​
 ​ ​ ​ ​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​ ​*​c​e​l​l​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​t​a​b​l​e​V​i​e​w​ ​d​e​q​u​e​u​e​R​e​u​s​a​b​l​e​C​e​l​l​W​i​t​h​I​d​e​n​t​i​f​i​e​r​:​@​"​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​"​]​;​
 ​ ​ ​ ​i​f​ ​(​c​e​l​l​ ​=​=​ ​n​i​l​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​e​l​l​ ​=​ ​[​[​[​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​S​t​y​l​e​:​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​S​t​y​l​e​D​e​f​a​u​l​t​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​u​s​e​I​d​e​n​t​i​f​i​e​r​:​@​"​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​"​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​a​l​l​A​s​s​e​t​s​ ​=​ ​[​[​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​d​e​f​a​u​l​t​S​t​o​r​e​]​ ​a​l​l​A​s​s​e​t​T​y​p​e​s​]​;​
 ​ ​ ​ ​N​S​M​a​n​a​g​e​d​O​b​j​e​c​t​ ​*​a​s​s​e​t​T​y​p​e​ ​=​ ​[​a​l​l​A​s​s​e​t​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​[​i​p​ ​r​o​w​]​]​;​

 ​ ​ ​ ​/​/​ ​U​s​e​ ​k​e​y​-​v​a​l​u​e​ ​c​o​d​i​n​g​ ​t​o​ ​g​e​t​ ​t​h​e​ ​a​s​s​e​t​ ​t​y​p​e​'​s​ ​l​a​b​e​l​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​a​s​s​e​t​L​a​b​e​l​ ​=​ ​[​a​s​s​e​t​T​y​p​e​ ​v​a​l​u​e​F​o​r​K​e​y​:​@​"​l​a​b​e​l​"​]​;​
 ​ ​ ​ ​[​[​c​e​l​l​ ​t​e​x​t​L​a​b​e​l​]​ ​s​e​t​T​e​x​t​:​a​s​s​e​t​L​a​b​e​l​]​;​

 ​ ​ ​ ​/​/​ ​C​h​e​c​k​m​a​r​k​ ​t​h​e​ ​o​n​e​ ​t​h​a​t​ ​i​s​ ​c​u​r​r​e​n​t​l​y​ ​s​e​l​e​c​t​e​d​
 ​ ​ ​ ​i​f​ ​(​a​s​s​e​t​T​y​p​e​ ​=​=​ ​[​p​o​s​s​e​s​s​i​o​n​ ​a​s​s​e​t​T​y​p​e​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​c​e​l​l​ ​s​e​t​A​c​c​e​s​s​o​r​y​T​y​p​e​:​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​A​c​c​e​s​s​o​r​y​C​h​e​c​k​m​a​r​k​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​[​c​e​l​l​ ​s​e​t​A​c​c​e​s​s​o​r​y​T​y​p​e​:​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​A​c​c​e​s​s​o​r​y​N​o​n​e​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​c​e​l​l​;​
}​

-​ ​(​v​o​i​d​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​t​a​b​l​e​V​i​e​w​
d​i​d​S​e​l​e​c​t​R​o​w​A​t​I​n​d​e​x​P​a​t​h​:​(​N​S​I​n​d​e​x​P​a​t​h​ ​*​)​i​p​
{​
 ​ ​ ​ ​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​ ​*​c​e​l​l​ ​=​ ​[​t​a​b​l​e​V​i​e​w​ ​c​e​l​l​F​o​r​R​o​w​A​t​I​n​d​e​x​P​a​t​h​:​i​p​]​;​

 ​ ​ ​ ​[​c​e​l​l​ ​s​e​t​A​c​c​e​s​s​o​r​y​T​y​p​e​:​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​A​c​c​e​s​s​o​r​y​C​h​e​c​k​m​a​r​k​]​;​

 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​a​l​l​A​s​s​e​t​s​ ​=​ ​[​[​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​d​e​f​a​u​l​t​S​t​o​r​e​]​ ​a​l​l​A​s​s​e​t​T​y​p​e​s​]​;​
 ​ ​ ​ ​N​S​M​a​n​a​g​e​d​O​b​j​e​c​t​ ​*​a​s​s​e​t​T​y​p​e​ ​=​ ​[​a​l​l​A​s​s​e​t​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​[​i​p​ ​r​o​w​]​]​;​
 ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​s​e​t​A​s​s​e​t​T​y​p​e​:​a​s​s​e​t​T​y​p​e​]​;​

 ​ ​ ​ ​[​[​s​e​l​f​ ​n​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​]​ ​p​o​p​V​i​e​w​C​o​n​t​r​o​l​l​e​r​A​n​i​m​a​t​e​d​:​Y​E​S​]​;​
}​

@​e​n​d​

In ItemDetailViewController.xib, add a new UIButton to the view. Create and connect outlets as shown in Figure 17.14.

Figure 17.14  Add a UIButton

Add a UIButton

At the top of ItemDetailViewController.m, import the header for this new table view controller.

#​i​m​p​o​r​t​ ​"​I​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​.​h​"​

#​i​m​p​o​r​t​ ​"​A​s​s​e​t​T​y​p​e​P​i​c​k​e​r​.​h​"​

Implement showAssetTypePicker: in ItemDetailViewController.m. (If you generated the action method by Control-dragging from the XIB to the source file, the method will already be entered with an empty body.)

-​ ​(​I​B​A​c​t​i​o​n​)​s​h​o​w​A​s​s​e​t​T​y​p​e​P​i​c​k​e​r​:​(​i​d​)​s​e​n​d​e​r​
{​
 ​ ​ ​ ​[​[​s​e​l​f​ ​v​i​e​w​]​ ​e​n​d​E​d​i​t​i​n​g​:​Y​E​S​]​;​

 ​ ​ ​ ​A​s​s​e​t​T​y​p​e​P​i​c​k​e​r​ ​*​a​s​s​e​t​T​y​p​e​P​i​c​k​e​r​ ​=​ ​[​[​[​A​s​s​e​t​T​y​p​e​P​i​c​k​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​a​s​s​e​t​T​y​p​e​P​i​c​k​e​r​ ​s​e​t​P​o​s​s​e​s​s​i​o​n​:​p​o​s​s​e​s​s​i​o​n​]​;​

 ​ ​ ​ ​[​[​s​e​l​f​ ​n​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​]​ ​p​u​s​h​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​a​s​s​e​t​T​y​p​e​P​i​c​k​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​n​i​m​a​t​e​d​:​Y​E​S​]​;​
}​

And finally, update the title of the button to show the asset type of a Possession. Add the following code to viewWillAppear: in ItemDetailViewController.m.

 ​ ​ ​ ​i​f​ ​(​i​m​a​g​e​K​e​y​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​G​e​t​ ​i​m​a​g​e​ ​f​o​r​ ​i​m​a​g​e​ ​k​e​y​ ​f​r​o​m​ ​i​m​a​g​e​ ​c​a​c​h​e​
 ​ ​ ​ ​ ​ ​ ​ ​U​I​I​m​a​g​e​ ​*​i​m​a​g​e​T​o​D​i​s​p​l​a​y​ ​=​ ​[​[​I​m​a​g​e​S​t​o​r​e​ ​d​e​f​a​u​l​t​I​m​a​g​e​S​t​o​r​e​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​m​a​g​e​F​o​r​K​e​y​:​i​m​a​g​e​K​e​y​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​U​s​e​ ​t​h​a​t​ ​i​m​g​e​ ​t​o​ ​p​u​t​ ​o​n​ ​t​h​e​ ​s​c​r​e​e​n​ ​i​n​ ​i​m​a​g​e​V​i​e​w​
 ​ ​ ​ ​ ​ ​ ​ ​[​i​m​a​g​e​V​i​e​w​ ​s​e​t​I​m​a​g​e​:​i​m​a​g​e​T​o​D​i​s​p​l​a​y​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​c​l​e​a​r​ ​t​h​e​ ​i​m​a​g​e​V​i​e​w​
 ​ ​ ​ ​ ​ ​ ​ ​[​i​m​a​g​e​V​i​e​w​ ​s​e​t​I​m​a​g​e​:​n​i​l​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​t​y​p​e​L​a​b​e​l​ ​=​ ​[​[​p​o​s​s​e​s​s​i​o​n​ ​a​s​s​e​t​T​y​p​e​]​ ​v​a​l​u​e​F​o​r​K​e​y​:​@​"​l​a​b​e​l​"​]​;​
 ​ ​ ​ ​i​f​(​!​t​y​p​e​L​a​b​e​l​)​
 ​ ​ ​ ​ ​ ​ ​ ​t​y​p​e​L​a​b​e​l​ ​=​ ​@​"​N​o​n​e​"​;​

 ​ ​ ​ ​[​a​s​s​e​t​T​y​p​e​B​u​t​t​o​n​ ​s​e​t​T​i​t​l​e​:​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​@​"​T​y​p​e​:​ ​%​@​"​,​ ​t​y​p​e​L​a​b​e​l​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​S​t​a​t​e​:​U​I​C​o​n​t​r​o​l​S​t​a​t​e​N​o​r​m​a​l​]​;​
}​

Build and run the application. Select a Possession and set its asset type.

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

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