How to save an array of dictionary to plist? [duplicate] - ios

This question already has answers here:
plist writing data inside dictionary
(3 answers)
Closed 10 years ago.
Plist just wont save save itself...
I don't understand what I'm doing wrong here. Please help.
NSString *factorsFile = [[NSBundle mainBundle]pathForResource:#"factors" ofType:#"plist"];
NSMutableArray *factorsArray = [NSMutableArray arrayWithContentsOfFile:factorsFile];
NSMutableDictionary *dictFactor = [NSMutableDictionary dictionary];
[dictFactor setObject:self.nameField.text forKey:#"Factor"];
[dictFactor setObject:self.rankField.text forKey:#"Rank"];
NSLog(#"%#",dictFactor);
[factorsArray addObject:dictFactor];
NSLog(#"%#",factorsArray);
if([factorsArray writeToFile:factorsFile atomically:YES]){
NSLog(#"lalal");
}
This code is able to go inside the last if loop but the plist remains unchanged... Why?

Because you can't write/modify files in the app bundle.
You can, however, perform a Google search for the expression "ios plist write to bundle unchanged", and you can have a look at the first four results. They're all identical/duplicate StackOverflow-questions:
plist writing data inside dictionary
write into plist file using NSDictionary object
Not Able to write Boolean in plist file
writeToFile fails on iphone but works on simulator

Related

Objective-C Access Specific Item in Plist

I have created an app, and I am trying to access a URL that is stored in a plist file. At this state I am just trying to log the contents out. I am aware similar questions have been asked before, but I am asking specifically to my scenario how to I access Item 0. I am trying to access Item 0 inside InternalViaSafari this manifests itself inside the URLValidator and then that inside the Root. The code I have so far is:
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"plist-file-name" ofType:#"plist"];
NSDictionary* plist = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSString* name = [plist valueForKeyPath:#"URLValidator.InternalViaSafari"];
NSLog(name);
You cannot use keyPath like that for this as far as I know. InternalViaSafari is not a property of URLValidator dictionary. What's more InternalViaSafari is an Array not a String in your plist.
In order to get this string you'd need something like this :
NSArray *internalViaSafari = plist[#"URLValidator"][#"InternalViaSafari"];
NSString *name = internalViaSafari.firstObject;
What happens here, is that you get the value under the URLValidator key from your plist dictionary. This value is also a Dictionary (this can be clearly seen in the plist screenshot you shared), so you get the value under the InternalViaSafari key. This value is in turn an Array, which has Strings as its elements. In this example I extracted the first entry from this array.

how to get INFOPLIST_FILE name iOS

Each target in my project has to has different info.plist. I want to get the name programmatically from BuildSettings> Packaging> Info.plist file but somehow I cant really retrieve the plist list filename. Is there a way i can retrieve the plist programmatically?
NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleInfoPlistURL")
only gives me
Info.plist -- file:///Users/XXXX/Library/Developer/CoreSimulator/Devices/XXXXXX-9B37-4C3D-88E9-XXXXX62B470/data/Containers/Bundle/Application/7XXXXXXXB-BE36-4D3E-9C90-XXXXX/PROJECT%20KOL.app/
Actually NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleInfoPlistURL") returns an NSURL object. Extract the absoluteString from it which will give you the name of your plist file. Check the code below. Sorry for giving answer in Objective-C as I don't know Swift
NSDictionary *infoDict = [NSBundle mainBundle].infoDictionary;
NSString *plistFilePath = [[infoDict objectForKey:#"CFBundleInfoPlistURL"] absoluteString];
NSString *plistFileName = [[plistFilePath componentsSeparatedByString:#"/"] lastObject];
plistFileName is the required file name (with extension .plist)

Why can't I add an object to a plist [duplicate]

This question already has an answer here:
Writing into plists in iOS 5.0.1
(1 answer)
Closed 7 years ago.
I'm trying to add an object to a plist whenever the user hits the star. I'm having problems displaying the objects in another table view, and I strongly suspect the promblem is in this method.
-(IBAction)favbutton
{
NSString *path = [[NSBundle mainBundle] pathForResource: #"Favorites" ofType:#"plist"];
// Build the array from the plist ------>>>
NSDictionary *favs = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
favMutArray = [favs objectForKey:#"Name"];
[favMutArray addObject:stateName];
[favMutArray writeToFile:path atomically:YES];
NSLog(#"StateButtonFinished");
}
The button is conntected(from seeing NSLog), stateName is an NSString, and Favorites.plist contains am root with Name, Phone, Fax, and Address arrays. If you need more information I can provide it. Thank you!
If you are getting the plist via NSBundle then it's part of the app bundle and that cannot be written to.
You will need to copy the plist to the app's document folder and update it from there, much like developers have to do with databases that are "seeded" from a copy held in the app bundle.

Edit Programmatically plist in the App bundle at development time

I have created a plist on XCode that will have a few values that I can't insert manually. So I want to add this values programmatically at development time. But it seems that I can only read the plist I can not save a plist that is on the App bundle, which makes sense at runtime.. When I will distribute my app I want everyone to have this plist file that's why I am not saving on documents or cache. How can I achieve what I want?
From http://www.karelia.com/cocoa_legacy/Foundation_Categories/NSFileManager__Get_.m (pasted below) you can build a path within the user's personal library with the -(NSString *) pathFromUserLibraryPath:(NSString *)inSubPath method found there.
For example, NSString *editedPlist = [self pathFromUserLibraryPath:#"my.plist"]; gets you the name of the modified plist within the user's library (even if that plist doesn't exist yet).
How you read/write it is according to what kind of plist you have, but you could read it into a dictionary with:
NSMutableDictionary *thePlist= [[NSMutableDictionary alloc] initWithContentsOfFile:editedPlist ];
If you are unable to read, easily detected by, for example [thePlist count] == 0, then you would instead call the same initWithContentsOfFile: initializer with a path to the template within your bundle, but you would then write the plist out to the editedPlist path so it appears in the user directory.
Here is the utility method I referenced above:
/*
NSFileManager: Get the path within the user's Library directory
Original Source: <http://cocoa.karelia.com/Foundation_Categories/NSFileManager__Get_.m>
(See copyright notice at <http://cocoa.karelia.com>)
*/
/*" Return the path in the user library path of the given sub-path. In other words, if given inSubPath is "foo", the path returned will be /Users/myUser/Library/foo
"*/
- (NSString *) pathFromUserLibraryPath:(NSString *)inSubPath
{
NSArray *domains = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES);
NSString *baseDir= [domains objectAtIndex:0];
NSString *result = [baseDir stringByAppendingPathComponent:inSubPath];
return result;
}
What I would suggest is writing code that checks for the plist in the documents directory at start. If it's there, read it into memory.
If you don't find the file in the documents directory, read it from the app bundle instead. Then drop into the code that uses it from memory and writes the changed version to the documents directory.
Remember that all the objects you read from a plist file are read as immutable, even if you wrote mutable objects into the file. You have to write code that makes mutable copies of anything that you want to change. (And have to implement a mutable deep copy if you have complex structures like arrays of dictionaries that in turn contain arrays of strings.)

Can a plist file be too big?

I'm working on a reference app that loads a plist file that can be searched.
The plist file is about 6kb with about 600 entries.
I have been working with the simulator and all works ok, however when I load it onto a device none of the data is there. Just an empty table.
I think the file may be too big because I can load a plist with 20 entries and it loads fine on the device.
If the file was too large wouldn't the whole app just crash?
Does anyone have any suggestions?
This is how I load my plist file
NSString* myFile = [[NSBundle mainBundle] pathForResource:#"myPlistFile" ofType:#"plist"];
array = [[NSMutableArray alloc] initWithContentsOfFile:myFile];
What it sounds like to me is that either your plist isn't even getting copied at all onto the device, or you're using case-sensitive file naming.
First see if the file exists at all:
NSString *myFilePath = [[NSBundle mainBundle] pathForResource:#"myPlistFile" ofType:#"plist"];
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:myFilePath];
Check that boolean (using the debugger or by logging, either way). If the file exists, then I suspect that you're using a wrong case when trying to access the filename. Filenames are case-sensitive on iOS devices, unlike the Simulator. For example, lets say your plist was named myAwesomeStuff.plist. If you tried to access myawesomestuff.plist on the Simulator, it would work just fine. Not so on the device. Make sure you are using the correct case on your file names.

Resources