0

I'm trying to save for the first time. Made my application, and now going to save data and load on startup and close. Looked into NSKeyedArchiver and added the two methods from the NSCoding to all my custom classes:

-(void)encodeWithCoder:(NSCoder *)aCoder{
 // Encode Stuff
}

-(id)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super init])
        // Decode Stuff
    return self;
}

But now i'm finding a problem, where and how do i need to call my saveFile and loadFile? My data is stored in 2 arrays in my FirstViewController. And i wanted to save the data with the AppDelegate's - (void)applicationWillTerminate:(UIApplication *)application and load data with - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions But how is this possible to reach the FirstViewController class with the Arrays in it to store from the AppDelegate method's?

Is the thinking right, or do i need to do it on an other way?

Kind Regards

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Kets
  • 438
  • 2
  • 8
  • 24
  • Are you attempting to preserve the state of your application? Or simply archiving and unarchiving data? If you just want to save data, have you looked in to Core Data or would you rather save the data to the disk? – Jonathan Apr 27 '14 at 18:27
  • Just at closing and opening the app, that the data needs to be updates, from what the user added in the Tableview – Kets Apr 27 '14 at 18:30
  • If I understand correctly, you want to save the data that user entered in to a UITableView and display the most recent saved data whenever the user opens the app, right? Blatantly put, you want data to persist. – Jonathan Apr 27 '14 at 18:33
  • @Jonathan Yeah sort of, my classes handles all data quite well, so all data is stored nice in one/two arrays in my main TableView. So if the user adds data to the tableview, and the user closes the app, the data needs to be there again like he/she added it before. That's why i want to save on close and load on open. – Kets Apr 27 '14 at 19:24
  • Well, it sounds like @Abhi Beckert's answer will solve your problem. – Jonathan Apr 27 '14 at 19:36
  • Although, I do recommend at least looking in to Core Data for the sake of knowing what else is out there. – Jonathan Apr 27 '14 at 19:46

2 Answers2

2

Loading data should be "lazy". This means the data should be loaded the first instant that you actually need to read the data. Also, if it's a lot of data, you should be prepared to free it while your app is running so other apps can use the RAM, this means your app is more likely to still be running next time the user launches your app.

So, make a class that provides access to the data, and the first time anything needs data it checks if the internal NSCoding object is nil, and if it is then that is where you should load the data.

As for saving, you should save before terminating but more importantly you should also save within a second or so of any data being modified by the user. Your app should crash at any moment due to a software bug, or it could be terminated for some other reason, or the battery could simply run out.

Lets say your internal data storage is an NSMutableDictionary saved using NSKeyedArchiver. It has a value with the key @"value", with a "getter" and "setter" implemented like this:

- (NSString *)value
{
  if (!self.data)
    self.data = [NSKeyedUnarchiver unarchiveObjectWithFile:self.dataFile];

  return self.data[@"value"];
}

- (void)setValue:(NSString *)value
{
  if (!self.data)
    self.data = [NSKeyedUnarchiver unarchiveObjectWithFile:self.dataFile];

  self.data[@"value"] = value;
  self.needsSave = YES;

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [self save];
  });
}

- (void)save
{
  if (!self.needsSave)
    return;

  [NSKeyedArchiver archiveRootObject:self.data toFile:self.dataFile];
  self.needsSave = NO;
}

Finally, your class should also register for UIApplicationDidReceiveMemoryWarningNotification, UIApplicationWillResignActiveNotification, UIApplicationWillTerminateNotification, where you want to save to disk and free the RAM so other apps can use it:

- (id)init
{
  if (!(self = [super init]))
    return nil;

  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveAndFreeMemory:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveAndFreeMemory:) name:UIApplicationWillResignActiveNotification object:nil];
  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveAndFreeMemory:) name:UIApplicationWillTerminateNotification object:nil];

  return self;
}

- (void)saveAndFreeMemory:(NSNotification *)notif
{
  [self save];

  self.data = nil;
}
Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
0

When a UIApplication launches in the normal way, you can always call [[UIApplication sharedApplication] delegate] from any object in the app, and you will thus obtain a reference to the application's delegate.

This is how view controller code generally gets ahold of the app delegate, you establish the connection in the view controllers in their respective -awakeFromNib methods -- it's generally less gotcha-prone to have the view controllers make the first contact with the app delegate, than having the app delegate reach out to the view controllers.

Have your app delegate decode the saved data into a model object hierarchy, have your view controllers connect with the app delegate and begin KV observing the model in -awakeFromNib.

Or just use Core Data.

iluvcapra
  • 9,436
  • 2
  • 30
  • 32