Im just working on what should be the "finishing touches" of my first iPhone game. For some reason, when I save with NSKeyedArchiver/Unarchiver, the data seems to load once and then gets lost or something.
I'm trying to save 2 objects with the same archiver: an NSMutableDictionary levelsPlist, and an NSMutableArray categoryLockStateArray. The are set as nonatomic, retain properties in the header file.
Here's what I've been able to deduce:
The object saved as the NSMutableDictionary always persists. It works just fine.
When I save in this viewController, pop to the previous one, and then push back into this one, the data is saved and prints as I want it to.
But when I save in this viewController, then push a new one and pop back into this one, the categoryLockState is lost.
Any idea why this might be happening? Do I have this set up all wrong? I copied it from a book months ago. Here's the methods I use to save and load.
- (void) saveGameData {
NSLog(#"LS:saveGameData");
// SAVE DATA IMMEDIATELY
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *gameStatePath = [documentsDirectory stringByAppendingPathComponent:#"gameState.dat"];
NSMutableData *gameSave= [NSMutableData data];
NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:gameSave];
[encoder encodeObject:self.categoryLockStateArray forKey:#"categoryLockStateArray];
[encoder encodeObject:self.levelsPlist forKey:#"levelsPlist"];
[encoder finishEncoding];
[gameSave writeToFile:gameStatePath atomically:YES];
NSLog(#"encoded catLockState:%#",categoryLockStateArray);
}
- (void) loadGameData {
NSLog(#"loadGameData");
// If there is a saved file, perform the load
NSMutableData *gameData = [NSData dataWithContentsOfFile:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"gameState.dat"]];
// LOAD GAME DATA
if (gameData) {
NSLog(#"-Loaded Game Data-");
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:gameData];
self.levelsPlist = [unarchiver decodeObjectForKey:#"levelsPlist"];
categoryLockStateArray = [unarchiver decodeObjectForKey:#"categoryLockStateArray"];
NSLog(#"decoded catLockState:%#",categoryLockStateArray);
}
// CREATE GAME DATA
else {
NSLog(#"-Created Game Data-");
self.levelsPlist = [[NSMutableDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:kLevelsPlist ofType:#"plist"]];
}
if (!categoryLockStateArray) {
NSLog(#"-Created categoryLockStateArray-");
categoryLockStateArray = [[NSMutableArray alloc] initWithCapacity:[[self.levelsPlist allKeys] count]];
for (int i=0; i<[[self.levelsPlist allKeys] count]; i++) {
[categoryLockStateArray insertObject:[NSNumber numberWithBool:FALSE] atIndex:i];
}
}
// set the properties of the categories
self.categoryNames = [self.levelsPlist allKeys];
NUM_CATEGORIES = [self.categoryNames count];
thisCatCopy = [[NSMutableDictionary alloc] initWithDictionary:[[levelsPlist objectForKey:[self.categoryNames objectAtIndex:pageControl.currentPage]] mutableCopy]];
NUM_FINISHED = [[thisCatCopy objectForKey:kNumLevelsBeatenInCategory] intValue];
}
I can only guess that it is related to the fact that the viewController is unloaded and reloaded when you pop then push back in, but my viewDidLoad code makes no mention of either of these variables. They are called from the viewDidAppear method.
Thanks for any help you can offer!
Related
Have tried storing my NSMutableArray's object to NSUserDefaults but, no luck.
My NSMutableArray contains this log right here:
`ALAsset - Type:Photo, URLs:assets-library://asset/asset.JPG?id=92A7A24F-D54B-496E-B250-542BBE37BE8C&ext=JPG`
I know that its a ALAsset object, in the AGImagePickerController it is compared as NSDictionary, so what I needed to do is save the NSDictionary or the Array I used to where I store my ALAsset object then save it in either in NSDocu or NSCaches as a file then retrieve it again (This was my idea).
But the problem is,Though I tried this code but not working, and doesn't display anything in NSDocu or NSCache Directories.
First try (info is the one that contains ALAsset object):
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSString *filePath = [basePath stringByAppendingPathComponent:#"filename.plist"];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:filePath];
NSString *error;
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
if(plistData) {
[info writeToFile:filePath atomically:YES];
} else {
NSLog(error);
}
Second try:
- (NSString *)createEditableCopyOfFileIfNeeded:(NSString *)_filename {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableFilePath = [documentsDirectory stringByAppendingPathComponent: _filename ];
success = [fileManager fileExistsAtPath:writableFilePath];
if (success) return writableFilePath;
// The writable file does not exist, so copy the default to the appropriate location.
NSString *defaultFilePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: _filename ];
success = [fileManager copyItemAtPath:defaultFilePath toPath:writableFilePath error:&error];
if (!success) {
NSLog([error localizedDescription]);
NSAssert1(0, #"Failed to create writable file with message '%#'.", [error localizedDescription]);
}
return writableFilePath;
}
Save it this way:
NSString *writableFilePath = [self createEditableCopyOfFileIfNeeded:[NSString stringWithString:#"hiscores"]];
if (![info writeToFile:writableFilePath atomically:YES]){
NSLog(#"WRITE ERROR");
}
Third try:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:??????];
[info writeToFile:filePath atomically:YES];
Fourth try(Unsure of because of its modifying in the appbundle):
https://stackoverflow.com/a/6311129/1302274
Is there other way? Hope someone would guide me.
You can store your NSMutableArray to NSUserDefault by archiving it to NSData and than retrieving it by Unarchiving it back to NSMutableArray.
-(NSData*) getArchievedDataFromArray:(NSMutableArray*)arr
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
return data;
}
-(NSMutableArray*) getArrayFromArchievedData:(NSData*)data
{
NSMutableArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return arr;
}
For saving array to NSUserDefault :
[[NSUserDefaults standardUserDefaults] setObject:[self getArchievedDataFromArray: yourArray] forKey:#"YourKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
For retrieving array back from NSUserDefault :
NSMutableArray *yourArray = [self getArrayFromArchievedData:[[NSUserDefaults standardUserDefaults]objectForKey:#"YourKey"]];
Also you can save Array in form of NSData to a file in NSDocumentDirectory or NSCachesDirectory. Hope this helps....
Edited: An UIImage+NSCoding category
.h file
#import <UIKit/UIKit.h>
#interface UIImage (NSCoding)
- (id) initWithCoderForArchiver:(NSCoder *)decoder;
- (void) encodeWithCoderForArchiver:(NSCoder *)encoder ;
#end
.m file
#import "UIImage+NSCoding.h"
#import <objc/runtime.h>
#define kEncodingKey #"UIImage"
#implementation UIImage (NSCoding)
+ (void) load
{
#autoreleasepool {
if (![UIImage conformsToProtocol:#protocol(NSCoding)]) {
Class class = [UIImage class];
if (!class_addMethod(
class,
#selector(initWithCoder:),
class_getMethodImplementation(class, #selector(initWithCoderForArchiver:)),
protocol_getMethodDescription(#protocol(NSCoding), #selector(initWithCoder:), YES, YES).types
)) {
NSLog(#"Critical Error - [UIImage initWithCoder:] not defined.");
}
if (!class_addMethod(
class,
#selector(encodeWithCoder:),
class_getMethodImplementation(class, #selector(encodeWithCoderForArchiver:)),
protocol_getMethodDescription(#protocol(NSCoding), #selector(encodeWithCoder:), YES, YES).types
)) {
NSLog(#"Critical Error - [UIImage encodeWithCoder:] not defined.");
}
}
}
}
- (id) initWithCoderForArchiver:(NSCoder *)decoder {
if (self = [super init]) {
NSData *data = [decoder decodeObjectForKey:kEncodingKey];
self = [self initWithData:data];
}
return self;
}
- (void) encodeWithCoderForArchiver:(NSCoder *)encoder {
NSData *data = UIImagePNGRepresentation(self);
[encoder encodeObject:data forKey:kEncodingKey];
}
#end
The documentation of NSArray for the "writeToFile:atomically:" method, shows that all members must be property list objects. ALAsset is not a property list object, so writing that to a file is not going to work.
I know that its a ALAsset object, in the AGImagePickerController it is
compared as NSDictionary
If you looked carefully then you would have seen that it does not compare ALAsset's, but their 'ALAssetPropertyURLs' property. The value of that property is an NSDictionary.
As ALAsset does not have a public constructor, there is no way you can reconstruct it after reading from a file or NSUserDefaults, even if you manage to write it.
So the best thing you can do is to re-fetch the ALAssets from the source that you originally got them from. I assume that is an ALAssetsGroup? Instead of saving to file and retrieving again, why don't you just regenerate them with the same query on ALAssetsGroup as you originally used to generate them?
EDIT:
So you say you got the original ALAsset's from an AGImagePickerController. In order to store them, you can take Matej's advice in the comments and store the URLs that identify them.
But keep in mind that AGImagePickerController is a means for the user to pick a number of photos and then do something with them. That is, the ALAssets are simply intermediare results pointing to the original locations of the photos. If you store the URL's and retrieve them later, there is no guarantee at all that the originals are still there.
So ask yourself: what is it that you want the user to do with the photos, and store the result of that action, rather than the assets themselves. For example, one reasonable action you could do is to create a new ALAssetGroup (with the addAssetsGroupAlbumWithName: method on ALAssetsLibrary), and store the assets in there. ALAssetGroups are automatically saved, so you don't need to do anything yourself for that.
EDIT 2 - after more information from the OP
What Matej hints at in the comments, is to convert the array of ALAssets that you have into an array of dictionaries by retrieving the urls from the assets. As you can read in the ALAsset class documentation you can do that in the following way:
NSArray *assetArray = // your array of ALAssets
NSMutableArray *urls = [NSMutableArray arrayWithCapacity:assetArray.count];
for( ALAsset *asset in assetArray ) {
NSDictionary *urlDictionary = [asset valueForProperty:#"ALAssetPropertyURLs"];
[urls addObject:urlDictionary];
}
The resulting array of dictionaries you can save in any way you like.
After restart of your app, you read the array of dictionaries back from where you stored it. Then Matej suggests to use ALAssetsLibrary's assetForURL:resultBlock:failureBlock: to recreate the ALAssets. But as we now know you want to put a checkmark on the original assets again, it is better to fetch the original array of ALAssets, and check whether any of them are present in the recovered urls. The following should work for that:
NSArray *assetArray = // the full array of ALAssets from AGImagePickerController
NSArray *urls = // the recovered array of NSDictionaries
for( ALAsset *asset in assetArray ) {
NSDictionary *urlDictionary = [asset valueForProperty:#"ALAssetPropertyURLs"];
if( [urls containsObject:urlDictionary] ) {
... // set checkmark on asset
}
}
This assumes the original assets have not changed, which is not under your control (the user has removed/added photos, for example).
This is the method I use for storing array or dictionary objects.
- (NSArray*)readPlist
{
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[documentPaths lastObject] stringByAppendingPathComponent:#"filename.plist"];
NSFileManager *fMgr = [NSFileManager defaultManager];
if (![fMgr fileExistsAtPath:plistPath]) {
[self writePlist:[NSArray array]];
}
return [NSArray arrayWithContentsOfFile:plistPath];
}
- (void)writePlist:(NSArray*)arr
{
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[documentPaths lastObject] stringByAppendingPathComponent:#"filename.plist"];
NSFileManager *fMgr = [NSFileManager defaultManager];
if ([fMgr fileExistsAtPath:plistPath])
[fMgr removeItemAtPath:plistPath error:nil];
[arr writeToFile:plistPath atomically:YES];
}
In the program I use this method to create a file in which I can save:
-(NSString*) saveFilePath{
NSString* path = [NSString stringWithFormat:#"%#%#",
[[NSBundle mainBundle] resourcePath],
#"savingfile.plist"];
return path;}
Then I used a button to initiate the process of putting the data into the file (I first put it all into an array so that it would be easier.):
- (IBAction)save
{
NSMutableArray *myArray = [[NSMutableArray alloc] init];
[myArray addObject:name.text];
[myArray addObject:position.text];
[myArray addObject:cell.text];
[myArray addObject:office.text];
[myArray addObject:company.text];
[myArray writeToFile:[self saveFilePath] atomically:YES];
}
Finally, I loaded the information back into the textfields in the - (void)viewDidAppear method.
- (void)viewDidAppear:(BOOL)animated
{
NSMutableArray* myArray = [NSMutableArray arrayWithContentsOfFile:[self saveFilePath]];
name.text = [myArray objectAtIndex:0];
position.text = [myArray objectAtIndex:1];
cell.text = [myArray objectAtIndex:2];
office.text = [myArray objectAtIndex:3];
company.text = [myArray objectAtIndex:4];
}
For some reason, on the simulator it's working perfectly on the simulator, but not working at all when I try to run on my physical iPhone.
I think you're trying to save to a location that's read-only on iOS. It works on the simulator because the simulator doesn't totally replicate the sandboxing environment on the actual hardware.
Rather than saving to the resourcesPath you should be saving your files to the Documents directory (or the cache directory, if appropriate).
You can get a path to the documents directory as follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
There's more information at this question here: How to save file in resource folder of an app in Objective-c
I've been stuck on this for ever and I finally figured it out and now just out of the blue it stopped working again...
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/scoreCards.dgs",documentsDirectory];
NSMutableArray *savedArrayOfScorecards = [[NSMutableArray alloc]init];
savedArrayOfScorecards = [NSMutableArray arrayWithContentsOfFile:filePath];
[savedArrayOfScorecards addObject:currentScoreCard];
[savedArrayOfScorecards writeToFile:filePath atomically:YES];
The file scoreCards.dgs is not even getting created...
What am I doing wrong?
There could be a couple things going wrong here.
1) The kind of data you're storing in the array might not be encodable or archive-able to a file. And the code snippet you included doesn't give a good hint as to what kind of data you're trying to save. If you have custom objects in your array (i.e. things that are not NSString, NSNumber, NSDate, etc.), then that's definitely the problem. There are plenty of questions here on StackOverflow that might help you solve this issue.
2) Your array's filepath could be bogus. For example, you're not checking to see if "documentsDirectory" is nil or valid or writeable.
3) Also possible, but not likely, "savedArrayOfScorecards" might be a nil array. You should do error checking to make sure "savedArrayOfScorecards" was instantiated and that there is more than one object in the array.
Your problem is, that although you create an array, before reading the file it is getting nil-ed on your call to:
savedArrayOfScorecards = [NSMutableArray arrayWithContentsOfFile:filePath];
So, because this savedArrayOfScorecards is now nil, your call to write it to a file is not doing anything.
You should load the array to another variable, and check it being nil, and create the new array only if the one read from the file is nil. Something like this:
NSMutableArray *savedArrayOfScorecards = [NSMutableArray arrayWithContentsOfFile:filePath];
if (!savedArrayOfScorecards) {
savedArrayOfScorecards = [[NSMutableArray alloc]init];
}
Are you sure the file exists when loading it?
savedArrayOfScorecards = [NSMutableArray arrayWithContentsOfFile:filePath];
This line creates a new NSMutableArray from the file. If the file does not exist, it returns nil. writeToFile is then sent to nil and nothing would happen.
Add a check to see if it's nil and create a new array if it is:
NSMutableArray *savedArrayOfScorecards = [NSMutableArray arrayWithContentsOfFile:filePath];
if(savedArrayOfScorecards == nil) savedArrayOfScorecards = [NSMutableArray array];
[savedArrayOfScorecards addObject:currentScoreCard];
[savedArrayOfScorecards writeToFile:filePath atomically:YES];
NSMutableArray is not a property-list-compliant format. You must use an NSArchiver to make it plist compliant.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/scoreCards.dgs",documentsDirectory];
NSMutableArray *savedArrayOfScorecards = [[NSMutableArray alloc]init];
savedArrayOfScorecards = [NSMutableArray arrayWithContentsOfFile:filePath];
[savedArrayOfScorecards addObject:#"ALLLAALLAAALLA"];
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archive = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data];
[archive encodeObject:savedArrayOfScorecards forKey:#"Scorecards"];
[archive finishEncoding];
BOOL result = [data writeToFile:filePath atomically:YES];
NSLog(result ? #"YES" : #"NO");
The correct answers are already here, just adding a better solution:
NSFileManager* fileManager = [NSFileManager defaultManager];
NSMutableArray* array;
if ([fileManager fileExistsAtPath:filePath]) {
array = [NSMutableArray arrayWithContentsOfFile:filePath];
NSAssert(array != nil, #"Invalid data in file.");
}
else {
array = [[NSMutableArray] alloc] init];
}
[array addObject:currentScoreCard];
[array writeToFile:filePath atomically:YES];
I am having troubles with my class which reads and writes data to a plist. Here is some code:
This first chunk is from my custom class with all my plist read and write methods.
-(NSString *) dataFilePath{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [path objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:#"userInformation.plist"];
}
-(bool)readUserIsMale{
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSDictionary *boolDict = [[NSDictionary alloc] initWithContentsOfFile:[self dataFilePath]];
return [[boolDict objectForKey:#"boolUserIsMale"] boolValue];
}
return nil;
}
-(void)writeUserIsMale:(bool)boolValue{
NSDictionary *boolDict = [[NSDictionary alloc] init];
[boolDict setValue:[NSNumber numberWithBool:boolValue] forKey:#"boolUserIsMale"];
[boolDict writeToFile:[self dataFilePath] atomically:YES];
}
I then in another class where desired import, create and use the class methods:
#import "plistReadWrite.h"
plistReadWrite *readWrite;
If I try and see its value in the console I get (null) return.
NSLog(#"%#",[readWrite readUserIsMale]);
This is of course after I have written some data like so:
[readWrite writeUserIsMale:isUserMale];
isUserMale being a bool value.
Any help would be massively appreciated, if you need anymore info let me know. Thanks.
I think this is mostly correct. In your writeUserIsMale: method you want a mutable dictionary, so you can actually set that key (this should have crashed for you as is, so I'm guessing a copy/paste problem?)
//NSDictionary *boolDict = [[NSDictionary alloc] init];
//should be:
NSMutableDictionary *boolDict = [[NSMutableDictionary alloc] init];
And then when you log the value, remember that bool (or BOOL) are primitives, not objects so:
NSLog (#"%d",[readWrite readUserIsMale]); // Will print 0 or 1
// or if you prefer:
NSLog (#"%#", ([readWrite readUserIsMale]? #"YES":#"NO")); // print YES or NO
Lastly, since this is objective-c, I would probably use BOOL instead of bool.
I'm assuming this is just a simple example, and that you know about NSUserDefaults for this sort of thing.
Hope that helps.
If I want to store 2 strings, and ABContact image (or a variable to call the image) that persist even after restarting the application, which method should I use? NsMutableArray, plist or SQLite?
For very small amounts of data, like a few strings, but not an image, you could also use the much simpler NSUserDefaults. This is usually for saving preferences or some persistent data.
To save it:
[[NSUserDefaults standardUserDefaults] aString forKey:#"aStringKey];
if (![[NSUserDefaults standardUserDefaults] synchronize])
NSLog(#"not successful in writing the default prefs");
To read it:
aString = [[NSUserDefaults standardUserDefaults] stringForKey:#"aStringKey];
Again, I wouldn't go ahead and use this if what you really need is a database or a filesystem.
If you are putting in more data, and not enough to warrant an SQL database or Core Data, then I would write the data to a plist file. If all your data are objects, then I would do this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:kSavedDocFileName];
NSMutableArray *myArrayToSave = [[NSMutableArray alloc] arrayWithCapacity: 48];
// 48 since you said in the comments that you have 48 of these
for (int i = 0; i < 48; i++) {
NSDictionary *myDict = [[NSDictionary alloc] initWithObjectsAndKeys:
sting1[i], #"firstStr", //I'm assuming your strings are in a C array.
string2[i], #"secondStr",// up to you to replace these with the right
url[i], #"myURL", //way to access your 48 pieces of data
nil];
[myArrayToSave addObject:myDict];
[myDict release];
}
if (![myArrayToSave writeToFile:path atomically:YES])
NSLog(#"not successful in saving the unfinished game");
And you can read the data:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:kMySavedDocName];
NSArray *myLoadedData = [[NSArray alloc] initWithContentsOfFile:path];
for (int i = 0; i < 48; i++) {
NSDictionary *myDict = [myLoadedData objectAtIndex:i];
string1[i] = [myDict objectForKey:#"firstStr"];
string2[i] = [myDict objectForKey:#"secondStr"];
url[i] = [myDict objectForKey:#"url"];
}
[myLoadedData release];
Make sure you don't just copy and paste this code since I just typed it in off the top of my head.
Now, if you don't want to mess with a dictionary, you could do that too. It's a little less elegant:
Save:
NSMutableArray *myArrayToSave = [[NSMutableArray alloc] arrayWithCapacity: 48 * 3];
// 48 since you said in the comments that you have 48 of these three objects
for (int i = 0; i < 48; i++) {
[myArrayToSave addObject:string1[i]];
[myArrayToSave addObject:string2[i]];
[myArrayToSave addObject:url[i]];
}
if (![myArrayToSave writeToFile:path atomically:YES])
NSLog(#"not successful in saving the unfinished game");
Load:
NSArray *myLoadedData = [[NSArray alloc] initWithContentsOfFile:path];
for (int i = 0; i < 48; i++) {
string1[i] = [myLoadedData objectAtIndex:i * 3];
string2[i] = [myLoadedData objectAtIndex:i * 3 + 1];
url[i] = [myLoadedData objectAtIndex:i * 3 + 2];
}
[myLoadedData release];
Another important note is that in all these examples, I am assuming that you are dealing with objects (48 times 3 of them). That is why you can just add them to dictionaries and arrays and save them and reload them easily. If you were dealing with non-objects, like ints, or c strings or BOOLs, then you will have to use [NSNumber ...] to turn those into objects and then [object ...] to turn them back into non-ojects to begin with.
Try some of these, and then go find your app folder in ~/Library/Application Support/iPhone Simulator/Applications folder, and look in the Documents folder, open the file you have saved, and verify its contents. It's always reassuring to be able to check the data you write out - and to see how the different methods change the contents.
Good luck!
An NSMutableArray will not persist after the application is restarted.
You may want to look at a plist that stores the two strings and a file URL pointing to the address book image that is cached in the application's Documents sandbox.
If you have lots of these to store and retrieve between application restarts, look into Core Data.
Don't do a database. It's way overkill for that little data.
A plist would be fine. You should probably not use NSUserDefaults, I think that's right on the borderline of too much data for it. Just write a plist to the documents directory inside your application, and load it up when your application restarts. You can store an NSMutableArray in a plist, but it comes back to you as an NSArray. Just re-create it as a mutable array with [NSMutableArray arrayWithArray:] and retain it.
(void)doneAceSpadeSetupClick:(id)sender {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *string1;
NSString *string2;
NSString *string3;
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"card1.plist"];
NSMutableArray *myArrayToSave = [[NSMutableArray alloc] initWithCapacity: 1];
//48 since you said in the comments that you have 48 of these
NSDictionary *myDict = [[NSDictionary alloc] initWithObjectsAndKeys:
string1, characterText.text, //I'm assuming your strings are in a C array.
string2, actionText.text,// up to you to replace these with the right
string3, objectText.text,
nil]; //iPhone simulator crashed at this line
[myArrayToSave addObject:myDict];
if (![myDict writeToFile:path atomically:YES])
NSLog(#"not successful in saving the unfinished game");
[self dismissModalViewControllerAnimated: YES];
}