Saving NSMutableArray to plist? [duplicate] - iphone

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
iOS: store two NSMutableArray in a .plist file
In the app I'm working on I have a bunch of "Registration" objects that contain some strings,dates and integers.
All of these objects are stored in a NSMutableArray when they're created, and I now want to save this array in someway, so that when the user closes the app etc., the content can be stored and restored when app opens again.
I've read that plist is probably the best way to do so, but I can't seem to find any examples, posts etc. that show how to do it?
So basicly: How to I save my NSMutableArray to a plist file when app closes, and how to restore it again?

NSMutableArray has a method for doing this, if you know exactly where to save it to:
//Writing to file
if(![array writeToFile:path atomically:NO]) {
NSLog(#"Array wasn't saved properly");
};
//Reading from File
NSArray *array;
array = [NSArray arrayWithContentsOfFile:path];
if(!array) {
array = [[NSMutableArray alloc] init];
} else {
array = [[NSMutableArray alloc] initWithArray:array];
};
Or you can use NSUserDefaults:
//Saving it
[[NSUserDefaults standardUserDefaults] setObject:array forKey:#"My Key"];
//Loading it
NSArray *array;
array = [[NSUserDefaults standardUserDefaults] objectForKey:#"My Key"];
if(!array) {
array = [[NSMutableArray alloc] init];
} else {
array = [[NSMutableArray alloc] initWithArray:array];
};

Related

How to retrive data from document directory when application start?

I have created application that record sound and display that sound file in tableview. In that application when sound is recorded than it's filepath will store in a array , which array i use to populate my tableview. But my problem is that when i close the application then my array will be empty , and i lost my recorded file.
So now how can i get my recorded file when i open my app second time. I am storing sound file in document directory.
Store array in NSUserDefaults. Retrive and Store the array in NSUserDefaults .. see the example of NSUserDefaults
In AppDelegate.h file just declare variable...
NSUserDefaults *userDefaults;
NSMutableArray *yourArray;
after...
In AppDelegate.m Fille in applicationDidFinishLonching: Method
userDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingtblArrayForSearch = [userDefaults objectForKey:#"yourArray"];
if (dataRepresentingtblArrayForSearch != nil) {
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingtblArrayForSearch];
if (oldSavedArray != nil)
yourArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
else
yourArray = [[NSMutableArray alloc] init];
} else {
yourArray = [[NSMutableArray alloc] init];
}
[yourArray retain];
after that when you want to insert Data in this UserDefaults Use Bellow Code...
[appDelegate.yourArray addObject:yourDataString];///set your value or anything which you want to store here...
NSData *data=[NSKeyedArchiver archivedDataWithRootObject:appDelegate.yourArray];
[appDelegate.userDefaults setObject:data forKey:#"yourArray"];
[appDelegate.userDefaults synchronize];
i hope this help you...

where to save 100 NSStrings + other type

I need to save somewhere 100 strings (quiz questions iphone app). Also, some datatype which will store the the information if the right button was pressed. What is the best way to store these values, in NSArray, NSMutableArray or in .plist?
Thank you in advance
You can use a PList to add the data to you project and then load them into you NSDictionary, you can read in a PList into a NSDictionary with -[NSDictionary initWithContentsOfFile:]
For each separate question create an NSMutableDictionary and store it into NSMutableArray is the best way to work with such process.
Code for reference:
NSMutableArray *arTemp = [[NSMutableArray alloc] init];
for(int i =0; i < 10; i++)
{
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init];
[tempDict setObject:#"object1" forKey:[NSString stringWithFormat:#"value%i", i]];
[tempDict setObject:#"object1" forKey:#"type"];
[arTemp addObject:tempDict];
[tempDict release];
}
Is that clear to you?

Reading and Writing to an NSDictionary?

In my app I have a table view where the user presses an "add" button in the nav bar and a new cell is added to the table view. The data from the table is loaded from an NSArray and each index in the array is storing an NSMutableDictionary with 4 Key-Value pairs. This array is saved to a .plist every time a cell is added. Now when the user selects a row in the table a detail view gets loaded. This detail view simply loads the data from the saved .plist depending on what row was selected.
In this detail view I want to allow the user to edit the data in the dictionary for that specific row. I've been trying different things and I can read the data from the dictionary and load it into the view but when I try and save the data back to the dictionary my app keeps terminating.
Can someone explain to me the proper way to reading and writing data to a an NSMutableDictionary?
This is how i'm writing the data back to the .plist:
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict = [array objectAtIndex:selectedRow];
NSString *tempA = [[NSString alloc] initWithFormat:#"%d pts", ivar_A];
NSString *tempB = [[NSString alloc] initWithFormat:#"%d pts", ivar_B];
NSString *tempC = [[NSString alloc] initWithFormat:#"%d pts", ivar_C];
[dict setValue:tempA forKey:#"keyA"];
[dict setValue:tempB forKey:#"keyB"];
[dict setValue:tempC forKey:#"keyC"];
[tempA release];
[tempB release];
[tempC release];
[array insertObject:dict atIndex:selectedRow];
//Save the modified array back to the plist
[array writeToFile:[self dataFilePath] atomically:YES];
[dict release];
[array release];
if you use [[NSMutableDictionary alloc] dictionaryFromContentsOfFile:#"pathtofile.plist], the contents of the dictionary will still be immutable, even if the top level container is mutable. It is specifically stated in Apple's reference.
NSDictionary Reference
The dictionary representation in the
file identified by path must contain
only property list objects (NSString,
NSData, NSDate, NSNumber, NSArray, or
NSDictionary objects). For more
details, see Property List Programming
Guide. The objects contained by this
dictionary are immutable, even if the
dictionary is mutable.
If you need to make the data structure writable again, you will need to make a copy of the loaded data structure using mutable types, like
[NSMutableDictionary dictionaryWithDictionary:oOrig]
etc.

How to persist data using archiving function on the iphone?

I am having trouble with persisting data.
Desired Behavior:
I have a text field defined in the view that captures user input. When the submit button is clicked (touch up inside), I expect to write that data to a mutable array. As of right now, I am going to archive that mutable array upon submit, but have also played with archiving once the view disappears/unloads. When a user navigates away from the view or exits the application and re-enters, I want to display the first element of that mutable array in the textfield.
Actual Behavior:
Textfield captures data and updates myMutableArray upon submitting. I have a few labels on the view plus NSLog to verify the count grows as I hit submit. I then archive this the first time, and I see that my file now exists in myDocuments plus when I revisit the view and check the count of dataArray, which is created if that file exists, the count of dataArray matches the number of elements I created in myMutableArray. If I start to enter in data again, it resets myMutableArray and dataArray all over again. That is one of a couple problems I think I have.
Questions:
If the file exists, then dataArray is created. Now I try to set the textfield to display the first element and the program bombs out! If dataArray exists, and I can see count is some positive value, I don't understand why I can't access that first element.
I feel like when I visit the view and go through this exercise, when I revisit the view I see I have dataArray present and with positive values, but when I start adding text again through the submit button, I reset everything! So I am persisting once maybe, but then wiping it clean. So much for real persistence. What am I doing wrong?
Eventually, I will be storing a record with a few elements. I will create multiple records over time for the user to review. Is archiving the best possible way? Should I start using NSCoding instead?
- (void)viewDidLoad {
[super viewDidLoad];
// Initialize mutable array and set equal to myMutableArray property
NSMutableArray *aMutableArray = [[NSMutableArray alloc] init];
myMutableArray = aMutableArray;
// Debug Logging
// NSLog(#"Mutable Array Count is: %i", [myMutableArray count]);
NSFileManager *filemgr;
NSString *docsDir;
NSArray *dirPaths;
filemgr = [NSFileManager defaultManager];
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the data file
dataFilePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"data.archive"]];
// Check if the file already exists
if ([filemgr fileExistsAtPath: dataFilePath])
{
NSMutableArray *dataArray;
dataArray = [NSKeyedUnarchiver unarchiveObjectWithFile: dataFilePath];
myTextField.text = #"%#", [dataArray objectAtIndex:0]; // THIS BOMBS OUT MY PROGRAM FOR SURE IF INCLUDED!!!
//myUpdatedMutableArray = dataArray;
// Debug Logging
NSLog(#"Mutable Array (dataArray) Count is: %i", [dataArray count]);
//NSLog(#"The first value in the array is: %i", [dataArray objectAtIndex:0]);
}
[filemgr release];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (IBAction) mySubmitButtonPressed:(id)sender {
// Debug Logging
NSLog(#"Submit Button was Pressed!");
NSLog(#"Mutable Array (myMutableArray) Count is: %i", [myMutableArray count]);
// Create string from textfield and addobject to myMutableArray; check to see that myMutableArray grows in count
NSString *myString = [[NSString alloc] initWithFormat:#"%#",myTextField.text];
[myMutableArray addObject:myString];
NSLog(#"Mutable Array (myMutableArray) Count is: %i", [myMutableArray count]);
// More Debug Logging just using on-screen labels
NSString *mySecondString = [[NSString alloc] initWithFormat:#"%i", [myMutableArray count]];
myFirstLabel.text = myString;
mySecondLabel.text = mySecondString;
// Archive myMutableArray
[NSKeyedArchiver archiveRootObject: myMutableArray toFile:dataFilePath];
//[contactArray release];
}
There are many issues with this code.
To begin, you leak memory all over the place. Specifically here:
NSMutableArray *aMutableArray = [[NSMutableArray alloc] init];
myMutableArray = aMutableArray;
Which should really be:
self.myMutableArray = [NSMutableArray array];
After you unarchive the data, if you want to keep it in myMutableArray, simply do this:
[myMutableArray appendArray: dataArray];
Or just:
self.myMutableArray = dataArray;
You should never release singleton instances like the NSFileManager, so completely get rid of this one:
[fileMgr release];
Your code crashes because this is not valid code:
myTextField.text = #"%#", [dataArray objectAtIndex:0];
(It actually is valid code, but it will do something very different than you think)
This is not Python, so you will have to do this:
myTextField.text = [NSString stringWithFormat: #"%#",
[dataArry objectAtIndex: 0]];
But that is really the same as:
myTextField.text = [dataArray objectAtIndex: 0];
Adding a string to your array in mySubmitButtonPressed can be done much simpler than you think. Just do this:
[myMutableArray addObject: myTextField.text];
You should really learn about autoreleased objects, memory management and what retaining properties actually do.

Creating Arrays from a plist file in iphone SDK 3.3 and up : objective c

I have run into this issue and have put some major time into finding the answer. I am somewhat new to objective c but not to programming.
Here is my question.
I have a plist file with this structure
root {
A (
{songTitle : contents of song},
{songTitle : contents of song}
),
B (
{songTitle : contents of song}
),
C (
{songTitle : contents of song}
),
... kepps going
}
Sorry if the the plist structure is not correct.
Pretty much I have a root dictionary (that is what it comes with) that contains an array of A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,...Z (alphabet)
Each letter of the alphabet array contains 1 or more dictionaries that have a key, value pair of songTitle (this could be any string) as the key and the song lyrics for the value.
My issue here is I need to create an array of all song titles and have been having a rough time trying to find out how to do this. I own 4 books on object c and none of them go into detail about multidimensional arrays and how to access pieces inside them.
I have created an array with all the letters and have created an array that contains the objects from each letter.
Like I stated before I need to find out how to make an array that contains each song title.
If you can help me that would save me a lot of time.
Thanks,
Wes
I am guessing you are suggesting I change my root from a dictionary to an array?
Maybe it might be better to show my code here.
Also I have attached an updated version of my plist file
Sorry seems I cannot add the image here but you can view it
http://www.wesduff.com/images/forum_images/plist_examp.png
So as you can see I have updated the plist file to show the array of letters that each contain multiple dictionaries. Each dictionary has a songTitle and a songLyrics.
How can I write code to get an array of songTitles.
Here is what I have come up with so far
NSString *path = [[NSBundle mainBundle] pathForResource:#"songs" ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
//This gives me an array of all the letters in alphabetical order
NSArray *array = [[dict allKeys] sortedArrayUsingSelector:#selector(compare:)];
/**
Now I need to find out how to get an array of all songTitles
**/
I am still working on this and looking through what others have written but have not found anything yet.
As the first answer has suggested, should I change the root to an array or keep it as I have it in this plist image I have attached.
Thanks again,
Wes
Ok so I did some more digging and came up with this from the plist file that was included in this picture
http://www.wesduff.com/images/forum_images/plist_examp.png
- (void)viewDidLoad {
//path for plist file
NSString *path = [[NSBundle mainBundle] pathForResource:#"songList" ofType:#"plist"];
//dictionary created from plist file
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
//release the path because it is no longer needed
[path release];
//temp array to hold an array of all alphabetical letters
NSArray *array = [[dict allKeys] sortedArrayUsingSelector:#selector(compare:)];
//assign array to allLetters array
self.allLetters = array;
//Create two mutable arrays for the songArray (could do a little cleaner job of this here)
NSMutableArray *songArray = [[NSMutableArray alloc] init];
NSMutableArray *songTitles = [[NSMutableArray alloc] init];
//Add messy array to songArray then we can work with the songArray (maybe something better to do here)
for(id key in dict)
{
[songArray addObject:[dict objectForKey:key]];
}
//temp array to hold a messy array for all of the songTitles
NSArray *tempArray = [songArray valueForKey:#"songTitle"];
//go through the temparray and clean it up to make one array of all song titles and sort them
for (NSUInteger i = 0; i < [tempArray count]; i++) {
[songTitles addObjectsFromArray:[[tempArray objectAtIndex:i] sortedArrayUsingSelector:#selector(compare:)]];
}
//assign all song titles to our array of songTitles
self.allSongTitles = songTitles;
[dict release];
[allSongTitles release];
[songArray release];
[tempArray release];
[array release];
[super viewDidLoad];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
I am sure there is probably a better way to do this but this is what I have come up with on my own. Thanks
If you have single array with the contents of all the letters, the rest is fairly simple. Iterate through the objects and call the dictionary method allKeys on each one. Each call to allKeys will return an NSArray containing the keys of that specific dictionary, which you can then place into another array.
EDIT
I made a mistake, didn't go deep enough. This is what I would do:
NSString *path = [[NSBundle mainBundle] pathForResource:#"songs" ofType:#"plist"];
NSDictionary plistDict = [NSDictionary dictionaryWithContentsOfFile:path]; //not using alloc and init means this isn't retained, so it will be autoreleased at the end of the method
NSArray *allLetterContents = [plistDict allValues]; // array of arrays, where each element is the content of a 'letter' in your plist (i.e. each element is an array of dictionaries)
NSMutableArray *allSongTitles = [[[NSMutableArray alloc] init] autorelease];
for(NSArray *oneLetterContents in allLetterContents)
{
for(NSDictionary *song in oneLetterContents)
{
[allSongTitles addObject:[song objectForKey:#"songTitle"]]
}
}
return allSongTitles;
This array isn't guaranteed to be sorted alphabetically, so you'll have to call [sortedArrayUsingSelector:] on it.
Reference:
NSMutableArray Class Reference
NSDictionary Class Reference