iPhone: How to write plist array of dictionary object - iphone

I'm a young Italian developer for the iPhone. I have a plist file (named "Frase") with this structure:
Root Array
- Item 0 Dictionary
Frase String
Preferito Bool
- Item 1 Dictionary
Frase String
Preferito Bool
- Item 2 Dictionary
Frase String
Preferito Bool
- Item 3 Dictionary
Frase String
Preferito Bool
exc.
An array that contains many elements dictionary, all the same, consisting of "Frase" (string) and "Preferito" (BOOL).
The variable "indirizzo", increase or decrease the click of the button Next or Back. The application interface:
http://img691.imageshack.us/img691/357/schermata20100418a20331.png
When I click on AddPreferito button, the item "Preferito" must be YES. Subsequently, the array must be updated with the new dictionary.The code:
(void)addpreferito:(id)sender {
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Frase" ofType:#"plist"];
MSMutableArray *frase = [[NSMutableArray alloc] initWithContentsOfFile:plistPath];
NSMutableDictionary *dictionary = [frase objectAtIndex:indirizzo];
[dictionary setValue: YES forKey:#"Preferito"];
[frase replaceObjectAtIndex:indirizzo withObject:dictionary];
[frase writeToFile:plistPath atomically:YES];
}
Why not work?
Thanks Thanks Thanks!

What Jason said is correct, but it looks like there's another, more serious problem in your code: it appears that you're passing a primitive value (the defined constant YES) as the first argument to -setValue:forKey:, which expects an argument of type id (in other words, an object, not a primitive).
Instead, you can use an instance of NSNumber to wrap the boolean value, and then put it in the array, like so:
[dictionary setValue:[NSNumber numberWithBool:YES] forKey:#"Preferito"];

You are loading your initial plist from inside your application bundle. Unfortunately, you cannot write to this directory. When you need to do is check if your plist already exists in the user documents directory. If it does, load it from there. If not, load your template (original) from inside the application bundle. When you write the file back, you must always write it to the user documents directory.
For more information on managing your application's files, please see the Files and Data Management section of the iPhone Application Programming Guide.

Related

How to avoid the duplicate dictionary objects in NSMutableArray

Hi in one of my application,I have an array which contains a group of NSMutableDictionary objects. The dictionary object have three key-value pairs as like below
company
product
quantity
And array having many number of objects. Here now by using different add buttons I am adding these dictionary objects to the array. Even while adding objects to array i am checking whether any duplicate objects are available or not using NSNotFound method. As such below
if([Array indexOfObject:dicObject] == NSNotFound)
{
[Array addObject:dicObject];
}
Here it is working fine in few cases, But it's not working in other cases.I will explain with one example :
For example i have one dicobject in array with following key value pairs
company:XYZ Product:ABC Quantity:2
Now for example I want to add one more dic object with the same above key value pairs. That time obviously it won't add because already same product is available in array.
This is valid condition.
Exceptional Case: For example I want to add one more product with following values
Company:XYZ Product:ABC Quantity:6
At this case this product is adding into the array without any error. But my concern is i don't want to add this into the array again only the quantity have to update, because company and product name both are same so. So can you please show me the way to handle this scenario.
You could use indexOfObjectPassingTest: to know if a similar dictionary is already present in the array.
This may look something like this:
NSMutableArray *arr = // your array
NSDictionary *dicObject = // your object
NSUInteger indexOfDicObject = [arr indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
{
return ([obj[#"company"] isEqualToString:dicObject[#"company"]] &&
[obj[#"product"] isEqualToString:dicObject[#"product"]]);
}];
if (indexOfDicObject == NSNotFound)
{
[arr addObject:dicObject];
}
else
{
NSNumber *quantity = arr[indexOfDicObject][#"quantity"];
arr[indexOfDicObject][#"quantity"] = #([quantity intValue] + [dicObject[#"quantity"] intValue]);
}
I made the following assumptions:
the company value is a NSString;
the product value is a NSString;
the quantity value is an integer, stored in a NSNumber.
See also trojanfoe's answer, which is better if you can replace your dictionaries by classes.
I think you need to change tack; first create a custom object to hold your company, product and quantity and ensure you implement the isEqual: and hash methods.
Then simply store your custom objects within an NSMutableSet object, which will ensure that duplicates cannot exist.
Your custom object will now become your principle Model object for the app (i.e. provide the 'M' in MVC, the design pattern upon which Cocoa and Cocoa Touch apps are based) and you will find that it will be reused over and over as the app grows.

Objective C plist searching and random entry

another couple of questions about plists and objective c for the iphone.
Both questions relate to my plist which can be seen in my last last question.
First thing is to do with searching, I know this is possible but what is the correct way to go about this? Should I pull all the searchable objects into an array and use this to build a table? Or is it possible to itereate through the plist and simply just show the matches? Or is there some other way I am missing here? As a quick example in the following I would want to bring back the two 'Jones' results:
<dict>
<key>A</key>
<array>
<string>A Jones</string>
<string>A King</string>
</array>
<key>T</key>
<array>
<string>T Jones</string>
<string>T King</string>
</array>
Secondly, is it possible to call up a random result from the plist, I'm pretty sure it is, but again what would be the correct way to go about this?
I will admit to finding the plist a bit of a pain as it seems to me like a bit of a rubbish form of xml. And I am still finding iterating through a plist dictionary pretty confusing to some degree. Still, any thoughts on these two questions would be greatly appreciated.
Thanks :)
It is obviously possible to iterate through a NSDictionary values using -(NSEnumarator *)objectEnumerator;, you can also retrieve all the values with -(NSArray *)allValues; and you could also have a look to -(NSSet *)keysOfEntriesPassingTest:(BOOL (^)(id key, id obj, BOOL *stop))predicate; which returns a NSSet containing the keys for the value have passed the test (from Mac OS X 10.6).
About the second question, I think there's no 'better' way. Here is how I would do that :
Get all the keys of the NSDictionary using -(NSArray *)allKeys;
Get a random number
Pick up a key in the array using -(id)objectAtIndex:(NSUInteger)anIndex;
Retrieve the corresponding object in the dictionary using : -(id)objectForKey:(id)aKey;
Then you got your object.
Hope this helps.
EDIT:
Here is a simple way to iterate over the values in the NSDictionary :
// assuming you already have a well initialized dictionary
// first create a container
NSMutableArray *selectedObjects = [[NSMutableArray alloc] init];
// then retrieve an enumerator for the dictionary
NSEnumerator *e = [theDictionary objectEnumerator];
id anObject;
// iterate...
while((anObject = [e nextObject]) != nil) {
// do what you want with the object
// in your case each object is an array
NSArray *theArray = (NSArray *)anObject;
// ...
// if you find any of them interesting put it in the first array
if([[theArray objectAtIndex:0] isEqualToString:#"Jones"]) {
[selectedObjects addObject:[theArray objectAtIndex:0]];
}
}
// here are the selected objects in selectedObjects.
// you won't forget to release selectedObjects when you don't need it anymore

Property List management

I am trying to create an app that has a list of users and each user will be assigned an array of data that is editable. I am new to persistent data so i thought a property list would be the easiest to use. The first view of the app will have a list of the users that have already edited at least their name, and then have "New User" for each property list that has not yet had anything edited. (I am sure there is code to have the program create new property lists each time a person clicked on "New User" but that is probably far too difficult for me so for the time being I have just simply defined 3 property lists that will correspond with my 3 users.)
So, for the 'viewDidLoad' portion of my apps main screen i need to populate a UIPicker with the names of the 3 users (or "New User" if there have not been any editing). I did this:
NSString *filePathForProfile1 = [self dataForProfile1];
NSString *filePathForProfile2 = [self dataForProfile2];
NSString *filePathForProfile3 = [self dataForProfile3];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePathForProfile1]) {
NSArray *arrayProfile1 = [[NSArray alloc]initWithContentsOfFile:filePathForProfile1];
NSArray *arrayProfile2 = [[NSArray alloc]initWithContentsOfFile:filePathForProfile2];
NSArray *arrayProfile3 = [[NSArray alloc]initWithContentsOfFile:filePathForProfile3];
NSArray *array = [[NSArray alloc]initWithObjects:[arrayProfile1 objectAtIndex:0],[arrayProfile2 objectAtIndex:0],[arrayProfile3 objectAtIndex:0],nil];
self.profileData = array;
arrayProfile1.release;
arrayProfile2.release;
arrayProfile3.release;
}
Now, because I have been running the program, there is already a file saved for Profile1 so the picker does display the name for that one - but for the life of me I can't quite figure out how to have it display "New User" for the other 2. I tried to set up an if-then statement with that fileExistsAtPath argument to create init the array with "New User" but then I couldn't pass the array out of the argument. Help Please!!
I would recommend you to have a look at the Archives and Serializations Programming Guide.
The most proper solution (well there is core data as well...) for your problem could be object serialisation.
You probably have an array of "Person"-Objects and the Person Class has a MutableArray of "Data"-Objects.
All you have to do then is make the Person-Class and the Data-Classe(s) NSCoding compliant. Afterwards you can easily persist your Array of Persons by something as easy as : [NSKeyedArchiver archiveRootObject:yourPersonsArray toFile:yourFilePath];
also there is a usefull tutorial found NSCoding / NSkeyedArchiver Tutorial
cheeers,
sam

how to get NSmutable array from the NSMutableDictionary?

I have use below syntax for for set the object.
[dict setObject:eventArray forKey:categoryName];
Now i am trying to get below syntax but i got nothing.
NSMutableArray *tempArrayValue=[[NSMutableArray alloc]init];
tempArrayValue =[tempDict valueForKey:categoryValue];
What is the problem i cant understand can u help me?
you have given key as categoryName not categoryValue, and while retrieving you are using categoryValue.
NSMutableArray *tempArrayValue=[[NSMutableArray alloc]init]; tempArrayValue =[tempDict valueForKey:categoryName];
If you're setting the value like this:
[dict setObject:eventArray forKey:categoryName];
Then you should be fetching it back again like this:
NSMutableArray* eventArray = [dict valueForKey:categoryName];
assuming that eventArray is of type NSMutableArray.
What you are doing has at least two different problems.
This line is a memory leak, since you allocate an object and then throw it away, so delete it:
NSMutableArray *tempArrayValue=[[NSMutableArray alloc]init];
This second line may return a nil object, if there is no object stored for the key categoryValue. (You are using an object called categoryName above, as the key to store the value):
tempArrayValue =[tempDict valueForKey:categoryValue];
You haven't posted enough code to be able to tell why it's not working otherwise.

Best way to handle persistent Boolean in plist?

What's the best way to handle Boolean values that derive from a UISwitch setting, and are stored in an NSMutableDictionary that is saved to the user's directory as persistent settings? Specifically, what's the best way to keep boolean values distinct from numeric values in an NSMutableDictionary that gets written to and read from the file system?
========
added: Thanks to Adrian and Ben. So, the upshot is that if stored via [NSNumber numberWithBool:] (this is what I have been doing) there is no convenient way to determine if that value originated as a boolean value, correct?
The second part of this is that the NSMutableDictionary that I'm storing is originally created by creating it via a plist that has Boolean values. These values have the class NSCFBoolean when first read in. Right now my strategy is to compare the value I'm working with in the dictionary with the corresponding entry in the plist.
The reason this matters is that I'm generating interface elements on the fly to allow the user to manipulate these values, and I'm associating booleans with a UISwitch. If I lose the original class by converting to a number, I get an inappropriate user interface element.
I'm considering substituting arrays as the stored value, where the first value is the stored value, and the remaining entries are the choices: where two choices exist, it is treated as a boolean... in other cases, they serve as picker values. But this seems cumbersome, if reliable.
Any ideas welcome...
BOOL values are serialized as NSNumber instances, created through NSNumber's numberWithBool: static method:
BOOL ok = YES;
NSNumber *value = [NSNumber numberWithBool:ok];
// ...later...
BOOL answer = [value boolValue];
There is no other way to serialize BOOL values. In the plist that gets generated, if saved as XML, BOOL values appear as <true/> and <false/> entities.
(Edition, Jan 17 2010)
Following my comment above, this snippet of code can help identify different subtypes of NSNumber instances when they are loaded in memory, using the "objCType" message:
NSString *path = [[NSBundle mainBundle] pathForResource:#"untitled"
ofType:#"plist"];
NSArray *array = [NSArray arrayWithContentsOfFile:path];
for (id item in array)
{
NSLog(#"%#", [NSString stringWithCString:[item objCType]
encoding:NSUTF8StringEncoding]);
}
Where untitled.plist has this structure:
<plist version="1.0">
<array>
<integer>123</integer>
<true/>
<real>1.2</real>
</array>
</plist>
In this case, the execution of the above code gives this:
2010-01-17 17:04:35.100 Untitled[62206:207] q
2010-01-17 17:04:35.101 Untitled[62206:207] c
2010-01-17 17:04:35.102 Untitled[62206:207] d
Maybe there's a different way to get the internal type of the data stored in NSNumber, but this seems to do the trick.
You can also use the constants kCFBooleanTrue and kCFBooleanFalse, though to add these to an NS-anything you'll need to cast them as IDs:
[myDictionary setObject: (id) kCFBooleanTrue forKey: #"am_I_awesome"];
CFBoolean objects are used to represent boolean values in CoreFoundation plists and collections. Using CFBoolean ensures maximum compatibility, especially when communicating with third party software.
The following function returns the value of a CFBoolean object as a C type boolean.
Boolean CFBooleanGetValue (CFBooleanRef boolean);
The following constants provide access to the true and false objects.
const CFBooleanRef kCFBooleanTrue;
const CFBooleanRef kCFBooleanFalse;