Moving Homepwner to Core Data

Your Homepwner application currently uses archiving to save and reload its data. For a moderately sized object model (say, fewer than 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 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 pieces of information about that thing. A table that represents people might have columns for the person’s last name, date of birth, and height. Every row in the table represents a single person.

Figure 23.1  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’s properties. Every row is an instance of that class. Thus, Core Data’s job is to move data to and from these two representations (Figure 23.1).

Core Data uses different terminology to describe these ideas: a table/class is called a entity, and the columns/properties are called attributes. A Core Data model file is the description of every entity along with its attributes in your application. In Homepwner, you are going to describe a BNRItem entity in a model file and give it attributes like itemName, serialNumber, and valueInDollars.

Open Homepwner.xcodeproj. From the File menu, create a new file. Select Core Data in the iOS section and create a new Data Model. Name it Homepwner.

Figure 23.2  Create the model file

Create the model file

This will create a Homepwner.xcdatamodeld file and add it to your project. Select this file from the project navigator, and the editor area will reveal the user interface for manipulating a Core Data model file.

Find the Add Entity button at the bottom left of the window and click it. A new Entity will appear in the list of entities in the lefthand table. Double-click this entity and change its name to BNRItem (Figure 23.3).

Figure 23.3  Create the BNRItem entity

Create the BNRItem entity

Now your BNRItem entity needs attributes. Remember that these will be the properties of the BNRItem class. The necessary attributes are listed below. For each attribute, click the + button in the Attributes section and edit the Attribute and Type values:

  • itemName is a String

  • serialNumber is a String

  • valueInDollars is an Integer 32

  • dateCreated is a Date

  • itemKey is a String

  • thumbnail is a Transformable (It is a UIImage, but that is not one of the possibilities. We will discuss Transformable shortly.)

There is one more attribute to add. In Homepwner, users can order items by changing their positions in the table view. Archiving items in an array naturally respects this order. However, relational tables do not order their rows. Instead, when you fetch a set of rows, you specify their order using one of the attributes (Fetch me all the BNREmployee objects ordered by lastName.).

To maintain the order of items, you need to create an attribute to record each item’s position in the table view. Then when you fetch items, you can ask for them to be ordered by this attribute. (You will also need to update that attribute when the items are reordered.) Create this final attribute: name it orderingValue and make it a Double.

Core Data is only able to store certain data types in its store, and UIImage is not one of these types. Instead, you declared the thumbnail as transformable. With a transformable attribute, Core Data will convert the object into NSData when saving, and convert the NSData back into the original object when loading it from the file system. In order for Core Data to do this, you have to supply it with an NSValueTransformer subclass that handles these conversions.

Create a new class named BNRImageTransformer that is a subclass of NSValueTransformer. Open BNRImageTransformer.m and override the methods necessary for transforming the UIImage to and from NSData.

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​B​N​R​I​m​a​g​e​T​r​a​n​s​f​o​r​m​e​r​

+​ ​(​C​l​a​s​s​)​t​r​a​n​s​f​o​r​m​e​d​V​a​l​u​e​C​l​a​s​s​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​N​S​D​a​t​a​ ​c​l​a​s​s​]​;​
}​

-​ ​(​i​d​)​t​r​a​n​s​f​o​r​m​e​d​V​a​l​u​e​:​(​i​d​)​v​a​l​u​e​
{​
 ​ ​ ​ ​i​f​ ​(​!​v​a​l​u​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​n​i​l​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​i​f​ ​(​[​v​a​l​u​e​ ​i​s​K​i​n​d​O​f​C​l​a​s​s​:​[​N​S​D​a​t​a​ ​c​l​a​s​s​]​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​v​a​l​u​e​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​U​I​I​m​a​g​e​P​N​G​R​e​p​r​e​s​e​n​t​a​t​i​o​n​(​v​a​l​u​e​)​;​
}​

-​ ​(​i​d​)​r​e​v​e​r​s​e​T​r​a​n​s​f​o​r​m​e​d​V​a​l​u​e​:​(​i​d​)​v​a​l​u​e​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​U​I​I​m​a​g​e​ ​i​m​a​g​e​W​i​t​h​D​a​t​a​:​v​a​l​u​e​]​;​
}​

@​e​n​d​

The implementation of BNRImageTransformer is pretty straightforward. The class method transformedValueClass tells the transformer what type of object it will receive from the transformedValue: method. The transformedValue: method will be called when your transformable variable is to be saved to the file system, and it expects an object that can be saved to Core Data. In this example, the argument to the method will be a UIImage and it will return an instance of NSData. Finally, the reverseTransformedValue: method is called when the thumbnail data is loaded from the file system, and your implementation will create the UIImage from the NSData that was stored. With this file created, Core Data must know to use this class when working with the thumbnail.

Open Homepwner.xcdatamodeld and select the BNRItem entity. Select thumbnail from the Attributes list and then click the Create the BNRItem entity tab in the inspector selector to show the data model inspector. Replace the Value Transformer Name placeholder text in the second Name field with BNRImageTransformer (Figure 23.4).

Figure 23.4  Update value transformer name for thumbnail attribute

Update value transformer name for thumbnail attribute

At this point, your model file is sufficient to save and load items. However, one of the benefits to using Core Data is that entities can be related to one another, so you are going to add a new entity called BNRAssetType that describes a category of items. For example, a painting might be of the Art asset type. BNRAssetType will be an entity in the model file, and each row of that table will be mapped to an Objective-C object at runtime.

In Homepwner.xcdatamodeld, add another entity called BNRAssetType. Give it an attribute called label of type String. This will be the name of the category the BNRAssetType represents.

Figure 23.5  Create the BNRAssetType entity

Create the BNRAssetType entity

Now you need to establish relationships between BNRAssetType and BNRItem. Relationships between entities are represented by pointers between objects. There are two kinds of relationships: to-one and to-many.

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. The BNRItem entity will have a to-one relationship to the BNRAssetType entity. Thus, a BNRItem instance will have a pointer to a BNRAssetType instance.

When an entity has a to-many relationship, each instance of that entity has a pointer to an NSSet. This set contains the instances of the entity that it has a relationship with. The BNRAssetType entity will have a to-many relationship to the BNRItem entity because many instances of BNRItem can have the same BNRAssetType. Thus, a BNRAssetType object will have a pointer to a set of all of the BNRItem objects that are its type of asset.

With these relationships set up, you can ask a BNRAssetType object for the set of BNRItem objects that fall into its category, and you can ask a BNRItem which BNRAssetType it falls under. Figure 23.6 diagrams the relationships between BNRAssetType and BNRItem.

Figure 23.6  Entities in Homepwner

Entities in Homepwner

Let’s add these relationships to the model file. Select the BNRAssetType entity and then click the + button in the Relationships section. Name this relationship items in the Relationship column. Then, select BNRItem from the Destination column. In the data model inspector, change the Type drop-down from To One to To Many (Figure 23.7).

Figure 23.7  Create the items relationship

Create the items relationship

Now go back to the BNRItem entity. Add a relationship named assetType and pick BNRAssetType as its destination. In the Inverse column, select items (Figure 23.8).

Figure 23.8  Create the assetType relationship

Create the assetType relationship

NSManagedObject and subclasses

When an object is fetched with Core Data, its class, by default, is NSManagedObject. NSManagedObject is a subclass of NSObject that knows how to cooperate with the rest of Core Data. An NSManagedObject works a bit like a dictionary: it holds a key-value pair for every property (attribute or relationship) in the entity.

An NSManagedObject is little more than a data container. If you need your model objects to do something in addition to holding data, you must subclass NSManagedObject. Then, in your model file, you specify that this entity is represented by instances of your subclass, not the standard NSManagedObject.

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

Figure 23.9  Changing the class of an entity

Changing the class of an entity

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

In Finder, drag both BNRItem.h and BNRItem.m to your desktop for safekeeping. Then, in Xcode, delete these two files from the project navigator. (They will appear in red after you have moved the files).

Now, open Homepwner.xcdatamodeld again and select the BNRItem entity. Then, select New File... from the New menu.

From the iOS section, select Core Data, choose the NSManagedObject subclass option, and click Next. The checkbox for the Homepwner data model should already be checked. If it is not checked, go ahead and check the box, and then click Next. On the following screen, make sure that the BNRItem is checked, and then click Next one last time. Finally, click Create to generate the NSManagedObject subclass files.

Xcode will generate new BNRItem.h and BNRItem.m files. Open BNRItem.h and see what Core Data has wrought. Change the type of the thumbnail property to UIImage and add the method declaration from the previous BNRItem. By default, Xcode generates the properties as objects, so your ints are now instances of NSNumber. Change orderingValue to be a double and valueInDollars to be an int.

#​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​;​

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​I​t​e​m​ ​:​ ​N​S​M​a​n​a​g​e​d​O​b​j​e​c​t​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​s​t​r​o​n​g​)​ ​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​,​ ​s​t​r​o​n​g​)​ ​N​S​S​t​r​i​n​g​ ​*​ ​i​t​e​m​K​e​y​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​s​t​r​o​n​g​)​ ​N​S​S​t​r​i​n​g​ ​*​ ​i​t​e​m​N​a​m​e​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​s​t​r​o​n​g​)​ ​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​)​ ​d​o​u​b​l​e​ ​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​,​ ​s​t​r​o​n​g​)​ ​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​,​ ​s​t​r​o​n​g​)​ ​i​d​ ​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​,​ ​s​t​r​o​n​g​)​ ​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​,​ ​s​t​r​o​n​g​)​ ​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​,​ ​s​t​r​o​n​g​)​ ​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​)​ ​i​n​t​ ​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​,​ ​s​t​r​o​n​g​)​ ​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​F​r​o​m​I​m​a​g​e​:​(​U​I​I​m​a​g​e​ ​*​)​i​m​a​g​e​;​

@​e​n​d​

(Xcode might have created the strong properties as retain. These represent the same thing; before ARC, strong properties were called retain, and not all parts of the tools have been updated to the new terminology.)

Copy the setThumbnailFromImage: method from your old BNRItem.m to the new one:

-​ ​(​v​o​i​d​)​s​e​t​T​h​u​m​b​n​a​i​l​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​ ​=​ ​C​G​R​e​c​t​M​a​k​e​(​0​,​ ​0​,​ ​4​0​,​ ​4​0​)​;​

 ​ ​ ​ ​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​)​;​

 ​ ​ ​ ​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​W​i​t​h​O​p​t​i​o​n​s​(​n​e​w​R​e​c​t​.​s​i​z​e​,​ ​N​O​,​ ​0​.​0​)​;​

 ​ ​ ​ ​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​]​;​

 ​ ​ ​ ​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​;​

 ​ ​ ​ ​[​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​]​;​

 ​ ​ ​ ​U​I​I​m​a​g​e​ ​*​s​m​a​l​l​I​m​a​g​e​ ​=​ ​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​.​t​h​u​m​b​n​a​i​l​ ​=​ ​s​m​a​l​l​I​m​a​g​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​(​)​;​
}​

Of course, when you first launch an application, there are no saved items or asset types. When the user creates a new BNRItem 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 and itemKey properties of a BNRItem. Implement awakeFromInsert in BNRItem.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​.​d​a​t​e​C​r​e​a​t​e​d​ ​=​ ​[​N​S​D​a​t​e​ ​d​a​t​e​]​;​

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​a​n​ ​N​S​U​U​I​D​ ​o​b​j​e​c​t​ ​-​ ​a​n​d​ ​g​e​t​ ​i​t​s​ ​s​t​r​i​n​g​ ​r​e​p​r​e​s​e​n​t​a​t​i​o​n​
 ​ ​ ​ ​N​S​U​U​I​D​ ​*​u​u​i​d​ ​=​ ​[​[​N​S​U​U​I​D​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​k​e​y​ ​=​ ​[​u​u​i​d​ ​U​U​I​D​S​t​r​i​n​g​]​;​
 ​ ​ ​ ​s​e​l​f​.​i​t​e​m​K​e​y​ ​=​ ​k​e​y​;​
}​

This adds the extra behavior of BNRItem’s old designated initializer. Build the application to check for syntax errors, but do not run it.

Updating BNRItemStore

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. In Homepwner, these objects will work with the BNRItemStore. These relationships are shown in Figure 23.10.

Figure 23.10  BNRItemStore and NSManagedObjectContext

BNRItemStore and NSManagedObjectContext

In BNRItemStore.m, import Core Data and add three properties to the class extension.

@​i​m​p​o​r​t​ ​C​o​r​e​D​a​t​a​;​

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​I​t​e​m​S​t​o​r​e​ ​(​)​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​)​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​p​r​i​v​a​t​e​I​t​e​m​s​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​s​t​r​o​n​g​)​ ​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​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​s​t​r​o​n​g​)​ ​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​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​s​t​r​o​n​g​)​ ​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​;​

Then change the implementation of itemArchivePath to return a different path that Core Data will use to save data.

-​ ​(​N​S​S​t​r​i​n​g​ ​*​)​i​t​e​m​A​r​c​h​i​v​e​P​a​t​h​
{​
 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​d​o​c​u​m​e​n​t​D​i​r​e​c​t​o​r​i​e​s​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​e​a​r​c​h​P​a​t​h​F​o​r​D​i​r​e​c​t​o​r​i​e​s​I​n​D​o​m​a​i​n​s​(​N​S​D​o​c​u​m​e​n​t​D​i​r​e​c​t​o​r​y​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​U​s​e​r​D​o​m​a​i​n​M​a​s​k​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​Y​E​S​)​;​

 ​ ​ ​ ​/​/​ ​G​e​t​ ​o​n​e​ ​a​n​d​ ​o​n​l​y​ ​d​o​c​u​m​e​n​t​ ​d​i​r​e​c​t​o​r​y​ ​f​r​o​m​ ​t​h​a​t​ ​l​i​s​t​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​d​o​c​u​m​e​n​t​D​i​r​e​c​t​o​r​y​ ​=​ ​[​d​o​c​u​m​e​n​t​D​i​r​e​c​t​o​r​i​e​s​ ​f​i​r​s​t​O​b​j​e​c​t​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​[​d​o​c​u​m​e​n​t​D​i​r​e​c​t​o​r​y​ ​s​t​r​i​n​g​B​y​A​p​p​e​n​d​i​n​g​P​a​t​h​C​o​m​p​o​n​e​n​t​:​@​"​i​t​e​m​s​.​a​r​c​h​i​v​e​"​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​[​d​o​c​u​m​e​n​t​D​i​r​e​c​t​o​r​y​ ​s​t​r​i​n​g​B​y​A​p​p​e​n​d​i​n​g​P​a​t​h​C​o​m​p​o​n​e​n​t​:​@​"​s​t​o​r​e​.​d​a​t​a​"​]​;​
}​

When the BNRItemStore is initialized, it needs to set up the NSManagedObjectContext and an 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? To answer these questions, you need to create an instance of NSManagedObjectModel to hold the entity information of 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.

In BNRItemStore.m, update initPrivate.

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​P​r​i​v​a​t​e​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​;​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​s​e​l​f​.​i​t​e​m​A​r​c​h​i​v​e​P​a​t​h​;​
 ​ ​ ​ ​ ​ ​ ​ ​_​p​r​i​v​a​t​e​I​t​e​m​s​ ​=​ ​[​N​S​K​e​y​e​d​U​n​a​r​c​h​i​v​e​r​ ​u​n​a​r​c​h​i​v​e​O​b​j​e​c​t​W​i​t​h​F​i​l​e​:​p​a​t​h​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​!​_​p​r​i​v​a​t​e​I​t​e​m​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​_​p​r​i​v​a​t​e​I​t​e​m​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​l​l​o​c​]​ ​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​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​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​ ​=​ ​s​e​l​f​.​i​t​e​m​A​r​c​h​i​v​e​P​a​t​h​;​
 ​ ​ ​ ​ ​ ​ ​ ​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​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​@​t​h​r​o​w​ ​[​N​S​E​x​c​e​p​t​i​o​n​ ​e​x​c​e​p​t​i​o​n​W​i​t​h​N​a​m​e​:​@​"​O​p​e​n​F​a​i​l​u​r​e​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​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​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​I​n​f​o​:​n​i​l​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​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​.​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​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

Before, BNRItemStore would write out the entire NSMutableArray of BNRItem objects 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. In BNRItemStore.m, change saveChanges.

-​ ​(​B​O​O​L​)​s​a​v​e​C​h​a​n​g​e​s​
{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​p​a​t​h​ ​=​ ​[​s​e​l​f​ ​i​t​e​m​A​r​c​h​i​v​e​P​a​t​h​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​N​S​K​e​y​e​d​A​r​c​h​i​v​e​r​ ​a​r​c​h​i​v​e​R​o​o​t​O​b​j​e​c​t​:​a​l​l​I​t​e​m​s​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​o​F​i​l​e​:​[​s​e​l​f​ ​i​t​e​m​A​r​c​h​i​v​e​P​a​t​h​]​]​;​

 ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​;​
 ​ ​ ​ ​B​O​O​L​ ​s​u​c​c​e​s​s​f​u​l​ ​=​ ​[​s​e​l​f​.​c​o​n​t​e​x​t​ ​s​a​v​e​:​&​e​r​r​o​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​o​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​;​
}​

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

NSFetchRequest and NSPredicate

In this application, you will fetch all of the items in store.data the first time you need them. 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. To fetch BNRItem instances, you specify the BNRItem entity. You can also set the request’s sort descriptors to 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 want to sort the returned instances of BNRItem by orderingValue in ascending order.

In BNRItemStore.m, define a new method, loadAllItems, to prepare and execute the fetch request and save the results into the allItems array.

-​ ​(​v​o​i​d​)​l​o​a​d​A​l​l​I​t​e​m​s​
{​
 ​ ​ ​ ​i​f​ ​(​!​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​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​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​E​n​t​i​t​y​D​e​s​c​r​i​p​t​i​o​n​ ​*​e​ ​=​ ​[​N​S​E​n​t​i​t​y​D​e​s​c​r​i​p​t​i​o​n​ ​e​n​t​i​t​y​F​o​r​N​a​m​e​:​@​"​B​N​R​I​t​e​m​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​M​a​n​a​g​e​d​O​b​j​e​c​t​C​o​n​t​e​x​t​:​s​e​l​f​.​c​o​n​t​e​x​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​q​u​e​s​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​o​r​t​D​e​s​c​r​i​p​t​o​r​s​ ​=​ ​@​[​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​ ​=​ ​[​s​e​l​f​.​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​]​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​

 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​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​]​;​
 ​ ​ ​ ​}​
}​

Also in BNRItemStore.m, send this message to the BNRItemStore at the end of initPrivate.

 ​ ​ ​ ​ ​ ​ ​ ​_​c​o​n​t​e​x​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​;​

 ​ ​ ​ ​ ​ ​ ​ ​[​s​e​l​f​ ​l​o​a​d​A​l​l​I​t​e​m​s​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

You can build to check for syntax errors.

In this application, you immediately fetched all the instances of the BNRItem entity. This is a simple request. In an application with a much larger data set, you would carefully fetch just the instances you needed. To selectively fetch instances, you add a predicate (an NSPredicate) to your fetch request, and only the objects that satisfy the predicate are returned.

A predicate contains a condition that can be true or false. For example, if you only wanted the items 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 allItems 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​I​t​e​m​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​]​;​

Adding and deleting items

Thus far, you have taken care of saving and loading, but what about adding and deleting? When the user wants to create a new BNRItem, you will not allocate and initialize this new BNRItem. Instead, you will ask the NSManagedObjectContext to insert a new object from the BNRItem entity. It will then return an instance of BNRItem.

In BNRItemStore.m, edit the createItem method.

-​ ​(​B​N​R​I​t​e​m​ ​*​)​c​r​e​a​t​e​I​t​e​m​
{​
 ​ ​ ​ ​B​N​R​I​t​e​m​ ​*​i​t​e​m​ ​=​ ​[​[​B​N​R​I​t​e​m​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​d​o​u​b​l​e​ ​o​r​d​e​r​;​
 ​ ​ ​ ​i​f​ ​(​[​s​e​l​f​.​a​l​l​I​t​e​m​s​ ​c​o​u​n​t​]​ ​=​=​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​o​r​d​e​r​ ​=​ ​1​.​0​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​o​r​d​e​r​ ​=​ ​[​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​ ​l​a​s​t​O​b​j​e​c​t​]​ ​o​r​d​e​r​i​n​g​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​"​,​ ​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​ ​c​o​u​n​t​]​,​ ​o​r​d​e​r​)​;​

 ​ ​ ​ ​B​N​R​I​t​e​m​ ​*​i​t​e​m​ ​=​ ​[​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​:​@​"​B​N​R​I​t​e​m​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​M​a​n​a​g​e​d​O​b​j​e​c​t​C​o​n​t​e​x​t​:​s​e​l​f​.​c​o​n​t​e​x​t​]​;​

 ​ ​ ​ ​i​t​e​m​.​o​r​d​e​r​i​n​g​V​a​l​u​e​ ​=​ ​o​r​d​e​r​;​

 ​ ​ ​ ​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​ ​a​d​d​O​b​j​e​c​t​:​i​t​e​m​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​i​t​e​m​;​
}​

When a user deletes a BNRItem, you must inform the context so that it is removed from the database. In BNRItemStore.m, add the following code to removeItem:.

-​ ​(​v​o​i​d​)​r​e​m​o​v​e​I​t​e​m​:​(​B​N​R​I​t​e​m​ ​*​)​i​t​e​m​
{​
 ​ ​ ​ ​N​S​S​t​r​i​n​g​ ​*​k​e​y​ ​=​ ​i​t​e​m​.​i​t​e​m​K​e​y​;​

 ​ ​ ​ ​[​[​B​N​R​I​m​a​g​e​S​t​o​r​e​ ​s​h​a​r​e​d​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​]​;​

 ​ ​ ​ ​[​s​e​l​f​.​c​o​n​t​e​x​t​ ​d​e​l​e​t​e​O​b​j​e​c​t​:​i​t​e​m​]​;​
 ​ ​ ​ ​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​ ​r​e​m​o​v​e​O​b​j​e​c​t​I​d​e​n​t​i​c​a​l​T​o​:​i​t​e​m​]​;​
}​

Reordering items

The last bit of functionality you need to replace for BNRItem is the ability to re-order items in the BNRItemStore. Because Core Data will not handle ordering automatically, you must update a BNRItem’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 BNRItem was placed in a new index, you would have to change the orderingValue’s of other items. This is why you created orderingValue as a double. You can take the orderingValues of the items that will be before and after the moving item, add them together, and divide by two. The new orderingValue will fall directly in between the values of the items that surround it.

In BNRItemStore.m, modify moveItemAtIndex:toIndex: to handle reordering items.

-​ ​(​v​o​i​d​)​m​o​v​e​I​t​e​m​A​t​I​n​d​e​x​:​(​N​S​U​I​n​t​e​g​e​r​)​f​r​o​m​I​n​d​e​x​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​o​I​n​d​e​x​:​(​N​S​U​I​n​t​e​g​e​r​)​t​o​I​n​d​e​x​
{​
 ​ ​ ​ ​i​f​ ​(​f​r​o​m​I​n​d​e​x​ ​=​=​ ​t​o​I​n​d​e​x​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​B​N​R​I​t​e​m​ ​*​i​t​e​m​ ​=​ ​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​[​f​r​o​m​I​n​d​e​x​]​;​

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

 ​ ​ ​ ​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​ ​i​n​s​e​r​t​O​b​j​e​c​t​:​i​t​e​m​ ​a​t​I​n​d​e​x​:​t​o​I​n​d​e​x​]​;​

 ​ ​ ​ ​/​/​ ​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​I​n​d​e​x​ ​>​ ​0​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​l​o​w​e​r​B​o​u​n​d​ ​=​ ​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​[​(​t​o​I​n​d​e​x​ ​-​ ​1​)​]​ ​o​r​d​e​r​i​n​g​V​a​l​u​e​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​l​o​w​e​r​B​o​u​n​d​ ​=​ ​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​[​1​]​ ​o​r​d​e​r​i​n​g​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​I​n​d​e​x​ ​<​ ​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​ ​c​o​u​n​t​]​ ​-​ ​1​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​u​p​p​e​r​B​o​u​n​d​ ​=​ ​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​[​(​t​o​I​n​d​e​x​ ​+​ ​1​)​]​ ​o​r​d​e​r​i​n​g​V​a​l​u​e​]​;​
 ​ ​ ​ ​}​ ​e​l​s​e​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​u​p​p​e​r​B​o​u​n​d​ ​=​ ​[​s​e​l​f​.​p​r​i​v​a​t​e​I​t​e​m​s​[​(​t​o​I​n​d​e​x​ ​-​ ​1​)​]​ ​o​r​d​e​r​i​n​g​V​a​l​u​e​]​ ​+​ ​2​.​0​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​d​o​u​b​l​e​ ​n​e​w​O​r​d​e​r​V​a​l​u​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​ ​%​f​"​,​ ​n​e​w​O​r​d​e​r​V​a​l​u​e​)​;​
 ​ ​ ​ ​i​t​e​m​.​o​r​d​e​r​i​n​g​V​a​l​u​e​ ​=​ ​n​e​w​O​r​d​e​r​V​a​l​u​e​;​

}​

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.

Adding BNRAssetTypes to Homepwner

In the model file, you described a new entity, BNRAssetType, that every item will have a to-one relationship to. You need a way for the user to set the BNRAssetType of a BNRItem. Also, the BNRItemStore will need a way to fetch the asset types. (Creating new instances of BNRAssetType is left as a challenge at the end of this chapter.)

In BNRItemStore.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 BNRItemStore.m, define this method. If this is the first time the application is being run – and therefore there are no BNRAssetType objects 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​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​E​n​t​i​t​y​D​e​s​c​r​i​p​t​i​o​n​ ​*​e​ ​=​ ​[​N​S​E​n​t​i​t​y​D​e​s​c​r​i​p​t​i​o​n​ ​e​n​t​i​t​y​F​o​r​N​a​m​e​:​@​"​B​N​R​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​:​s​e​l​f​.​c​o​n​t​e​x​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​q​u​e​s​t​.​e​n​t​i​t​y​ ​=​ ​e​;​

 ​ ​ ​ ​ ​ ​ ​ ​N​S​E​r​r​o​r​ ​*​e​r​r​o​r​ ​=​ ​n​i​l​;​
 ​ ​ ​ ​ ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​r​e​s​u​l​t​ ​=​ ​[​s​e​l​f​.​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​:​@​"​B​N​R​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​:​s​e​l​f​.​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​:​@​"​B​N​R​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​:​s​e​l​f​.​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​:​@​"​B​N​R​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​:​s​e​l​f​.​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 to change the user interface so that the user can see and change the BNRAssetType of the BNRItem in the BNRDetailViewController.

Figure 23.11  Interface for BNRAssetType

Interface for BNRAssetType

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

In BNRAssetTypeViewController.h, forward declare BNRItem, change the superclass to UITableViewController, and give it a BNRItem property.

#​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​>​

@​c​l​a​s​s​ ​B​N​R​I​t​e​m​;​

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​A​s​s​e​t​T​y​p​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​:​ ​N​S​O​b​j​e​c​t​
@​i​n​t​e​r​f​a​c​e​ ​B​N​R​A​s​s​e​t​T​y​p​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​:​ ​U​I​T​a​b​l​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​s​t​r​o​n​g​)​ ​B​N​R​I​t​e​m​ ​*​i​t​e​m​;​

@​e​n​d​

This table view controller will show a list of the available asset types. Tapping a button on the BNRDetailViewController’s view will display it. Implement the data source methods and import the appropriate header files in BNRAssetTypeViewController.m. (You have seen all this before.)

#​i​m​p​o​r​t​ ​"​B​N​R​A​s​s​e​t​T​y​p​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​.​h​"​

#​i​m​p​o​r​t​ ​"​B​N​R​I​t​e​m​S​t​o​r​e​.​h​"​
#​i​m​p​o​r​t​ ​"​B​N​R​I​t​e​m​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​B​N​R​A​s​s​e​t​T​y​p​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​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​P​l​a​i​n​]​;​
}​

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​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​)​v​i​e​w​D​i​d​L​o​a​d​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​v​i​e​w​D​i​d​L​o​a​d​]​;​

 ​ ​ ​ ​[​s​e​l​f​.​t​a​b​l​e​V​i​e​w​ ​r​e​g​i​s​t​e​r​C​l​a​s​s​:​[​U​I​T​a​b​l​e​V​i​e​w​C​e​l​l​ ​c​l​a​s​s​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​C​e​l​l​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​"​]​;​
}​

-​ ​(​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​ ​[​[​[​B​N​R​I​t​e​m​S​t​o​r​e​ ​s​h​a​r​e​d​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​n​d​e​x​P​a​t​h​
{​
 ​ ​ ​ ​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​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​o​r​I​n​d​e​x​P​a​t​h​:​i​n​d​e​x​P​a​t​h​]​;​

 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​a​l​l​A​s​s​e​t​s​ ​=​ ​[​[​B​N​R​I​t​e​m​S​t​o​r​e​ ​s​h​a​r​e​d​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​[​i​n​d​e​x​P​a​t​h​.​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​.​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​ ​=​=​ ​s​e​l​f​.​i​t​e​m​.​a​s​s​e​t​T​y​p​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​c​e​l​l​.​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​.​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​n​d​e​x​P​a​t​h​
{​
 ​ ​ ​ ​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​n​d​e​x​P​a​t​h​]​;​

 ​ ​ ​ ​c​e​l​l​.​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​ ​=​ ​[​[​B​N​R​I​t​e​m​S​t​o​r​e​ ​s​h​a​r​e​d​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​[​i​n​d​e​x​P​a​t​h​.​r​o​w​]​;​
 ​ ​ ​ ​s​e​l​f​.​i​t​e​m​.​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 BNRDetailViewController.xib, drag a UIBarButtonItem onto the toolbar. Create an outlet to this button by selecting the toolbar then the new bar button and Control-dragging to the class extension of BNRDetailViewController.m. Name this outlet assetTypeButton. Then, create an action from this button in the same way by dragging into the @implementation section instead of the class extension and name it showAssetTypePicker.

The following method and instance variable should now be declared in BNRDetailViewController.m:

@​p​r​o​p​e​r​t​y​ ​(​w​e​a​k​,​ ​n​o​n​a​t​o​m​i​c​)​ ​I​B​O​u​t​l​e​t​ ​U​I​B​a​r​B​u​t​t​o​n​I​t​e​m​ ​*​a​s​s​e​t​T​y​p​e​B​u​t​t​o​n​;​

@​e​n​d​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​B​N​R​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

/​/​ ​O​t​h​e​r​ ​m​e​t​h​o​d​s​ ​h​e​r​e​

-​ ​(​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​
{​

}​

@​e​n​d​

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

#​i​m​p​o​r​t​ ​"​B​N​R​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​ ​"​B​N​R​A​s​s​e​t​T​y​p​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​.​h​"​

Finish implementing showAssetTypePicker: in BNRDetailViewController.m.

-​ ​(​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​]​;​

 ​ ​ ​ ​B​N​R​A​s​s​e​t​T​y​p​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​*​a​v​c​ ​=​ ​[​[​B​N​R​A​s​s​e​t​T​y​p​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​a​v​c​.​i​t​e​m​ ​=​ ​s​e​l​f​.​i​t​e​m​;​

 ​ ​ ​ ​[​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​v​c​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​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 BNRItem. In BNRDetailViewController.m, add the following code to viewWillAppear:.

 ​ ​ ​ ​i​f​ ​(​s​e​l​f​.​i​t​e​m​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​ ​=​ ​[​[​B​N​R​I​m​a​g​e​S​t​o​r​e​ ​s​h​a​r​e​d​S​t​o​r​e​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​m​a​g​e​F​o​r​K​e​y​:​s​e​l​f​.​i​t​e​m​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​
 ​ ​ ​ ​ ​ ​ ​ ​s​e​l​f​.​i​m​a​g​e​V​i​e​w​.​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​.​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​ ​=​ ​[​s​e​l​f​.​i​t​e​m​.​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​"​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​s​e​l​f​.​a​s​s​e​t​T​y​p​e​B​u​t​t​o​n​.​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​]​;​

 ​ ​ ​ ​[​s​e​l​f​ ​u​p​d​a​t​e​F​o​n​t​s​]​;​
}​

Build and run the application. Select a BNRItem 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