access to plist in app bundle returns null - objective-c

I'm using a piece of code I've used before with success to load a plist into an array:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"species_names" ofType:#"plist"];
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
The initWithContentsOf File is failing (array is null, suggesting it can't find the plist in the app bundle).
Under the iPhone simulator I've checked that the path created by the first line points to the app file (it does), and used the Finder context menu "Show package contants" to prove to myself that "species_name.plist" is actually in the bundle--with a length that suggests it contains stuff, so it should work (and has in other iOS apps I've written). Suggestions most welcome...
[env Xcode 4.2 beta, iOS 4.3.2].

You should use initWithContentsOfFile: method of NSDictionary instance, not NSArray.
Here is sample:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"species_names" ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
NSArray *array = [NSArray arrayWithArray:[dict objectForKey:#"Root"]];
Or this:
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSString *path = [[NSBundle mainBundle] pathForResource:#"startups" ofType:#"plist"];
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:path];
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];
NSArray *array = [NSArray arrayWithArray:[temp objectForKey:#"Root"]];
EDIT
You can use initWithContentsOfFile: method of NSArray with file created with writeToFile:atomically: method of NSArray. Plist created with writeToFile:atomically: has this stucture:
<plist version="1.0">
<array>
...Content...
</array>
</plist>
And Plist created in XCode has this stucture:
<plist version="1.0">
<dict>
...Content...
</dict>
</plist>
Thats why initWithContentsOfFile: method of NSArray dosnt work.

plist of this type:
OR (basically both are same)
Can be read like this:
NSString *file = [[NSBundle mainBundle] pathForResource:#"appFeatures" ofType:#"plist"];
NSArray *_arrFeats = [NSArray arrayWithContentsOfFile:file];
NSLog(#"%d", _arrFeats.count);
Where appFeatures is name of the plist.
(iOS 6.1)

Try this simple metod:
- (void)viewDidLoad
{
[super viewDidLoad];
[self plistData];
}
- (void)plistData
{
NSString *file = [[NSBundle mainBundle] pathForResource:#"appFeatures" ofType:#"plist"];
NSArray *messages = [NSArray arrayWithContentsOfFile:file];
NSLog(#"%d", messages.count);
for (int i=0; i<messages.count; i++)
{
NSString *data = [messages objectAtIndex:i];
NSLog(#"\n Data = %#",data);
}
}

Related

iOS 9 Error write .plist

My App errors after update to iOS 9. Can't write .plist file in project.
-(BOOL)writeDictionaryToPlist:(NSDictionary*)plistDict {
NSString *path = [[NSBundle mainBundle] pathForResource:#"History" ofType:#"plist"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValuesForKeysWithDictionary:plistDict];
NSDictionary *saveDict = [NSDictionary dictionaryWithDictionary:dict];
BOOL result = [saveDict writeToFile:path atomically:YES];
return result;
}
result is NO.
But iOS 7 and iOS 8 is works fine.
How I can fix it ?
Use dictionaryWithContentsOfFile: to write to dictionary from plist
NSString *path = [[NSBundle mainBundle] pathForResource:#"History" ofType:#"plist"];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
BOOL result = [dict writeToFile:path atomically:YES];
NOTE since plist is the app resource you cant write to that file, hence create a plist file in your documents directory and write to that file.
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
path = [path stringByAppendingString:#"/History.plist"];
BOOL result = [dict writeToFile:path atomically:YES];
Retrieve the data from plist
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
UPDATE
Add NSAppTransportSecurity as a dictionary and the first child should be a bool named NSAllowsArbitraryLoads set to YES.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

Mac OSX How can I add plist to bundle and modify the plist

I have a plist and I copy the plist to my xcode project but it seems like the file is not in bundle:
NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
nslog (#"dict %#",dict);
but the file plist file is not showing. Question, how can I add the file to project in a way to see in the bundle? also any of you knows how can I modify the plist and save the changes?
I'll really appreciated your help
P.S. I'm using Xcode 5.
You can create a plist file in your bundle and modify its contents as:
NSString *bundlePath = [[NSBundle mainBundle]bundlePath]; //Path of your bundle
NSString *path = [bundlePath stringByAppendingPathComponent:#"MyPlist.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableDictionary *data;
if ([fileManager fileExistsAtPath: path])
{
data = [[NSMutableDictionary alloc] initWithContentsOfFile: path]; // if file exist at path initialise your dictionary with its data
}
else
{
// If the file doesn’t exist, create an empty dictionary
data = [[NSMutableDictionary alloc] init];
}
//To insert the data into the plist
int value = 5;
[data setObject:[NSNumber numberWithInt:value] forKey:#"value"];
[data writeToFile: path atomically:YES];
[data release];
//To retrieve the data from the plist
NSMutableDictionary *savedData = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
int savedValue;
savedValue = [[savedData objectForKey:#"value"] intValue];
NSLog(#"%i",savedValue);
infoDictionary will return Info.plist file.
You should use pathForResource:ofType: method of NSBundle class.
NSString *commonDictionaryPath;
if (commonDictionaryPath = [[NSBundle mainBundle] pathForResource:#"CommonDictionary" ofType:#"plist"]) {
theDictionary = [[NSDictionary alloc] initWithContentsOfFile:commonDictionaryPath];
}
How can I add plist to bundle
Drag your plist to "Copy Bundle Resources" phase of your build target
modify the plist
Copy the plist to your app's Documents folder and modify it there.
this should work
NSString*pListDictPath = [[NSBundle mainBundle] pathForResource:#"yourPlistName" ofType:#"plist" inDirectory:#"YourDirectory"];
if()
{
NSDictionary *theDictionary = [[NSDictionary alloc] initWithContentsOfFile:pListDictPath ];
}

Objective-C: writing values to a specific index of an Array inside a .plist

I understand I can for instance write a value to a .plist file as such
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"stored" ofType:#"plist"];
NSString *comment = #"this is a comment";
[comment writeToFile:filePath atomically:YES];
But If i had for say an Array inside my .plist (gameArray) and I'd like to white comment into a particular index of my array i.e. gameArray[4] ; how would I do this ?
allow me to clarify
I have a plist: stored.plist
inside my plist there is an array gameArray
i would like to update specific indexes of gameArray inside the plist
is this possible ?
You cannot update and save data in application's main bundle instead you have to do in document directory or other directory like this:
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistFilePath = [documentsDirectory stringByAppendingPathComponent:#"stored.plist"];
if([[NSFileManager defaultManager] fileExistsAtPAth:plistFilePath])
{//already exits
NSMutableArray *data = [NSMutableArray arrayWithContentsOfFile:plistFilePath];
//update your array here
NSString *comment = #"this is a comment";
[data replaceObjectAtIndex:4 withObject:comment];
//write file here
[data writeToFile:plistFilePath atomically:YES];
}
else{ //firstly take content from plist and then write file document directory
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"stored" ofType:#"plist"];
NSMutableArray *data = [NSMutableArray arrayWithContentsOfFile:plistPath];
//update your array here
NSString *comment = #"this is a comment";
[data replaceObjectAtIndex:4 withObject:comment];
//write file here
[data writeToFile:plistFilePath atomically:YES];
}
Assuming the contents of 'stored.plist' is an array, you need to instantiate a mutable array from the path:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"stored" ofType:#"plist"];
NSMutableArray *array = [NSMutableArray arrayWithContentsOfFile:filePath];
NSString *comment = #"this is a comment";
// inserting a new object:
[array insertObject:comment atIndex:4];
// replacing an existing object:
// classic obj-c syntax
[array replaceObjectAtIndex:4 withObject:4];
// obj-c literal syntax:
array[4] = comment;
// Cannot save to plist inside your document bundle.
// Save a copy inside ~/Library/Application Support
NSURL *documentsURL = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] objectAtIndex:0];
NSURL *arrayURL = [documentsURL URLByAppendingPathComponent:[filePath lastPathComponent]];
[array writeToURL:arrayURL atomically:NO];

Plist cannot be read ... What am I doing wrong?

I have got a plist in my Resources folder called "levelconfig.plist" and I want to read something out of it.
I did the following :
-(NSString *) dataFilePath
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [path objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:#"levelconfig.plist"];
}
-(void) readPlist{
NSString *filePath = [self dataFilePath];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath]){
NSArray *array = [[NSArray alloc]initWithContentsOfFile:filePath];
NSLog(#"%#",array);
NSLog(#"%#", filePath);
}
}
And in the ccTouchesBegan method I call :
[self readPlist];
My plist contains an array, that should be displayed right ? Is it a good idea to store level data in a .plist file ?
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>Root</key>
<array>
<string>sunday</string>
<string>monday</string>
<integer>44</integer>
</array>
</dict>
</plist>
You must first read the dictionary as specified in your plist. For example:
NSString *mainPath = [[NSBundle mainBundle] bundlePath];
NSString *itemPositionPlistLocation = [mainPath stringByAppendingPathComponent:#"test.plist"];
NSDictionary * itemPositions = [[NSDictionary alloc] initWithContentsOfFile:itemPositionPlistLocation];
NSArray * items = [itemPositions objectForKey:#"Root"];
NSLog(#"%#", items);
I'm not sure what you are doing with your other code but two lines suffice to load the file. Please excuse the variable names; I quickly adapted this from my current project. Also, make sure you release the Array and Dictionary in dealloc or wherever appropriate.
Are you sure it's in your resources folder? In that case you aren't building the path properly. You will probably want to use [NSBundle pathForResource:] (docs)
Your pList file is a Dictionary that has an array in the Root key and you are reading it in to an Array.
-(void) readPlist{
NSString *filePath = [self dataFilePath];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath]){
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
NSArray *array = [[NSArray alloc]initWithArray:[dict objectForKey:#"Root"]];
NSLog(#"%#",array);
NSLog(#"%#", filePath);
[dict release];
[array release];
}
}

Plist Array to NSDictionary

I have a plist:
<plist version="1.0">
<array>
<dict>
<key>name</key>
<string>Alabama</string>
<key>abreviation</key>
<string>AL</string>
<key>date</key>
<string>1819</string>
<key>population</key>
<string>4,627,851</string>
<key>capital</key>
<string>Montgomery</string>
<key>largestCity</key>
<string>Birmingham</string>
</dict>
<dict>
<key>name</key>
<string>Alaska</string>
<key>abreviation</key>
<string>AK</string>
<key>date</key>
<string>1959</string>
<key>population</key>
<string>683,478</string>
<key>capital</key>
<string>Juneau</string>
<key>largestCity</key>
<string>Anchorage</string>
</dict>
...
</array>
</plist>
I am trying to load it into an NSDictionary like this:
NSString *path = [[NSBundle mainBundle] pathForResource:#"stateInfo" ofType:#"plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:path]) {
NSLog(#"The file exists");
} else {
NSLog(#"The file does not exist");
}
NSMutableDictionary *myDic = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
//NSDictionary *myDic = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(#"The count: %i", [myDic count]);
NSArray *thisArray = [[NSArray alloc] initWithContentsOfFile:path];
NSLog(#"The array count: %i", [thisArray count]);
I always get an array count of 50 but a dictionary count of zero. So I tried looping through the array and adding it to the dictionary:
NSDictionary *eachState;
for (eachState in thisArray) {
State *thisState = [[State alloc] initWithDictionary:eachState];
[myDic setObject:thisState forKey:thisState.name];
}
But the loop is throwing an exception:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '*** -[NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
State is a class with properties matching my plist. What am I doing wrong? I see all kinds of related questions out here but I can't get it.
The two problems:
Loading the plist into an NSDictionary:
This is a simple problem, which it seems you have already figured out. The global object in your plist is an array, not a dict, so when you load it into the dictionary, it doesn't know what to do (incompatable types), so you're getting an empty dictionary.
Looping through the array of dictionaries:
From the Exception you're getting, you are calling 'setObject:forKey:' on the dictionary, which is initialized as an NSDictionary, not an NSMutableDictionary. The pointer is typed as NSMutableDictionary, but not the actual in memory object. You need to change your line from.
NSMutableDictionary *myDic = [[NSDictionary alloc] initWithContentsOfFile:path];
to
NSMutableDictionary *myDic = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
and actually, since loading the dictionary from the file gives you an empty dictionary, you are wasting cycles trying to load it from the file, and should just create a new one:
NSMutableDictionary *myDic = [[NSMutableDictionary alloc] init];
A more flexible way to load plists into memory, that also allows you to create mutable plists:
NSData* data = [NSData dataWithContentsOfFile:path];
NSMutableArray* plist = [NSPropertyListSerialization propertyListFromData:data
mutabilityOption:NSPropertyListImmutable
format:NSPropertyListXMLFormat_v1_0
errorDescription:NULL];
Thus your code could be implemented as:
NSString *path = [[NSBundle mainBundle] pathForResource:#"stateInfo" 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");
}