Problems extracting values from NSMutableArray - objective-c

I'm trying to parse data from a plist file into a NSMutableArray.
In my plist
Root is a Dictionary containing an Array of 6 Numbers
I've created a label hooked with the IBOutlet UILabel *lbl4 object and I want this label to show the first element of the array made reading the plist. The problem is that the program crashes at the assigning instruction (the last one).
My code is this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths objectAtIndex:0];
NSString *plistPath = [docPath stringByAppendingPathComponent:#"settings.plist"];
if(![[NSFileManager defaultManager] fileExistsAtPath:plistPath]);
{
plistPath = [[NSBundle mainBundle] pathForResource:#"settings" ofType:#"plist"];
}
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *err = nil;
NSPropertyListFormat format;
NSDictionary *temp = (NSDictionary *) [NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&err];
if(!temp)
{
NSLog(#"Error reading plist: %#, format: %d", err, format);
}
self.dataSet = [NSMutableArray arrayWithArray:[temp objectForKey:#"Dadi"]];
[lbl4 setText:[NSString stringWithFormat:#"%#", [dataSet objectAtIndex:0]]];
The plist source code is the following:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Dadi</key>
<array/>
<key>D4</key>
<integer>0</integer>
<key>D6</key>
<integer>0</integer>
<key>D8</key>
<integer>0</integer>
<key>D10</key>
<integer>0</integer>
<key>D12</key>
<integer>0</integer>
<key>D20</key>
<integer>0</integer>
</dict>
</plist>
The Debug output says "2012-09-02 18:29:55.483 Faith[6014:707] * Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'"

In your plist, the array stored at the dictionary's Dadi key is empty!
<key>Dadi</key>
<array/>
So
self.dataSet = [NSMutableArray arrayWithArray:[temp objectForKey:#"Dadi"]];
Sets self.dataSet to an empty array (i.e. even index:0 is beyond the bounds).
I would check for se.f.dataSet.count == 0 and provide a default in this case.
#warrenm mentioned in the comments that the structure of your plist is not what you might have expected. These are XML files, so any tag which ends with /> is "self-closing", and therefore always empty. To contain those numbers, you need to add an ending tag and place them inside:
<array>
<integer>7</integer>
</array>
Of course, on further evaluation, your existing plist has keys associated with those, so this is possibly also not the right solution. You'll need to evaluate what your needs are for that plist.

Related

NSMutableArray addObjects NSString

I have an problem with NSString and NSMutableArray
I Have:
NSString *msgID;
NSMutableArray *mArray;
and msgID string is an unique ID for every message and it changes it self every time when you receive a new message.
and now i want to save those IDS into NSMutableArray to put them inside plist file.
but the problem is when i do like the following
a = [[NSMutableArray alloc] init];
[a addObject:msgID];
it save only the first ID not the rest of theme
Example if the output ID is 65465465151 and you received new message after one second with ID 2123545445 the NSMutableArray save only the first output.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>65465465151</string>
</array>
</plist>
how can I make NSMutableArray add all outputs or strings which already output using one NSString ?
Here is my code
NSString *msgID = [viewcontroller.messageID substringFromIndex:[viewcontroller.messageID length] - 21];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"msgIDs.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableArray *mArray;
if (![fileManager fileExistsAtPath:path]) {
path = [documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat: #"msgIDs.plist"] ];
}
if ([fileManager fileExistsAtPath:path]) {
mArray = [[NSMutableArray alloc] init];
[mArray addObject:msgID];
} else {
// If the file doesn’t exist, create an empty dictionary
mArray = [[NSMutableArray alloc] init];
}
[mArray writeToFile:path atomically:YES];
If that's your code, and it's all in one block like that and not spread over several methods, in several different loops, then you're creating a new instance of mArray every time you go through the code block, and, as a result, at most there will be one entry in the array.
Your code looks OK, provided you are not trying to create the array in a loop repeatedly. Each addObject will enlarge the array by one. Make sure you write your plist file after the array is populated the way you expect. Make sure any old plist file is overwritten.

read multi-array from plist

I can't get it work to have the 8 items from the plist below.
I have a plist "settings.plist". Now I wan't from vragen -> category (self.catagorie - 1) -> question. The 8 items. But the array keeps empty.
What am I doing wrong?
Code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"instellingen.plist"];
NSMutableDictionary *instellingen = [NSMutableDictionary dictionaryWithContentsOfFile:path];
NSArray *vragen = [instellingen objectForKey:#"vragen"];
NSArray *cata = [vragen objectAtIndex: (self.catagorie-1)];
NSArray *question = [cata objectAtIndex: 0];
NSLog(#"count: %i", [question count]);
Plist file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>vragen</key>
<array>
<array> <--catagorydata
<array> <-- questiondata
<string>vraag</string>
<string>A</string>
<string>B</string>
<string>C</string>
<string>D</string>
<string>1</string>
<string>standaard</string>
<string></string>
</array>
<array>
<string>vraag2</string>
<string>A</string>
<string>B</string>
<string>C</string>
<string>D</string>
<string>1</string>
<string>afbeelding</string>
<string>afbeelding.jpg</string>
</array>
</array>
</array>
<key>firstBoot</key>
<true/>
<key>regelAtRuntime</key>
<true/>
</dict>
</plist>
First of all this line
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"instellingen.plist"]; ?
should be
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"settings.plist"];
I also had to delete these from the dictionary:
<--catagorydata
<-- questiondata
Besides the two things above, there is nothing wrong with your code or your dictionary.
After making those changes I tested your code and the output I got was:
tester[69637:c07] size of dict 3
tester[69637:c07] count: 8
which is correct according to the .plist file
So your problem is at the first 4 lines. Make sure your file is in Documents directory (use something like the following code to see whats going wrong)
NSLog(#"Path : %#",path);
I copied the file in the project folder (because I used the simulator) and used the following code and everything worked fine
NSBundle* b=[NSBundle mainBundle];
NSURL* path1=[b URLForResource:#"settings" withExtension:#"plist"];
NSMutableDictionary *instellingen = [NSMutableDictionary dictionaryWithContentsOfURL:path1];
NSLog(#"size of dict %d",[instellingen count]);
NSArray *vragen = [instellingen objectForKey:#"vragen"];
NSArray *cata = [vragen objectAtIndex: 0];
NSArray *question = [cata objectAtIndex: 0];
NSLog(#"count: %i", [question count]);
Just a quick read, but looks like you have an array of "categoryData" items, that contains an array of questionData items; questionData items are strings.
NSArray *vragen = [instellingen objectForKey:#"vragen"]; // count = 1
NSArray *cata = [vragen objectAtIndex: (self.catagorie-1)]; // count = 2
NSString *question = [cata objectAtIndex: 0];
Recommend you add some breakpoints and look at your data. OR, NSLog(#"data = %#", cata); etc.

Reading NSMutableArray from File

I am trying to read an 2 NSMutableArrays from file. I am saving and loading as such:
SAVE:
NSMutableDictionary *saveDict = [NSMutableDictionary dictionary];
[saveDict setValue:name forKey:#"name"];
[saveDict setValue:last_episodue forKey:#"whereat"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingString:#"/ShopFile.sav"];
[saveDict writeToFile:filePath atomically:YES];
LOAD:
name = [[NSMutableArray alloc]init];
last_episodue = [[NSMutableArray alloc]init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingString:#"/ShopFile.sav"];
NSDictionary *loadDict = [NSDictionary dictionaryWithContentsOfFile:filePath];
name = [loadDict valueForKey:#"name"];
last_episodue= [loadDict valueForKey:#"whereat"];
The variables name and last_episodue have been declared in the header file.
The program compiles and runs, however at runtime when trying to load the file, the LOAD part of the code executes, and when it finishes, the program stops working. This is the debugging information (first part):
2012-10-13 12:14:10.801 series[5223:303] -[NSISRestrictedToZeroMarkerVariable copyWithZone:]: unrecognized selector sent to instance 0x1001900c0
2012-10-13 12:14:10.803 series[5223:303] -[NSISRestrictedToZeroMarkerVariable copyWithZone:]: unrecognized selector sent to instance 0x1001900c0
2012-10-13 12:14:10.906 series[5223:303] (
Any idea what the problem might be? Thanks!
Edit: This is the content of the file where the saving takes place:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<array>
<string>a</string>
</array>
<key>whereat</key>
<array>
<string>a</string>
</array>
</dict>
</plist>
This is kind of a shot in the dark, since I can't tell without more context what your memory management looks like, but I just had this issue stemming from a variable whose value was not retained properly (we were assigning it using objc_setAssociatedObject but passing OBJC_ASSOCIATION_ASSIGN as the objc_AssociationPolicy). The pointer I held consistently ended up pointing over to an instance of NSISRestrictedToZeroMarkerVariable.
Since NSISRestrictedToZeroMarkerVariable is not a publicly-exposed class, what you're seeing is most likely the result of a memory overwrite. Set an exception breakpoint in Xcode and check out which line is throwing this error, and then track your memory management for that variable.

How to use arrayWithContentsOfFile to load file successfully written with writeToFile in ObjectiveC/XCode

I am saving an array of custom objects to a plist list file like so:
+(void) arrayToPlist: (NSArray*) plant_types{
NSMutableArray* dictionary_array = [NSMutableArray arrayWithCapacity: plant_types.count];
for(int i = 0; i<plant_types.count; i++){
PlantType* pt = [plant_types objectAtIndex: i];
[dictionary_array addObject: [pt toDict]];
}
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* filePath = [rootPath stringByAppendingPathComponent:#"PlantTypes.plist"];
NSLog(#"Writing plist to: %#", filePath);
[dictionary_array writeToFile: filePath atomically: YES];
}
Which creates a file that looks like:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>name</key>
<string>Autumn Olive</string>
<key>radius</key>
<real>10</real>
</dict>
<dict>
<key>name</key>
<string>Dwarf Cherry</string>
<key>radius</key>
<real>5</real>
</dict>
<dict>
<key>name</key>
<string>Bamboo</string>
<key>radius</key>
<real>2</real>
</dict>
<dict>
<key>name</key>
<string>Pomegranate</string>
<key>radius</key>
<real>6</real>
</dict>
<dict>
<key>name</key>
<string>Lupin</string>
<key>radius</key>
<real>0.60000002384185791</real>
</dict>
</array>
</plist>
And loading them this way:
+(NSMutableArray*) arrayFromPlist{
NSLog(#"Loading array of plant types from plist.");
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* filePath = [rootPath stringByAppendingPathComponent:#"PlantTypes.plist"];
NSFileManager* fileManager = [NSFileManager defaultManager];
if([fileManager fileExistsAtPath:filePath]){
NSLog(#"Plist file exists at expected location.");
NSMutableArray* plant_dicts = [NSMutableArray arrayWithContentsOfFile:filePath];
NSLog(#"Loaded array with contents of file, got %d plant types.", plant_dicts.count);
NSMutableArray* plant_types = [NSMutableArray arrayWithCapacity: plant_dicts.count];
for(int i = 0; i< plant_dicts.count; i++){
NSMutableDictionary* dict = [plant_dicts objectAtIndex: i];
[plant_types addObject: [PlantType fromDict: dict]];
}
return plant_types;
}
NSLog(#"Plant Type plist file not found.");
return NULL;
}
Nothings crashing, but every time I return a NSMutableArray of size zero (and that logging statement confirms that plant_dicts is of size zero).
What am I doing wrong? All the similar problems I'm seeing with arrayWithContentsOfFile: is people creating the plist by hand or with an editor...I'd think making it from the code itself wouldn't have these problems...
Edit: I have confirmed that I'm no longer getting a size zero result (which is weird, there have been no code changes in that section since the original post). However, I'm still not loading things correctly. Here is my fromDict and toDict Methods.
The problem is that the string "shape" doesn't seem to be loading right. At least, when I ask if shape == #"Circle" I get that it doesn't, even if it clearly does in the NSLog statement. I am making an identical comparison elsewhere in the code (when checking how to render the object) and it works fine there (so I assume there isn't some weird == vs .equals like there is in Java).
Double Edit: Wait, no there is [shape isEqualToString: #"Circle"]....why I didn't need to use it before confuses me, but adding it here helps.
With your plist, this code return a correct result
+(NSMutableArray*) arrayFromPlist{
NSLog(#"Loading array of plant types from plist.");
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* filePath = [rootPath stringByAppendingPathComponent:#"PlantTypes.plist"];
NSFileManager* fileManager = [NSFileManager defaultManager];
if([fileManager fileExistsAtPath:filePath]){
NSLog(#"Plist file exists at expected location.");
NSMutableArray* plant_dicts = [NSMutableArray arrayWithContentsOfFile:filePath];
NSLog(#"Loaded array with contents of file, got %d plant types.", plant_dicts.count);
NSMutableArray* plant_types = [NSMutableArray arrayWithCapacity: plant_dicts.count];
for(int i = 0; i< plant_dicts.count; i++){
NSMutableDictionary* dict = [plant_dicts objectAtIndex: i];
[plant_types addObject: dict];
}
return plant_types;
}
NSLog(#"Plant Type plist file not found.");
return NULL;
}
result
2012-02-22 16:24:43.697 Test[5574:10103] Loading array of plant types from plist.
2012-02-22 16:24:43.698 Test[5574:10103] Plist file exists at expected location.
2012-02-22 16:24:43.699 Test[5574:10103] Loaded array with contents of file, got 5 plant types.
2012-02-22 16:24:43.700 Test[5574:10103](
{
name = "Autumn Olive";
radius = 10;
},
{
name = "Dwarf Cherry";
radius = 5;
},
{
name = Bamboo;
radius = 2;
},
{
name = Pomegranate;
radius = 6;
},
{
name = Lupin;
radius = "0.6000000238418579";
}
)
I think problem come from your PlantType implementation. Can you post your code (toDict and fromDict methods)
This line:
NSMutableDictionary* dict = [plant_dicts objectAtIndex: i];
does not return a mutable dictionary, but an immutable one.
If you want it to be mutable, you should do something along these lines:
MutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary: [plant_dicts objectAtIndex: i]];

Parse Plist (NSString) into NSDictionary

So I have a plist structured string, that get dynamically (not from the file system). How would I convert this string to a NSDictionary.
I've tried converting it NSData and then to a NSDictionary with NSPropertyListSerialization, but it returns "[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x100539f40" when I attempt to access the NSDictionary, showing that my Dictionary was not successfully created.
Example of the NSString (that is the plist data):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Key1</key>
<dict>
<key>Test1</key>
<false/>
<key>Key2</key>
<string>Value2</string>
<key>Key3</key>
<string>value3</string>
</dict>
</dict>
</plist>
Thanks!
See Serializing a Property List
NSData* plistData = [source dataUsingEncoding:NSUTF8StringEncoding];
NSString *error;
NSPropertyListFormat format;
NSDictionary* plist = [NSPropertyListSerialization propertyListWithData:plistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
NSLog( #"plist is %#", plist );
if(!plist){
NSLog(#"Error: %#",error);
[error release];
}
Try this:
NSData * data = [yourString dataUsingEncoding:NSUTF8StringEncoding];
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSDictionary * dict = (NSDictionary*)[NSPropertyListSerialization
propertyListFromData:data
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];
I've tried converting it NSData and then to a NSDictionary with NSPropertyListSerialization, but it returns "[NSCFString objectAtIndex:]: unrecognized selector sent to instance 0x100539f40" when I attempt to access the NSDictionary, showing that my Dictionary was not successfully created.
No, it shows no such thing. What it shows is that you tried to treat a string as an array. You'd need to determine where in the plist you were trying to get an array and why there was a string where you expected an array—i.e., whether you created the plist incorrectly (putting a string into it where you meant to put an array) or are examining it incorrectly (the presence of a string is correct; your subsequent expectation of an array is wrong).