Challenge: Saving and Loading - My Solution


#1

Here’s my crack at this Challenge. Would love to hear any feedback on where I could’ve done things better/smarter.

I started by modifying initWithCoder: in TouchDrawView.m to the following:

[code]- (id) initWithCoder:(NSCoder *)c
{
self = [super initWithCoder:c];

if (self) {
    linesInProcess = [[NSMutableDictionary alloc]init];
    
    if (!completeLines) {
        NSString *path = [self lineArchivePath];
        completeLines = [[NSKeyedUnarchiver unarchiveObjectWithFile:path]retain];
    }
    if (!completeLines){
        completeLines = [[NSMutableArray alloc]init];
    }
    [self setMultipleTouchEnabled:YES];
}

[self setTag:66];
return self;

}
[/code]

Wondering what’s up with the [self setTag:66] call?

I knew I needed the app delegate to initiate the save when applicationDidEnterBackground was called but I couldn’t figure out the best way of getting a reference to my TouchDrawView object from inside the app delegate. I found the setTag: method and decided to use it since it seemed simplest. Other options would have been to store a pointer to the TouchDrawView object as an ivar in the app delegate object but that seemed kludgey. I thought the app delegate had access to the root view and that I could find it that way, somehow, but I couldn’t find the details on how to make that work (if it’s even possible).

Inside my app delegate I modified applicationDidEnterBackground:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    TouchDrawView *myTouchDrawView = (TouchDrawView*)[self.window viewWithTag:66];
    [myTouchDrawView saveChanges];
}

I added a lineArchivePath method to TouchDrawView.m that simply borrows the code from Whereami to handle the path creation:

-(NSString *)lineArchivePath
{
    return pathInDocumentDirectory(@"lines.data");
}

The method above gets called from a saveChanges: method I added to TouchDrawView:

-(BOOL)saveChanges
{
    //returns success or failure
    return [NSKeyedArchiver archiveRootObject:completeLines toFile:[self lineArchivePath]];
}

Lastly I made Line conform to NSCoding protocol and implemented the two encode/decode methods from said protocol:

- (id)initWithCoder:(NSCoder *)decoder
{
    self = [super init];
    
    if(self){
        // Our CGPoints are archived as NSValues. We need to create CGPoints and somehow get the data out of NSValue objects and into CGPoints.

        NSValue *vBegin = [[NSValue alloc]init];
        NSValue *vEnd = [[NSValue alloc]init];
        
        vBegin = [decoder decodeObjectForKey:@"begin"];
        begin = [vBegin CGPointValue];
        
        vEnd = [decoder decodeObjectForKey:@"end"];
        end = [vEnd CGPointValue];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder
{
    NSValue *vBegin = [NSValue valueWithCGPoint:begin];
    NSValue *vEnd = [NSValue valueWithCGPoint:end];
    
    [encoder encodeObject:vBegin forKey:@"begin"];
    [encoder encodeObject:vEnd forKey:@"end"];
}

Again - any comments on things I missed or where/how I could optimize this are welcome and encouraged.

HTH,
mb