Read iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides) Online
Authors: Aaron Hillegass,Joe Conway
Tags: #COM051370, #Big Nerd Ranch Guides, #iPhone / iPad Programming
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 16.11
.
Figure 16.11 BNRItemStore and NSManagedObjectContext
In
BNRItemStore.h
, import Core Data and add three instance variables.
In
BNRItemStore.m
, change the implementation of
itemArchivePath
to return a different path that Core Data will use to save data.
When the
BNRItemStore
is initialized, it needs to set up the
NSManagedObjectContext
and a
NSPersistentStoreCoordinator
. The persistent store coordinator needs to know two things:
“
What are all of my entities and their attribute and relationships?
”
and
“
Where am I saving and loading data from?
”
To answer these questions, we 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, we will create the instance of
NSManagedObjectContext
and specify that it use this persistent store coordinator to save and load objects.
In
BNRItemStore.m
, update
init
.
Before,
BNRItemStore
would write out the entire
NSMutableArray
of
BNRItem
s 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
.
Note that this method is already called when the application is moved to the background.
In this application, we will fetch all of the
BNRItem
s in
store.data
the first time we 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. We want to sort the returned
BNRItem
s by
orderingValue
in ascending order. In
BNRItemStore.h
, declare a new method.
In
BNRItemStore.m
, define
loadAllItems
to prepare and execute the fetch request and save the results into the
allItems
array.
In
BNRItemStore.m
, send this message to the
BNRItemStore
at the end of
init
.
You can build to check for syntax errors. You will see a warning that
allAssetTypes
hasn’t been implemented yet – that’s okay for now.
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:
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:
This handles 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.
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:
.
The last bit of functionality you need to replace for
BNRItem
is the ability to re-order
BNRItem
s in the
BNRItemStore
. Because Core Data will not handle ordering automatically, we 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, we would have to change the
orderingValue
’s of other
BNRItem
s. This is why we created
orderingValue
as a
double
. We can take the
orderingValue
s of the
BNRItem
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
BNRItem
s that surround it. In
BNRItemStore.m
, modify
moveItemAtIndex:toIndex:
to handle reordering items.
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.