iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides) (60 page)

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

BOOK: iOS Programming: The Big Nerd Ranch Guide, 3/e (Big Nerd Ranch Guides)
2.13Mb size Format: txt, pdf, ePub
 

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

 

Figure 16.7  Entities in Homepwner

 
 

Let’s add these relationships to our 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, check the box for
To-Many Relationship
(
Figure 16.8
).

 

Figure 16.8  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 16.9
).

 

Figure 16.9  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, 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 16.10
. 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 16.10  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 hit
Next
. When prompted to save, check the box for
Use scalar properties for primitive data types
.

 

Xcode
will generate two 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
.

 
#import
#import
@interface BNRItem : NSManagedObject
@property (nonatomic, strong) NSString * itemName;
@property (nonatomic, strong) NSString * serialNumber;
@property (nonatomic) int32_t valueInDollars;
@property (nonatomic) NSTimeInterval dateCreated;
@property (nonatomic, strong) NSString * imageKey;
@property (nonatomic, strong) NSData * thumbnailData;
@property UNKNOWN_TYPE UNKNOWN_TYPE thumbnail;
@property (nonatomic, strong) UIImage *thumbnail;
@property (nonatomic) double orderingValue;
@property (nonatomic, strong) NSManagedObject *assetType;
- (void)setThumbnailDataFromImage:(UIImage *)image;
@end

Notice that the types of a few properties have changed, like
valueInDollars
and
dateCreated
. The type
int32_t
is just like an
int
, so we don’t have to worry about that. However,
dateCreated
is now an
NSTimeInterval
instead of an
NSDate
.

 

NSDate
, as we know it, represents a date. You might expect for it to store the current year, month, day, hour, etc., but this would cause issues for other types of calendars. (We use the Gregorian calender; other nations may not.) Instead, an
NSDate
stores the number of seconds since a fixed point in time: January 1st, 2001 in Gregorian. Thus, it is perfectly reasonable to represent an
NSDate
as an
NSTimeInterval
, which is just a type definition for
double
.

 

However, this change in type causes an issue with the code in
Homepwner
where we treat this variable as an
NSDate
object. In
DetailViewController.m
, locate
viewWillAppear:
and substitute the following line of code:

 
[dateLabel setText:[dateFormatter stringFromDate:[item dateCreated]]];
// Convert time interval to NSDate
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:[item dateCreated]];
[dateLabel setText:[dateFormatter stringFromDate:date]];
 

Next, in
BNRItem.m
, copy the
setThumbnailFromImage:
method from your old
BNRItem.m
to the new one:

 
- (void)setThumbnailDataFromImage:(UIImage *)image
{
    CGSize origImageSize = [image size];
    CGRect newRect = CGRectMake(0, 0, 40, 40);
    float ratio = MAX(newRect.size.width / origImageSize.width,
                      newRect.size.height / origImageSize.height);
    UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0);
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:newRect
                                                    cornerRadius:5.0];
    [path addClip];
    CGRect projectRect;
    projectRect.size.width = ratio * origImageSize.width;
    projectRect.size.height = ratio * origImageSize.height;
    projectRect.origin.x = (newRect.size.width - projectRect.size.width) / 2.0;
    projectRect.origin.y = (newRect.size.height - projectRect.size.height) / 2.0;
    [image drawInRect:projectRect];
    UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
    [self setThumbnail:smallImage];
    NSData *data = UIImagePNGRepresentation(smallImage);
    [self setThumbnailData:data];
    UIGraphicsEndImageContext();
}
 

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, which you will meet in a moment. Thus, you do not implement
init
methods for
NSManagedObject
subclasses. Instead, to configure an object after it has been created, you override the method
awakeFromFetch
. Implement
awakeFromFetch
in
BNRItem.m
to set the thumbnail from the
thumbnailData
(which is saved).

 
- (void)awakeFromFetch
{
    [super awakeFromFetch];
    UIImage *tn = [UIImage imageWithData:[self thumbnailData]];
    [self setPrimitiveValue:tn forKey:@"thumbnail"];
}

This adds the extra behavior of
BNRItem
’s old implementation of
initWithCoder:
.

 

Of course, when you first launch an application, there are no saved
BNRItem
s or
BNRAssetType
s. 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
instance variable of a
BNRItem
. Implement
awakeFromInsert
in
BNRItem.m
.

 
- (void)awakeFromInsert
{
    [super awakeFromInsert];
    NSTimeInterval t = [[NSDate date] timeIntervalSinceReferenceDate];
    [self setDateCreated:t];
}

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

 

Other books

What I Did For a Duke by Julie Anne Long
Decoration Day by Vic Kerry
Payoff for the Banker by Frances and Richard Lockridge
Sins of the Father by Evelyn Glass
The Crooked Branch by Jeanine Cummins
Sweet Surprises by Shirlee McCoy