Hi
I'm writing an iPhone app, and having problems trying to archive the settings. I am using a class (AppData) to hold settings (the example shows just one at present), and using the App Delegate to create an instance of AppData when the app loads, and store it when the app terminates. I am conforming (I think) to the NSCoding protocol
The app stores the file in the Documents directory OK, and checking the file structure seems to hold the data I would expect. However when the file is loaded in the unarchiver returns the error * Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '* -[NSKeyedUnarchiver decodeObjectForKey:]: unarchiver has finished; cannot decode anything more'
I have been wrestling with this one for a while now, so if anyone can see the problem I'd be very grateful.
Code:
App Delegate:
interface:
#import <UIKit/UIKit.h>
#import "AppData.h"
#import "Constants.h"
@interface iLeanAppDelegate : NSObject <UIApplicationDelegate> {
AppData *appData;
UIWindow *window;
UITabBarController *tabBarController;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
@property (nonatomic, retain) AppData *appData;
@end
implementation:
#import "iLeanAppDelegate.h"
@implementation iLeanAppDelegate
@synthesize window;
@synthesize tabBarController;
@synthesize appData;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
appData = [[AppData alloc] init];
if ([[NSFileManager defaultManager] fileExistsAtPath:[AppData dataFilePath]]) { //If previous settings have been found retrieve and initialise
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:[AppData dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
[unarchiver finishDecoding];
appData = [unarchiver decodeObjectForKey:kDataKey];
[unarchiver finishDecoding];
[unarchiver release];
[data release];
}
// Add the tab bar controller's current view as a subview of the window
[window addSubview:tabBarController.view];
}
-(void)applicationWillTerminate:(NSNotification *)notification {
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:appData forKey:kDataKey];
[archiver finishEncoding];
BOOL success = [data writeToFile:[AppData dataFilePath] atomically:YES];
if (success)
NSLog(@"OK");
else
NSLog(@"Problem here");
[archiver release];
[data release];
}
- (void)dealloc {
[tabBarController release];
[window release];
[appData release];
[super dealloc];
}
@end
AppData class:
Interface:
#import <Foundation/Foundation.h>
#import "Constants.h"
@interface AppData : NSObject <NSCoding, NSCopying> {
BOOL audioAlert; //YES = audio alerts are on, NO = audio alerts are off
}
+(NSString *)dataFilePath;
@property(nonatomic, assign) BOOL audioAlert;
@end
Implementation:
#import "AppData.h"
@implementation AppData
@synthesize audioAlert;
+(NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kSettingsFilename];
}
#pragma mark NSCoding
-(void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeBool:audioAlert forKey:kSettingsKey];
}
-(id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.audioAlert = [decoder decodeObjectForKey:kSettingsKey];
}
return self;
}
#pragma mark -
#pragma mark NSCopying
-(id)copyWithZone:(NSZone *)zone {
AppData *copy = [[[self class] allocWithZone: zone] init];
//Will do this once coding works
return copy;
}
@end
Finally constants.h
//This file contains all the application constants
#define kSettingsFilename @"archive" //Filename where all application settings are stored
#define kSettingsKey @"settingsKey" //Key name
#define kDataKey @"data"