iPhone programming, loading plist data, access it - ios

I have a plist in the following form:
Root (array)---> item 1 (dictionary) ----> Sch (string)
---> Name (string)
----> price (number)
----> item 2 (dictionary)----> .....same as item 1
How can I access each row (item1 to ...) and the its child (Sch, Name etc.)? One at a time?
I use:
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"Data.plist"];
NSDictionary *plistData = [[NSDictionary dictionaryWithContentsOfFile:finalPath] retain];
to load the file. How should I go about accessing each child?
What I am trying to do is, I have a NSString *message, what I want to do is to search the whole plist for matching string and display the whole item 1. Any suggestion?

When you initialize a collection from a plist, the type is the root level object. Therefore you would not initialize a dictionary but an array like so:
NSArray *plistData = [[NSArray arrayWithContentsOfFile:finalPath] retain];
Then you would access it like this:
NSString *sch;
NSString *name;
NSString *price;
for (NSDictionary *aDict in plistData) {
sch = [aDict objectAtKey:"Sch"];
name = [aDict objectAtKey:"Name"];
price = [aDict objectAtKey:"price"];
//.. do whatever
}

Here is a link to a discussion that provided a very good example of how to access your data and which type of storage schemes would be more beneficial under certain circumstances. #TechZen's solution is on target, I just thought this question would add an additional resource. Hope this helps!

Related

iOS - extracting data from a plist not working

This is a routine exercise. I have done it a number of times in my current project and it has worked fine. I copied the code line for line, same initializations. My plist data goes into a dictionary but then it does not go into its respective arrays in their initializations. I have a method called initArraysPlist
-(void)initArraysPlist{
NSString *path1 = [[NSBundle mainBundle] pathForResource:#"trainerProfile" ofType:#"plist"];
// Load the file content and read the data into arrays
NSDictionary *dict1 = [[NSDictionary alloc] initWithContentsOfFile:path1];
trainerNames = [dict1 objectForKey:#"Names"];
trainerIcons = [dict1 objectForKey:#"Icons"];
trainerFactSheet= [dict1 objectForKey:#"Fact Sheet"];
trainerFocus = [dict1 objectForKey:#"Focus"];
trainerContactInfo= [dict1 objectForKey:#"Contact Info"];
}
Ive done this a few times and it currently works in my code. all the values are correct. Ive checked it many times. when
Please read the comments for the each line.
NSString *path1 = [[NSBundle mainBundle] pathForResource:#"trainerProfile" ofType:#"plist"]; // **check if your plist is actually added in Bundle.If its there move to second line , if not then add plist in bundle.**
NSDictionary *dict1 = [[NSDictionary alloc] initWithContentsOfFile:path1];// **if plist is added in bundle , then check if you are getting value for dict1 . If no then you might be making some mistake in plist structure.**
For more clarifications please post your plist if possible.
Please try this code it may be helpful to you
// Read plist from bundle and get Root Dictionary out of it
NSDictionary *dictRoot = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"]];
// Your dictionary contains an array of dictionary
// Now pull an Array out of it.
NSArray *arrayList = [NSArray arrayWithArray:[dictRoot objectForKey:#"catlist"]];
// Now a loop through Array to fetch single Item from catList which is Dictionary
[arrayList enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) {
// Fetch Single Item
// Here obj will return a dictionary
NSLog(#"Category name : %#",[obj valueForKey:#"category_name"]);
NSLog(#"Category id : %#",[obj valueForKey:#"cid"]);
}];

Append items to an existing key in NSDictionary saved in a plist

How can I append values to an existing key in NSDictionary from a plist?
What I have is basically a plist saved in disc with a few keys and values, the keys are the names of some students with one initial value. What I’m trying to do that doesn't work is to append more items to existing keys/Students by reading the plist, adding it to a temporary NSDictionary, appending a temporary array to an existing key in the plist but when I save the plist back it doesn’t work, it only saves the last two items and basically deletes the initial value for that key.
Here is the code that I’m using…
- (IBAction)testing:(id)sender
{
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [dirPaths objectAtIndex:0];
NSString *fullPath = [documentsPath stringByAppendingPathComponent:#"studentsRecords.plist"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullPath];
if(fileExists)
{
NSMutableDictionary *dictionaryRecords = [[NSMutableDictionary alloc]initWithContentsOfFile:fullPath];
NSLog(#"Items for Nathan: %#",[dictionaryRecords objectForKey:#"Nathan"]);// here the output is, Items for Nathan: Ruler
// which make sense since I only had one initial record for Nathan in my plist,
// Here, I want to add two more items to the key Nathan by appending an arry to
// NSMutableDictionary (dictionaryRecords) but it doesnt work
NSArray *tempArray = #[#"NoteBook", #"Pencil"];
[dictionaryRecords setObject:tempArray forKey:#"Nathan"];
[dictionaryRecords writeToFile:fullPath atomically:YES];
// when I check the plist after saving this, I only see the last two values, NoteBook and Pencil
// instead of the three that I'm expecting, Ruler, NoteBook and Pencil
}
}
What am I missing here?
Thanks a lot in advance.
[dictionaryRecords setObject:tempArray forKey:#"Nathan"] replaces whatever the previous value was with tempArray.
If you want to add to the existing array, you have to retrieve it, make a mutable copy, and append to it.
Here is how I made it work. I hope I didn't over complicated things here but for some reason I had to use two arrays, an NSArray and an NSMutableArray, I would think that the NSMutableArray would be enough but it didn't work.
- (IBAction)testing:(id)sender
{
// get directory path
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [dirPaths objectAtIndex:0];
// create plist
NSString *fullPath = [documentsPath stringByAppendingPathComponent:#"studentsRecords.plist"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullPath];
// if file exists create dictionary
if(fileExists)
{
// add items from plist to dictionary
NSDictionary *dictionaryExistingRecords = [[NSDictionary alloc]initWithContentsOfFile:fullPath];
// make a copy if dictinary with existing items
NSMutableDictionary *dictionaryNewRecords = [dictionaryExistingRecords mutableCopy];
// array to hold existing values from a spcecified key
NSArray *arrayWithExistingKey = [dictionaryExistingRecords objectForKey:#"Nathan"];
// mutable array to add existing and be able to insert new items
NSMutableArray *arrayOldAndNewItems = [NSMutableArray arrayWithArray:arrayWithExistingKey];
// insert new items to array
[arrayOldAndNewItems addObject:#"Snow Pans"];
// add old and new items to specified key
[dictionaryNewRecords setObject:arrayOldAndNewItems forKey:#"Nathan"];
// save new records to plist
[dictionaryNewRecords writeToFile:fullPath atomically:YES];
}
}
#end

Remove items from array to ensure no duplicate strings [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I need to be able to pull 100's of strings from a plist and display them in a label within my project. I have managed to get this to work pulling data from two different arrays within my plist and then generating a random string to display.
What i want to do now is ensure that no two strings are displayed twice in one session and also be able to set a counter system up that after 5 goes it displays a message.
I was thinking of doing a simple counter for the display message after x amount of turns however when it comes to not displaying duplicates from the array im a little lost. I need it to only basically remove an item from the array (not the plist every time the user presses the button)
- (IBAction)truth:(id)sender {
NSString *path = [[NSBundle mainBundle] pathForResource:
#"test" ofType:#"plist"];
NSDictionary *plistDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSMutableArray *plistArray = plistDict[#"truth"];
NSDictionary *plistDict2 = [[NSDictionary alloc] initWithContentsOfFile:path];
NSMutableArray *plistArray2 = plistDict2[#"dare"];
plistArray = [plistArray arrayByAddingObjectsFromArray:plistArray2];
NSLog(#"%#", plistArray);
int randV = arc4random() % plistArray.count;
self.joke.text = plistArray[randV];
NSLog(#"dictionary: %#, array: %#", plistDict, plistArray);
}
The problem you have is that every time you are getting a message, you are reading the plists again and recreating plistArray
The correct way would be to save plistArray into a local variable (a property) and only populate it the first time.
Then, if you want to remove an item, you call `[plistArray removeObjectAtIndex: randV];
Edit: code
// In .h file
#property (strong, nonatomic) NSMutableArray * plistArray;
- (IBAction)truth:(id)sender {
if (!self.plistArray) {
NSString *path = [[NSBundle mainBundle] pathForResource:
#"test" ofType:#"plist"];
NSDictionary *plistDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray * plistArray1 = plistDict[#"truth"];
NSDictionary *plistDict2 = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *plistArray2 = plistDict2[#"dare"];
self.plistArray = [[plistArray1 arrayByAddingObjectsFromArray:plistArray2] mutableCopy];
}
NSLog(#"%#", plistArray);
int randV = arc4random() % self.plistArray.count;
self.joke.text = self.plistArray[randV];
[self.plistArray removeObjectAtIndex:randV];
NSLog(#"dictionary: %#, array: %#", plistDict, self.plistArray);
}
#property
Create an NSMutableSet from plistArray and each time you retrieve one remove it from the set. Presto!
EDIT: Add code
NSMutableSet *jokes = [[NSMutableSet alloc] initWithArray:plistArray];
NSString *joke = [jokes anyObject];
[jokes removeObject:joke];
Disclaimer: As the documentation says, anyObject is not guaranteed to be random. I believe you can work something out for yourself here. E.g.
NSString *joke = [[jokes allObjects] objectAtIndex:arc4random() % [jokes count]];
[plistArray removeObjectAtIndex: randV];

Create a new plist from the existing plist?

I have a plist like in the below first image ,thats my main plist and in that the field "bid" is unique . i have the value of bid (for example bid=90) then i need to search the value in all entries of my main (first image)plist and create a new plist& retrieve the corresponding values(bid,description,category,rating) belongs to that plist .How can i do this?
EDIT
if i have multiple values with me like bid =88 and another bid=90 then i have to create a new plist from the main plist (as shown in first image) then the result should be like image 3
NSString *path = [[NSBundle mainBundle] pathForResource:#"mainPlistFile" ofType:#"plist"];
//path has path for main plist.
NSDictionary *dictionary=[NSDictionary dictionaryWithContentOfFile: path];
NSArray *listbook = [dictionary objectForKey:#"listbook"];
for (NSDictionary *item in listbook) {
if ([[item objectForKey:#"bid"] isEqualToString yourNSStringWithBidToSearch]) {
//now item is the desired dictionary, you can get values by using objectForKey
//or write it to a file like below.
[item writeToFile:pathToSaveNewPlist automatically:NO];
break;
}
}
Remember you will have to first create the NSString pathToSaveNewPlist and yourNSStringWithBidToSearch, I'll leave that to you.
So you edited the question :
Lets assume you have an NSArray bidsToSearch which has NSStrings of the bid values to be searched :
NSMutableArray *targetListbook = [[NSMutableArrray alloc] init]];
NSDictionary *dictionary=[NSDictionary dictionaryWithContentOfFile:[[NSBundle mainBundle] pathForResource:#"mainPlistFile" ofType:#"plist"]];
for (NSDictionary *item in [dictionary objectForKey:#"listbook"])
if ([bidsToSearch containsObject:[item objectForKey:#"bid"]])
[targetListbook addObject:item];
[#{listbook:targetListbook} writeToFile:pathToSaveNewPlist automatically:NO];
You can use NSPropertyListSerialization class to serialize and deserialize property lists.
EDIT: below is some pseudo code for how to do it.
NSData* data = [NSData dataWithContentsOfFile:plistFile]; // read file
id plist = [NSPropertyListSerialization propertyListWithData:data ...]; // deserialize
// do your work
data = [NSPropertyListSerialization dataWithPropertyList:plist2 ...]; // serialize
[data writeToFile:plistFile2 ...]; // write to file

How to add and get the values from .plist in iOS

I am implementing a application based on web services. In that I need to add a string as property in .plist and I need to get the value from the .plist whenever I need in the code.
Here is a code sample:
NSString *path = [[NSBundle mainBundle] pathForResource: #"YourPLIST" ofType: #"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: path];
id obj = [dict objectForKey: #"YourKey"];
NSBundle* mainBundle = [NSBundle mainBundle]; 
 
// Reads the value of the custom key I added to the Info.plist
NSString *value = [mainBundle objectForInfoDictionaryKey:#"key"];
//Log the value
NSLog(#"Value = %#", value);
// Get the value for the "Bundle version" from the Info.plist
[mainBundle objectForInfoDictionaryKey:#"CFBundleVersion"];
// Get the bundle identifier
[mainBundle bundleIdentifier];
NSURL *url = [[NSBundle mainBundle] URLForResource:#"YOURPLIST" withExtension:#"plist"];
NSArray *playDictionariesArray = [[NSArray alloc ] initWithContentsOfURL:url];
NSLog(#"Here is the Dict %#",playDictionariesArray);
or you can use following also
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Sample.plist"];
Get from plist is very simple.
NSString *path = [[NSBundle mainBundle] pathForResource:#"SaveTags" ofType:#"plist"];
if (path) {
NSDictionary *root = [NSDictionary dictionaryWithContentsOfFile:path];
}
If you want to add something to a plist, maybe you can find the answer here:
How to write data in plist?
But if you only want save some message in you app, NSUserDefaults is the better way.
You can not do this. Any Bundle wether it is iOS or Mac OS is readonly, you can only read to it and you can't create files, write or do anything with the files in a bundle. This is part of the Security features of apple. You can use the NSDocumentsDirectory to writr and read your stuff you need for your app
Swift
I know this was asked 12+ years ago. But this was the first SO question to come up via google. So to save time for everyone, here is how to do this in swift:
struct Config {
// option 1
static var apiRootURL: String {
guard let value = (Bundle.main.object(forInfoDictionaryKey: "BASE_URL") as? String), !value.isEmpty else {
fatalError("Base URL not found in PLIST")
}
return value
}
// option 2
static var databaseName: String {
guard let value = (Bundle.main.infoDictionary?["DB_NAME"] as? String), !value.isEmpty else {
fatalError("DB NAME not found in PLIST")
}
return value
}
}
Notice the 2 functions use slightly diffrent methods to access the plist. But in effect they are almost the same.
In theory there might not be a plist. Hence infoDictionary is optional. But in this case the first method will also return an unexpected value, resulting in an error.
One actual difference as noted by Apple:
Refering to Bundle.main.object(forInfoDictionaryKey: "BASE_URL")
Use of this method is preferred over other access methods because it returns the localized value of a key when one is available.

Resources