Why can't I serialize this data? - iphone

I want to serialize an array and an integer. When I save only the integer, it works. When I add the array, it fails.
NSArray *tempArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [tempArray objectAtIndex:0];
tempArray = nil;
tempArray = [NSArray arrayWithObjects:
( (someOtherArray != nil) ? (id) someOtherArray : [NSNull null] ),
[NSNumber numberWithInt:someIntValue],
nil];
NSString *filePath = [NSString stringWithFormat:#"%#/%#.plist", docsPath, kDataFilename];
NSString *errString;
NSData *serializedStrings= [NSPropertyListSerialization
dataFromPropertyList:tempArray
format:NSPropertyListBinaryFormat_v1_0
errorDescription:&errString];
if( errString != nil ) {
NSLog(#"SaveData NSpropertyListSerialization error: %#", [errString description]);
[errString release];
}
// returns NO
BOOL success = [serializedStrings writeToFile:filePath atomically:YES];
The array may be nil so I do a check for that first. I also tried [NSNumber numberWithInt:0] instead of [NSNull null] just to see if it would work instead of null, but it didn't.
I also get: NSpropertyListSerialization error: Property list invalid for format

You can't store +[NSNull null] in a plist. You need a workaround like a placeholder value (even though NSNull is itself a placeholder value...)

Related

Searching for objects in array by name

is there a possibility to check for an object in an array by name.
At some point in my app I need to access the plist and pull information stored under an object which I found by its name.
The reason I need to do this is because I don't have the integer number under which where the object is placed in my array.
I have started the usual procedure but i'm not getting anywhere. Maybe its just getting a little too late for me....
This is how my plist looks
Array
Dictionary
title ...
text ...
ect ...
Dictionary
title ...
text ...
ect ...
Dictionary
title ...
text ...
ect ...
My code so far isn't helping at all. I'm definitely doing something wrong.
-(NSString*)nameFromPlist:(NSString*)name {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"Orte.plist"];
_regionArray = [NSArray arrayWithContentsOfFile:writableDBPath];
NSString *string = [[NSString alloc]init];
for(NSDictionary *dict in _regionArray) {
string = [[dict objectForKey:name] valueForKey:#"title"];
}
return [NSString stringWithFormat:#"%#",string];
}
My string is just returning null.
Any ideas anyone?
Thanks a lot!
ANSWER:
Here is the code for everyone:
-(NSString*)nameFromPlist:(NSString*)name {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *tmpFileName = [[NSString alloc] initWithFormat:#"Orte.plist"];
NSString *path = [documentsDirectory stringByAppendingPathComponent:tmpFileName];
NSString *string;
NSMutableArray *array = [NSMutableArray arrayWithContentsOfFile:path];
NSDictionary *dict = [[NSDictionary alloc] init];
NSString *title;
for (int i=0; i<[array count]; i++) {
dict = [array objectAtIndex:i];
title = [dict valueForKey:#"title"];
if([title isEqualToString:name]) {
string = [dict valueForKey:#"title"];
NSLog(#"Titel: %#", string);
}
}
return [NSString stringWithFormat:#"%#",string];
}
Thank a lot everyone!
First you don't need to alloc the NSString, 'cause you'll get a different one from valueForKey.
Then if I understood right what you want to get a better code may be something that make use of NSPredicate or the block-based indexOfObjectPassingTest. Both iterate across the array and perform a test on the elements to get you the matching elements (or the index of them).
NSString* plistPath = [[NSBundle bundleForClass:[self class]] pathForResource:#"<PlistFileName>" ofType:#"plist"];
NSArray *array = [[NSArray alloc] initWithContentsOfFile:plistPath];
NSMutableArray* titles = [[NSMutableArray alloc] init];
for (NSDictionary* d in array)
[titles addObject:[d objectForKey:#"title"]];
NSLog(#"Object found at index %i", [a indexOfObject:#"<Your object name>"]);
Make sure you're testing against NSNotFound for failure to locate, and not (say) 0.

Simple NSSArray regarding strings and NSRangeException

I have a simple question. I am designing some simple highscores code yet I am having alot of trouble with it, specifically storing and saving players names. I have not been programming for very long in obj-c, only 2 weeks. I am not quite sure what is going on when I use NSStrings in an array.
Here is the code for saving strings:
-(NSString *) getFilePath2 {
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[pathArray objectAtIndex:0] stringByAppendingPathComponent:#"savedFile2.plist"];
}
-(void) savePlayerNameData {
NSArray *highScoreNames = [[NSArray alloc]
initWithObjects:
playerName1,
playerName2,
playerName3,
playerName4,
playerName5,
nil];
[highScoreNames writeToFile:[self getFilePath2] atomically:YES];
}
-(void) loadPlayerNameData{
// load data here
NSString *myPath = [self getFilePath2];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists) {
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
playerName1 = [values objectAtIndex:0];
playerName2 = [values objectAtIndex:1];
playerName3 = [values objectAtIndex:2];
playerName4 = [values objectAtIndex:3];
playerName5 = [values objectAtIndex:4];
}
else {
NSLog(#"first Launch. no file yet");
}
}
I end up getting this error :
2012-08-15 22:25:30.780 Pig Fly![5304:f803] *** Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (0) beyond bounds (0)'
*** First throw call stack:
(0x14c5022 0xec5cd6 0x146da48 0x146d9b9 0x14bec30 0xbd21 0x62e2 0x964eb6 0x1499936 0x14993d7 0x13fc790 0x13fbd84 0x13fbc9b 0x13ae7d8 0x13ae88a 0x28626 0x1fdd 0x1f45)
terminate called throwing an exception(lldb)
However, what I find very interesting is that when I write similar code for saving int values, The program runs fine. This is the working int code:
-(NSString *) getFilePath {
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[pathArray objectAtIndex:0] stringByAppendingPathComponent:#"savedFile.plist"];
}
-(void) saveData {
//save data here
NSArray *highScores = [[NSArray alloc]
initWithObjects:
[NSString stringWithFormat:#"%i",highScore1],
[NSString stringWithFormat:#"%i",highScore2],
[NSString stringWithFormat:#"%i",highScore3],
[NSString stringWithFormat:#"%i",highScore4],
[NSString stringWithFormat:#"%i",highScore5],
nil];
[highScores writeToFile:[self getFilePath] atomically:YES];
}
-(void) loadData{
// load data here
NSString *myPath = [self getFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists) {
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
highScore1 = [[values objectAtIndex:0] intValue];
highScore2 = [[values objectAtIndex:1] intValue];
highScore3 = [[values objectAtIndex:2] intValue];
highScore4 = [[values objectAtIndex:3] intValue];
highScore5 = [[values objectAtIndex:4] intValue];
}
else {
NSLog(#"first Launch. no file yet");
}
}
Can anybody tell me what is going on? What are the differences that need to be kept in mind when writing NSArrays for ints and NSStrings?
Make sure that playerName1 is not NULL.

Write to string in plist dictionary

I'm using a plist with structure like this: plist, array, dict key string key string /dict, dict key string key string /dict, /array, /plist.
What I want with the following code it to set the "Done" string's value in current dictionary to "Yes".
But now, the code replaces the whole array of dictionaries with the strings of the current dictionary (with the "Done" key valued "Yes" as it should, though.)
What would be the correct code for what I want?
in DetailViewController.m:
-(IBAction)favoriteButtonPressed:(id)sender
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Object.plist"];
[[detailsDataSource objectAtIndex: detailIndex] setValue:#"Yes" forKey:#"Done"];
[[detailsDataSource objectAtIndex: detailIndex] writeToFile:path atomically:YES];
}
The detailsDataSource and detailIndex are recieved from TableViewController.m segue if that matters.
TableViewController.m segue part:
detailViewController.detailsDataSource = [[NSArray alloc]initWithArray:objectsArray];
detailViewController.detailIndex = selectedRowIndex.row;
DetailViewController viewDidLoad
if ([[[detailsDataSource objectAtIndex: detailIndex] valueForKey:#"Favorite"] isEqual:#"Yes"]) {
[favoriteButton setImage:[UIImage imageNamed:#"favoritedItem.png"] forState:UIControlStateSelected | UIControlStateHighlighted];
}
districtLabel.text = [[detailsDataSource objectAtIndex: detailIndex] valueForKey:#"District"];
detailsDataSource array is used like this in detailViewController so I cannot change from array to dictionary, must make a mutable copy in that case.
setValue:forKey is not for modifying mutable dictionaries. You should use setObject:forKey. I.e., this:
[[detailsDataSource objectAtIndex: detailIndex] setValue:#"Yes" forKey:#"Done"];
Should be:
[[detailsDataSource objectAtIndex: detailIndex] setObject:#"Yes" forKey:#"Done"];
If that doesn't fix the problem, make sure the dictionary and array are both mutable:
-(IBAction)favoriteButtonPressed:(id)sender {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Object.plist"];
NSMutableDictionary *mutDict = [NSMutableDictionary dictionaryWithDictionary:[detailsDataSource objectAtIndex:detailIndex]];
[mutDict setObject:#"Yes" forKey:#"Done"];
NSMutableArray *tmpMutArr = [NSMutableArray arrayWithArray:detailsDataSource];
[tmpMutArr replaceObjectAtIndex:detailIndex withObject:[NSDictionary dictionaryWithDictionary:mutDict]]; // make dict immutable before replacing. Casting it ( (NSDictionary *)mutDict ) works too.
[detailsDataSource release], detailsDataSource = nil;
detailsDataSource = [[NSArray alloc] initWithArray:tmpMutArr];
[detailsDataSource writeToFile:path atomically:YES];
}
Also, instead of #"Yes", you can use [NSNumber numberWithBool:YES]:
[mutDict setObject:[NSNumber numberWithBool:YES] forKey:#"Done"];
would generally be better form then using #"Yes".
And:
districtLabel.text = [[detailsDataSource objectAtIndex: detailIndex] valueForKey:#"Yes"];
Should be:
districtLabel.text = [[detailsDataSource objectAtIndex: detailIndex] objectForKey:#"Yes"];
Also noticed that you may have a memory leak with:
detailViewController.detailsDataSource = [[NSArray alloc] initWithArray:objectsArray];
Make sure to autorelease when using property setters that retain.
[[detailsDataSource objectAtIndex: detailIndex] setObject:#"Yes" forKey:#"Done"];

NSMutableDictionary initWithContentsOfFile - null

I'm trying to create a NSMutableDictionary with the contents of a .plist. I have the following code:
NSString *myFile = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"plist"];
NSMutableDictionary* myDict = [[NSMutableDictionary alloc]initWithContentsOfFile:myFile];
NSObject *randomWord = [myDict objectForKey:#"Item 6"];
NSLog(#"%#", randomWord);
myFile and myDict are filled (not null), but when want to print randomWord (or myDict for that matter), it prints "(null)".
I've looked everywhere. What am I doing wrong?
Edit:
self MainViewController *const 0x068c0d60
myDict NSMutableDictionary * 0x0000586c
myFile NSString * 0x068c31f0
and the NSLog:
2012-05-10 12:26:22.302 project2[681:f803] (null)
So it seems myFile and myDict are filled, but randomWord isn't?
Edit #2:
NSLog(#"%#", myDict) prints:
2012-05-10 14:15:17.016 project2[2641:f803] (null)
So you're right, myDict is empty.
remove space here in item 6
[myDict objectForKey:#"Item6"];
and if this not works try this one
NSString *path = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"plist"];
NSData* data = [NSData dataWithContentsOfFile:path];
NSMutableArray* array = [NSPropertyListSerialization propertyListFromData:data
mutabilityOption:NSPropertyListImmutable
format:NSPropertyListXMLFormat_v1_0
errorDescription:NULL];
if (array) {
NSMutableDictionary* myDict = [NSMutableDictionary dictionaryWithCapacity:[array count]];
for (NSDictionary* dict in array) {
State* state = [[State alloc] initWithDictionary:dict];
[myDict setObject:state forKey:state.name;
[state release];
}
NSLog(#"The count: %i", [myDic count]);
} else {
NSLog(#"Plist does not exist");
}

retaining plist but loosing plist values

I am creating a plist to save some values, however during testing I have noted that I retain my newly created plist after the application is closed & removed from multitasking. However I loose my values inside that plist if the application is removed from multitasking but not if the app is closed...
This is the save data method I have in my plist controller class thats managing all of the read/write/save etc.
- (void) saveData:(NSString *)methodName signature:(NSString *)pSignature Version:(NSNumber *)pVersion request:(NSNumber *)rNumber dataVersion:(NSNumber *)dvReturned cacheValue:(NSNumber *)cValue;
{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// set the variables to the values in the text fields
self.signature = pSignature;
self.version = pVersion;
self.request = rNumber;
self.dataVersion = dvReturned;
//do some if statment stuff here to put the cache in the right place or what have you.
if (methodName == #"manu")
{
self.man = cValue;
}
else if (methodName == #"models")
{
self.mod = cValue;
}
else if (methodName == #"subMod")
{
self.sub = cValue;
}
self.cacheValue = [NSDictionary dictionaryWithObjectsAndKeys:
man, #"Manu",
mod, #"Models",
sub, #"SubModels", nil];
NSDictionary *plistDict = [NSDictionary dictionaryWithObjectsAndKeys:
signature, #"Signature",
version, #"Version",
request, #"Request",
dataVersion, #"Data Version",
cacheValue, #"Cache Value", nil];
NSString *error = nil;
// create NSData from dictionary
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
// check is plistData exists
if(plistData)
{
// write plistData to our Data.plist file
[plistData writeToFile:plistPath atomically:YES];
NSString *myString = [[NSString alloc] initWithData:plistData encoding:NSUTF8StringEncoding];
// NSLog(#"%#", myString);
}
else
{
NSLog(#"Error in saveData: %#", error);
// [error release];
}
}
#end
My question has two parts..
Can I save the values so they are retained in the plist even when the app is removed from the multitaksing bar.
what do I need to change to get this to work if it can work?
This probably has to do with where the saveData method is being called. Check out the app delegate, there are a few stubs in the by default. You probably want applicationWillResignActive, applicationDidEnterBackground or applicationWillTerminate. Check the documentation for each, the one you want will depend on when you want the data to be written.