0

I have an app that is storing Task objects (custom class) inside of a NSMutableArray. The Task class conforms to the NSCoding and NSCopying protocols, and I have also implemented the encodeWithCoder and initWithCoder methods.

When the user adds a new task to an NSMutableArray list, I save the array using NSKeyedArchiver. The list populates a UITableView.

The data is being saved, and when I exit the app and reenter, the data is still there. When I use another app for a while and come back, the data is still there. However, when I "kill" the app in the multitasking task manage or restart the device, the data disappears. Here are some important code snippets:

#define kFilename @"epapsTasksFile"

.

- (NSString *)dataFilePath {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    return [documentsDirectory stringByAppendingPathComponent:kFilename];
}

- (void)viewDidLoad {

    NSString *filePath = [self dataFilePath];
    if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
        self.list = [[NSKeyedUnarchiver unarchiveObjectWithFile:kFilename] retain];
    }
    else {
        self.list = [NSMutableArray arrayWithCapacity:1];
    }

    ...
}

- (void)applicationWillResignActive:(NSNotification *)notification {
    NSMutableArray *updatedList = [[NSMutableArray alloc] initWithArray:self.list];
    [NSKeyedArchiver archiveRootObject:updatedList toFile:kFilename];
}

Why is my app not saving the data when the app is "killed" or the device is restarted? Also, it may be interesting to note that when I restart the iPhone simulator, the data stays in place.

epaps
  • 516
  • 1
  • 8
  • 20

1 Answers1

1

You need to save the data
([NSKeyedArchiver archiveRootObject:updatedList toFile:kFilename]; in applicationWillTerminate delegate method as as well to save it on termination.

EDIT: applicationWillTerminate is not gauranteed in IOS4.0 and above.

Best is to check the return status of archiveRootObject:toFile: and see if the data is stored properly. As you figured it out, it can be case with wrong file path.

Tatvamasi
  • 2,537
  • 19
  • 14
  • This doesn't seem to be working. I added `- (void)applicationWillTerminate:(UIApplication *)application { ... }` - and it didn't work. The code inside the ... is the `NSKeyedArchiver` method. Is it okay to put this in the controller class or should it go in my `AppDelegate.m` class? – epaps Jun 13 '11 at 22:05
  • @epaps you are right, though the documentation says , this delegate method will be called, looks like it wont (in IOS4.0 and above). This http://stackoverflow.com/questions/4967745/recognize-application-multitask-kill-ipad suggests there is no way you can achieve this (save data while killing the app). Btw, cant you save the data up front (i am not aware of your app logic, but if you feel the data is critical , you should save it on frequent basis ?) – Tatvamasi Jun 13 '11 at 22:12
  • Yes, the data is being saved frequently. I can exit the app and everything is saved. But when I kill it or restart the device, the already saved data disappears. – epaps Jun 13 '11 at 22:15
  • @epaps how are you checking that data is not present? Are you killing the app when data is being saved? I just checked on device as well as simulator:Data once saved (to file) persists, even after killing the app or restart of device – Tatvamasi Jun 13 '11 at 22:35
  • The data is saved, I navigate around the app and around the phone, and check that the data is still in the app. I then kill the app and then reopen the app to where the data should appear, and it's not there. As I mentioned, it works when I do this in the simulator, but not on my device. – epaps Jun 13 '11 at 22:39
  • Are you checking if the data is archived properly (return value)? Also when you are un able to access the data , are you sure file is not present ? (try with iPhone explorer and see if the data is not really present). Was trying to understand if it is problem with file read or file save ... – Tatvamasi Jun 13 '11 at 22:50
  • How should I check if it's archived properly? – epaps Jun 13 '11 at 22:51
  • return value(BOOL) of `archiveRootObject: toFile :` – Tatvamasi Jun 13 '11 at 22:57
  • In the console it's saying `deny file-write-create` – epaps Jun 13 '11 at 22:58
  • are you calling `archiveRootObject: toFile :` from main thread ? – Tatvamasi Jun 13 '11 at 23:02
  • Not entirely sure what you mean by that. It's being called in two different classes. – epaps Jun 13 '11 at 23:05
  • btw, another line of thought, is the list (that you are trying to archive, serializable ?if yes, cant you serialize it ? – Tatvamasi Jun 13 '11 at 23:06
  • So I did this: `BOOL test = [NSKeyedArchiver archiveRootObject:updatedList toFile:kFilename]; NSLog(@"Did it save:%@",test ? @"Yes!" :@"Nope");` and it returned "Nope". Hmm. – epaps Jun 13 '11 at 23:09
  • When I run it on the simulator, it looks like it's saving the file directly on "Macintosh HD" and not in any sort of folder. – epaps Jun 13 '11 at 23:12
  • can you post the methods from where you are calling `NSKeyedArchiver archiveRootObject:updatedList toFile:kFilename` and how abt serialization ? – Tatvamasi Jun 13 '11 at 23:12
  • Sure, here: `- (void)applicationWillResignActive:(NSNotification *)notification { NSMutableArray *updatedList = [[NSMutableArray alloc] initWithArray:self.list]; BOOL test = [NSKeyedArchiver archiveRootObject:updatedList toFile:kFilename]; NSLog(@"Did it save? %@",test ? @"Yes!" :@"Nope"); }` The other one is after the save and is the same. I'd prefer to try to get it working this way over trying a different method. – epaps Jun 13 '11 at 23:17
  • i am actually interested in the other one, which method and how is it called ? – Tatvamasi Jun 13 '11 at 23:22
  • Turns out I was trying to save it to kFileName when I should have been saving it to filePath (`NSString *filePath = [self dataFilePath];`). Do you want to update your answer to reflect this? Thank you SO MUCH for your help. – epaps Jun 13 '11 at 23:29
  • One more thing, can you comment on if this is good practice? `self.list = [NSMutableArray arrayWithCapacity:1];` It's in the if/else statement in the question. – epaps Jun 13 '11 at 23:32
  • I would prefer instance method `initWithCapacity` over class method or retain / release a class method. – Tatvamasi Jun 13 '11 at 23:39
  • please check the updated answer ? written it generally ,not specifically for your question. Is that fine ? – Tatvamasi Jun 13 '11 at 23:41
  • also have a look at http://stackoverflow.com/questions/4288719/serialization-vs-archiving/6260854#6260854 for quick understanding on Serialization and Archiving. – Tatvamasi Jun 13 '11 at 23:45