iOS read data from a bit more 'complex' .plist - ios

I know this question had been asked for several times, but I have trouble reading data in Xcode iOS. The problem was to achieve an implementation without having the keys for NSDictionaries hardcoded in the code.
Let's say, we have this .plist:
This is my plist
In this case I want to access the name and address from the restaurants dynamically without accessing the data by a hardcoded string.
Background of this is that I try to access the data on every cell on a grouped UITableView for iOS.
Anyone has some good information about this?
Generally (normally I code in c#) I know how to access dictionaries. But in this case, there are nested dictionaries with arrays which get me really confused and also frustrating :(

NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyListName" ofType:#"plist"];
//Plist should exist in main bundle
NSDictionary *root = [[NSDictionary alloc] initWithContentsOfFile:path];
Then in your case your first value is a array in dictionary read that object in an array like,
NSArray * restaurantsArray = [names objectForKey:#"restaurants"];
Then so forth You know what type of object exist in the hierarchy just call respective methods of each,
like objectForKey for dictionary.
objectAtIndex for array.

Related

How to maintain the order of key added in NSMutableDictionary

I didn't knew this at first place, I thought that the key/value pair I am adding to my dictionary will be in the same order when I try to retrieve it back...
Now here is my question -
I have a ViewController which I have made as singleton, inside this VC I have defined:
#property (nonatomic) NSMutableDictionary *dictionary;
Now I try accessing this dictionary from various other classes and set its contents via:
[[[ViewController sharedViewController] dictionary] setValue:[NSNumber numberWithBOOL:NO] forKey:#"xx"] , yy ,zz and so on
I have my delegates implemented which would hit for a particular key(xx,yy,zz and so on). Now whenever a particular delegate method is hit I need to update my dictionary with same key but [NSNumber numberWithBOOL:YES] which is happening .. but the order in which I added the items in dictionary is not maintained ... How can I achieve this?? I need to maintain the order in the same way in which I have added my items to the dictionary at first place??
Can someone please help me out??
As pointed, you should use NSArray to have your values ordered. You can also use some 3rd party APIs like: M13OrderedDictionary or others, however, that's not a pretty solution.
Obviously, the desired solution is to create your own object or struct and keep an array of those objects/structs.
NSDictionary is just a mapping between keys and values that has no defined order. If you try to iterate over the keys, you may or may not get them back in the same order you added them, and you should never depend on the ordering of the keys when iterating over them.
One thing you could do is create an NSArray that contains the keys in the order you want them. Then you can iterate over the keys in the array and get the values from the NSDictionary in the order you wanted. The combination of these two basically gives you a sorted key NSDictionary.
Another thing you could do is to just use an NSArray to stores the values, unless the keys that you're using matter as well. But if all you're trying to do is get the values in a certain order, then I would say NSArray should work fine.
You can make a copy of key by using the following code:
NSArray *allKeys = [dictionary allKeys];
Then, the keys all saved in order and you can access particular value by getting specific key in allKeys array and retrieving it by using dictionary[allKeys[2]].

How to export data to an Xcode-project file

Is it possible to export data to a Xcode-project file?
For example, I want to generate a new "hello.csv" file, using swift, that says "hello world"
into my Xcode Project locally but not in in-app document.
To put it simply, no. If you want to use databases in code, use .plist (Preference List) files. Unfortunately, I don't know how to do this in Swift, but I do know how to in Objective-C.
To start off, make a PLIST file in Xcode, and add your data to it. Remember the key name values.
In your .m file, add this code to create an NSMutableDictionary that you can modify in code later:
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"yourFile" ofType:#"plist"]];
Then, to pull data out of the dictionary you made (dict), do something like this. Switch data types as necessary.
NSString *helloWorld = (NSString*)[dict valueForKey:#"yourKeyName"];
(Note: the cast of NSString* is necessary because, otherwise, it will yell at you for having the incompatible type of id. In Swift, it's object. If you plan on modifying the string, then use NSMutableString.)
Then, to save it to the file, save the key/value pair in the dictionary:
[dict setValue:helloWorld forKey:#"yourKeyName"];
And finally, save the dictionary and overwrite the PLIST file.
[dict writeToFile:[[NSBundle mainBundle] pathForResource:#"yourFile" ofType:#"plist"] atomically:YES];
Note: the writeToFile method's atomically parameter is asking whether or not it has to write the file right away or after it does whatever it needs to (as in, it's not a high priority). I recommend setting this to YES or true.

objectAtIndex for Nested Array iOS

Beginners iOS dev question here.
I have a plist in the following format:
I've converted this plist into an array in my app using:
NSMutableArray *array2 = [[NSMutableArray alloc] initWithContentsOfFile:path];
and have verified the content using NSLog.
However the issue I have is in understanding objectAtIndex. How do i obtain Item2 in Item1 i.e. the value 6? I'm used to Javas "array[1][2]" style :p
You can actually still use array[1][2] in Objective-C (with Xcode 4). Otherwise the "Objective-C way" to do it is:
[[array objectAtIndex:1] objectAtIndex:2];
But it is lots more code and for someone from most other backgrounds, not as readable either.
It was quite tedious, but Apple have provided a simple way to do it now. Simply put, it would be:
array2[1][2];
The first number inside the square brackets sends -[array2 objectAtIndex: 1] to the top level array. The second number sends the inner array the same message with 2 as an argument.

Copying objects from plist file to another plist file

So in my last question I tried to figure out what should I use, Plist or Core data or sqlight and I've decided that I'll try all of them to improve my skills.
So I've started reading about plist and the way they work and i'm not really sure about a few things. The app purpose I'm writing is to give the user a few lists of different songs from different genre from plist files that looks like this:
The user will be abale to choose what songs he likes and those will be added to a new list.
Can I copy songs (a simple Dictionary in a plist file in my case) and create a new plist out of them. Or should I use a stack or an array to push the songs in and use a table view as an outlet? Eventually I want the user to have saved playlists he created.
I'm curious to know if something that I've suggested won't work and what is a more elegant approach for doing so.
Thanks
Try it like this,
NSString *plistFileName = [[self documentPath] stringByAppendingPathComponent: #"Original.plist"];
if ([[NSFileManager defaultManager] fileExistsAtPath:plistFileName]) {
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plistFileName];
}
Show this data in table view and ask the user to select the songs. Save the selected one in another dictionary as,
NSDictionary *savedPlayListDict = //saved songs
NSString *fileName = [[self documentPath] stringByAppendingPathComponent: #"SavedList.plist"];
[savedPlayListDict writeToFile: fileName atomically:YES];
Or you can create your own model classes for saving the key value pairs in dictionary and use that to represent each row of the table.
For eg:- Song can be a model object with properties as, UIImage *pic, NSInteger number and NSString *name. You can add these Song objects in an array and populate your table view, using this array. Once user has selected the list, you can convert the saved list into a dictionary and save to plist as shown above.
These are just a few options. You can do it a lot of different ways.

Add objects to dictionary created by JSONKit?

In my project I have to load a number of json files. I parse them with JSONKit and after every single parsing with
NSMutableDictionary *json = [myJSON objectFromJSONString];
I add them to an array like:
[self.themeArray addObject:json];
This works fine so far. Now I need to pass the dictionaries arround between views. Works so far as well, but I need to add few more objects to the dictionary object-> json. Even it I declared json as NSMutableDictionary it does not allow me to add objects as it seems the JSONKit parser creates non-mutable dictionaries.
I was thinking about creating an object which contains the json dictionary and my additional data side by side so I wouldn´t have to change the json dictionary. I could even change it to NSDictionary because there is no need to change it. But that seems somehow not-elegant to me.
Do you have any idea how I can solve this issue without changing the JSONKit lib?
Thanks in advance!
EDIT
i just tried after changing my code to
NSMutableDictionary *json = [[myJSON objectFromJSONString] mutableCopy];
something like this
[[self.theme objectForKey:#"theme"] setObject:sender forKey:#"sender"];
[[self.theme objectForKey:#"theme"] setValue:sender forKey:#"sender"];
Xcode throws an exception:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '* -[JKDictionary setObject:forKey:]: mutating method sent to immutable object'
I assume that´s due to the fact there are still nested dictionaries in the superior dictionary. Then i would have to interate through my json object to copy all dictionaries to mutable dictionaries, right?
Perhaps it's better to switch to NSJSONSerialization as suggested by Guillaume.
EDIT
I just tried something like this
[self.theme setValue:sender forKey:#"sender"];
And it works now! It was as i assumend. Only the json object was copied to a mutable object. Probably obvious to you, it was not to me.
Thank you all for your help!
EDIT
Finally i changed my code again after i could not manage to change all objects deep inside my dictionary data to mutable objects. I threw out JSONKit and use now NSJSONDeserialization as recommendet here with the option NSJSONReadingMutableContainers. My code looks now like this and all containers (arrays and dictionaries) are mutable deep inside too. That makes me happy! ;-)
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:myJSON options:NSJSONReadingMutableContainers error:&jsonParsingError];
You can always create mutable versions of objects from their non-mutable counterparts by copying them.
NSMutableDictionary* json = [[myJSON objectFromJSONString] mutableCopy];
It is not optimal, but copying smaller dictionaries does is usually not noticable from a performance point of view.
Even it I declared json as NSMutableDictionary it does not allow me to add objects as it seems the JSONKit parser creates non-mutable dictionaries.
What type the variable is declared at means nothing. You could have declared json as NSNumber and that wouldn't make it an NSNumber.
You need to make a mutable copy of the dictionary (with mutableCopy) to get an NSMutableDictionary.
I have three ideas for your.
Create real data model objects and store them in your array. Use the JSON dictionary to init your object.
Store NSMutableDictionary objects in your array. Pass the JSON dictionary to +[NSMutableDictionary dictionaryWithDictionary:] to init the NSMutableDictionary. Others have suggested calling -[NSDictionary mutableCopy] on the JSON dictionary to do the same thing.
Create a category based on NSDictionary that stores the additional data.
NOTES:
Generally creating classes to represent your data is considered the best option, but it is also the most amount of up front work. Basically you are trading more up front work against more maintenance work as you try to keep up maintaining the dictionaries.
Storing mutable dictionary is exactly what you seem to be asking for, but it may be lots of works to find all the places where JSON dictionaries are added to the array and replacing them with the new call.
Creating a category for NSDictionary means you shouldn't need to change any of your current code, but it requires maintainers to understand how you have enhanced NSDictionary. In addition, it will help separate your changes from the original parsed JSON. You can use associated objects to store the data.

Resources