Saving and Loading


#1

I’m a little stuck on this.

Another user is creating a view controller and moving code around, and, I suppose if that’s the way to go, then that’s the solution, but I’ve been exploring and I thought that if I could get a pointer back to the app delegate, and then setting a pointer inside the app delegate, by adding (with the app delegate declared as conforming to TouchDrawView and an instance of TouchDrawView declared in the app delegate’s .h file)

TouchTrackerAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.touchDrawView = self; // or set up the accessors 

to -(id)initWithCoder: at the beginning of TouchDrawView.m.

I think I’m on the right track with that, but that line keeps returning NIL as initWithCoder is run before the appDelegate is instantiated, and unless the instance variable touchDrawView in the appDelegate points to the instance of TouchDrawView that’s created on launch, then I can’t see a way to save/load the files.

Help?


#2

I know that it’s possible to get a pointer to your app delegate thru that approach, but you never should. There are a million design problems that will arise if you choose that route.

Why not give your app delegate an outlet to point at the TouchDrawView and have it give the TouchDrawView Lines loaded in from a file. When the app will terminate, then get the lines back from the TouchDrawView and save those.

Also, don’t encode the Lines as part of the TouchDrawView - the TouchDrawView is a view and the Lines are model objects. They are separate.


#3

Thanks for the help, Joe. I spent some time working on it today and I ended up moving away from that direction anyway.

I’ve got to the point where I am confident that the lines are saved properly.

I’ve imported the filehelpers.h/.m files and added them to the prefix as in the other programs we’ve worked on, so I won’t include that code here.

(Here’s Line.h/m):


//
//  Line.h
//  TouchTracker

#import <Foundation/Foundation.h>

@interface Line : NSObject <NSCoding> {
	CGPoint begin;
	CGPoint end;
}

@property (nonatomic) CGPoint begin;

@property (nonatomic) CGPoint end;
@end

//
//  Line.m
//  TouchTracker

#import "Line.h"

@implementation Line

-(id)initWithCoder:(NSCoder *)decoder
{
	[super init];
	
	begin.x = [decoder decodeFloatForKey:@"Begin.x"];
	begin.y = [decoder decodeFloatForKey:@"Begin.y"];
	end.x = [decoder decodeFloatForKey:@"End.x"];
	end.y = [decoder decodeFloatForKey:@"End.y"];
	return self;
}

-(void)encodeWithCoder:(NSCoder *)encoder
{
	[encoder encodeFloat:begin.x forKey:@"Begin.x"];
	[encoder encodeFloat:begin.y forKey:@"Begin.y"];
	[encoder encodeFloat:end.x forKey:@"End.x"];
	[encoder encodeFloat:end.y forKey:@"End.y"];
}

@synthesize begin, end;
@end

As you can see, I broke it down to the absolute basics there. It’s likely there’s a better, more robust way, but this works.

Below are the relevant parts of TouchTrackerAppDelegate.h/.m

//
//  TouchTrackerAppDelegate.h
//  TouchTracker

#import <UIKit/UIKit.h>

@class TouchDrawView;

@interface TouchTrackerAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
	IBOutlet TouchDrawView *touchDrawView;
}

-(NSString *)touchTrackerArrayPath;

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) TouchDrawView *touchDrawView;
@end

//
//  TouchTrackerAppDelegate.m
//  TouchTracker

#import "TouchTrackerAppDelegate.h"
#import "TouchDrawView.h"

@implementation TouchTrackerAppDelegate

@synthesize window, touchDrawView;

#pragma mark -
#pragma mark Application lifecycle

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
	
    // Override point for customization after application launch.
	NSString *fileName = [self touchTrackerArrayPath];
	NSMutableArray *completeLines = [NSKeyedUnarchiver unarchiveObjectWithFile];
	if (!completeLines) 
		completeLines = [NSMutableArray array]; 
	touchDrawView = [[TouchDrawView alloc] init];
	[touchDrawView setCompleteLines:completeLines];
	
	[window addSubview:touchDrawView]; 
        [window makeKeyAndVisible];
	
	return YES;
}

- (void)applicationWillTerminate:(UIApplication *)application {

	NSString *fileName = [self touchTrackerArrayPath];
	NSMutableArray *completedLines = [touchDrawView completeLines];
	[NSKeyedArchiver archiveRootObject:completedLines toFile];
}

#pragma mark -
#pragma mark File Management
	
- (NSString *)touchTrackerArrayPath
{
	return pathInDocumentDirectory(@"Lines.data");
}

Here’s the relevant parts of TouchDrawView.h/.m

//
//  TouchDrawView.h
//  TouchTracker

#import <UIKit/UIKit.h>

#import "TouchTrackerAppDelegate.h"

@class NSObject;
@interface TouchDrawView : UIView // <NSCoding> 
{
	NSMutableDictionary *linesInProcess;
	NSMutableArray *completeLines;
}

- (NSString *)touchTrackerArrayPath;
- (void)clearAll;

@property (nonatomic, retain) NSMutableArray *completeLines; // Set up accessors.
@end

//
//  TouchDrawView.m
//  TouchTracker
//
//  Created by Steven Britton on 10-10-31.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "TouchDrawView.h"
#import "Line.h"

@implementation TouchDrawView
@synthesize completeLines;

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

	if (!(completeLines = [NSKeyedUnarchiver unarchiveObjectWithFile]))
		completeLines = [[NSMutableArray alloc] init]; */
	linesInProcess = [[NSMutableDictionary alloc] init];
	completeLines = [[NSMutableArray alloc] init]; 
	[self setMultipleTouchEnabled:YES];
	[self setNeedsDisplay];
	
	return self;
}

-(id)init {

	return self;
}

When the program launches, TouchDrawView runs initWithCoder first, which sets up the arrays and display parameters as written in the book. Then, TouchTrackerAppDelegate runs application:DidFinishLaunchingWithOptions:launchOptions,
which opens the data file, reads its contents, and then creates an instance of TouchDrawView, and sends the init message, (which simply returns the address of the instance back.) Then, it tells the completeLines ivar in touchDrawView to point to the completedLines array already opened up.

It then adds touchDrawView as a subview of window and sends the makeKeyAndVisible message to display the graphics.

I am confident everything works, inasmuch as the lines are saved properly, and retrieved properly (as in the data is there and not corrupted) however the problem I’m having is that while the program runs fine, it doesn’t display the loaded lines on the screen when re-launched. Also, the completedLines array is reset with new values when I add lines to the screen and terminate the application! (I’ve stepped through the methods for loading and saving, watching the ivars.)

So, while I’m closer than I was, I’m still missing something. (I even set up an outlet and dragged the lines across in interface builder :slight_smile: )

hmmm…


#4

SOLVED. Wow I was so close when I posted that last msg it’s funny!