modifying a plist is not working - iphone

i have to modify a BOOL value in my plist file stored with the bundle.i am able to access the dictionary which i have to modify .from nslogging i can see that dict is updated with the new value ,but the problem is when i check the plist in bundle it is not being modified.any clue on why it is not updating the plist
NSString* plistPath = nil;
NSFileManager* manager = [NSFileManager defaultManager];
if (plistPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"TopicsList.plist"])
{
if ([manager isWritableFileAtPath:plistPath])
{
NSMutableArray* dictarrays = [NSMutableArray arrayWithContentsOfFile:plistPath];
NSMutableDictionary *dict=[dictarrays objectAtIndex:indexPath.row];
NSLog(#"%# ",dict );
[dict setObject:[NSNumber numberWithBool:YES] forKey:#"isPaid"];
NSLog(#"%# ",dict );
[dict writeToFile:plistPath atomically:NO];
NSLog(#"%# ",dict );
[manager setAttributes:[NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate] ofItemAtPath:[[NSBundle mainBundle] bundlePath] error:&error];
}
}

Is the plist a part of your resources? Not sure if we can edit a plist there. Copy the plist to your app's Documents folder and update it there.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"TopicsList.plist"];
success = [fileManager fileExistsAtPath:plistPath];
if(!success){
//file does not exist. So look into mainBundle
NSString *defaultPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"TopicsList.plist"];
success = [fileManager copyItemAtPath:defaultPath toPath:plistPath error:&error];
}
Now whatever changes you need to make to the plist or read data from the plist, read it from the copy in Documents folder.

You must store your plist in the documents directory. After that, you must also save the plist upon leaving the app. Otherwise, the plist is in the main bundle and cannot be modified.

Is there any error
Check with
NSError *error = nil
[dict writeToFile:plistPath atomically:YES encoding:NSASCIIStringEncoding error:&error];
if (error) {
NSLog(#"Error: %#", [error description]);
} else {
NSLog(#"Success");
}

Related

Creating a plist if one doesn't exist

I've got a bit of code that imports settings into my app from an email, but it only works if the plist it imports the settings into already exists.
This is the code I'm using currently to import the settings and write to the plist.
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
if (url){
NSDictionary *openedDictionary = [NSDictionary dictionaryWithContentsOfURL:url];
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"Data.plist"];
NSDictionary *originalDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSMutableDictionary *newDictionary = [originalDictionary mutableCopy];
for (NSString *key in openedDictionary) {
if (!newDictionary[key]) {
newDictionary[key] = openedDictionary[key];
}
}
[newDictionary writeToFile:plistPath atomically:YES];
}
NSError *error = nil;
if (![[NSFileManager defaultManager] removeItemAtURL:url error:&error]) {
NSLog(#"error while deleting: %#", [error localizedDescription]);
}
return YES;
}
But what I need to do is create that Data.plist if it's not there, or alternatively rename the plist that's emailed to Data.plist and store it provided there isn't already a Data.plist.
Here is how I would create the property list through code and retrieve data from it as well.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"plist.plist"]; NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
path = [documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat: #"plist.plist"] ];
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableDictionary *data;
if ([fileManager fileExistsAtPath: path])
{
data = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
}
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 *savedStock = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
int value1;
value1 = [[savedStock objectForKey:#"value"] intValue];
NSLog(#"%i",value1);
[savedStock release];
NSFileManager *defaultManager = [NSFileManager defaultManager];
if ([defaultManager fileExistsAtPath:plistPath])
{
// rename the file
}
else
{
//create empty file
}
this is how you can create empty plist file
NSDictionary *dict = [NSDictionary dictionary];
//you can create file in any path in app sandbox, for example I'm creating in document dir.
NSString *FilePathWithName = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
FilePathWithName = [FilePathWithName stringByAppendingPathComponent:#"name.plist"];
[dict writeToFile:FilePathWithName atomically:YES];
and for renaming what you can do is, you can load the .plist/.property file (that you want to rename) as dictionary and write that Dictionary with new name (write plist as above) and delete the .plist/.property file.
NSFileManager *defaultManager = [NSFileManager defaultManager];
if ([defaultManager fileExistsAtPath:plistPath]) {
//Do stuff with path
}
else {
//Write a plist to the path
}

How can I overwrite the contents of a plist file? (iPhone / iPad)

I have a plist file in my main app bundle that I want to update via my app. Here is the code I'm using, the problem is that the plist doesn't seem to be getting updated. Is my code incorrect or is there another issue?
// Data
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
[data setObject:self.someValue forKey:#"Root"];
// Save the logs
NSString *filepath = [[NSBundle mainBundle] pathForResource:#"MyFile" ofType:#"plist"];
[data writeToFile:filepath atomically:YES];
Please can someone help me out?
IOS restricts writing to bundled files. If you want a writable plist, you need to copy it to your app's Documents folder and write to it there.
Here's how I'm doing it in one of my apps
//get file paths
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *documentPlistPath = [documentsDirectory stringByAppendingPathComponent:#"document.plist"];
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *bundlePlistPath = [bundlePath stringByAppendingPathComponent:#"bundle.plist"];
//if file exists in the documents directory, get it
if([fileManager fileExistsAtPath:documentPlistPath]){
NSMutableDictionary *documentDict = [NSMutableDictionary dictionaryWithContentsOfFile:documentPlistPath];
return documentDict;
}
//if file does not exist, create it from existing plist
else {
NSError *error;
BOOL success = [fileManager copyItemAtPath:bundlePlistPath toPath:documentPlistPath error:&error];
if (success) {
NSMutableDictionary *documentDict = [NSMutableDictionary dictionaryWithContentsOfFile:documentPlistPath];
return documentDict;
}
return nil;
}
Hope this helps

How to read & write to an NSArray to plist?

I'm having several issues based around reading and writing an NSArray to and from a plist.
I have created a plist file in the 'Supporting Files' folder which I want to use to initialise the app data with upon the first load.
Here is what my plist looks like:
I then use this code to try load the plist into the app:
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
filePath = [documentsDirectory stringByAppendingPathComponent:kDataFile];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath:filePath error:&error];
}
I then try to load the data from the plist file like so, however nothing seems to be displayed.
NSMutableDictionary *savedData = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
NSMutableArray *myNSArray = [[savedData objectForKey:#"KEY_Level_1"] mutableCopy];
savedData = nil;
Sorry if this is a simple task, however I've been looking at lots of tutorials and trying to work out how to do this with no luck. I'm getting really frustrated now - I would have thought it should be a simple thing to do.
NOTE: My NSArray will contain a whole bunch of NSDictionaries.
You need to check the return value of copyItemAtPath:toPath:error: and at least log the error if the method returns false:
if (![fileManager copyItemAtPath:bundle toPath:filePath error:&error]) {
NSLog(#"error: copyItemAtPath:%# toPath:%# error:%#", bundle, filePath, error);
return;
}
-[NSDictionary initWithContentsOfFile:] has no way to report errors, so if it's failing, you cannot easily figure out why. Try reading the file into an NSData and using -[NSPropertyListSerialization propertyListWithData:options:format:error:] to parse it:
NSData *data = [NSData dataWithContentsOfFile:filePath options:0 error:&error];
if (!data) {
NSLog(#"error: could not read %#: %#", filePath, error);
return;
}
NSMutableDictionary *savedData = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainers format:NULL error:&error];
if (!savedData) {
NSLog(#"error: could not parse %#: %#", filePath, error);
return;
}
NSMutableArray *myNSArray = [savedData objectForKey:#"KEY_Level_1"];
savedData = nil;
if (!myNSArray) {
NSLog(#"error: %#: object for KEY_Level_1 missing", filePath);
return;
}
If you do this, you'll be able to more easily see why your data is not being loaded.
UPDATE
On further inspection, it looks like the top-level dictionary in your plist contains the key "Root". The value for "Root" is a dictionary containing the key "KEY_Level_1". So you need to do this:
NSMutableArray *myNSArray = [[savedData objectForKey:#"Root"] objectForKey:#"KEY_Level_1"];

copying plist to document directory

How do I copy an existing plist I have in the app build to the Document directory?
My plist is basically an array of dictionary, I have successfully copy the plist to the directory using the following:
BOOL success;
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"States.plist"];
success = [fileManager fileExistsAtPath:filePath];
if (success) return;
NSString *path = [[NSBundle mainBundle] pathForResource:#"States" ofType:#"plist"];
success = [fileManager copyItemAtPath:path toPath:filePath error:&error];
if (!success) {
NSAssert1(0, #"Failed to copy Plist. Error %#", [error localizedDescription]);
}
However, when I try to access the NSDictionary it gives me:
CoreAnimation: ignoring exception: -[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object
Why is this? I have to relaunch the app again and now I can change the dictionary stored in my array
This is the way I copy the *.plist file to documents folder. Hope this helps.
+ (NSString*) getPlistPath:(NSString*) filename{
//Search for standard documents using NSSearchPathForDirectoriesInDomains
//First Param = Searching the documents directory
//Second Param = Searching the Users directory and not the System
//Expand any tildes and identify home directories.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:filename];
}
+ (void) copyPlistFileToDocument{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dbPath = [Utilities getPlistPath:#"AppStatus.plist"];
if( ![fileManager fileExistsAtPath:dbPath])
{
NSString *defaultDBPath = [[NSBundle mainBundle] pathForResource:#"AppStatus" ofType:#"plist"];
BOOL copyResult = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if(!copyResult)
NSAssert1(0, #"Failed to create writable plist file with message '%#'.", [error localizedDescription]);
}
}
Just create a new pList in the documents directory, and set to the contents of the existing plist you have.

check if plist exist, if not load from here

I am trying to check whether a plist exists in my doc folder: if yes, load it, if not load from resource folder.
- (void)viewWillAppear:(BOOL)animated
{
//to load downloaded file
NSArray *docpaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [docpaths objectAtIndex:0];
NSString *docpath = [documentsDirectory stringByAppendingPathComponent:#"downloadedfile.plist"];
//if document folder got file
if(docpath != nil)
{
NSDictionary *dict = [[NSDictionary alloc]
initWithContentsOfFile:docpath];
self.allNames = dict;
[dict release];
}
//on error it will try to read from disk
else {
NSString *path = [[NSBundle mainBundle] pathForResource:#"resourcefile"
ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc]
initWithContentsOfFile:path];
self.allNames = dict;
[dict release];
}
[table reloadData];
Where did I go wrong? The plist is not loading from resource folder.
I think if you create an instance of NSFileManager you can then use the file exists method
BOOL exists;
NSFileManager *fileManager = [NSFileManager defaultManager];
exists = [fileManager fileExistsAtPath:docPath];
if(exists == NO)
{
// do your thing
}
You need to check wether the file exists in your Documents folder (with NSFileManager or something like this). stringByAppendingPathComponent: doesn’t care wether the path it returns exists or is valid.
I have used a similar approach in one of the applications and it was working fine for me. Where at the launch of the application, I check for the plist file inside the document directory and if it does not exists there, then it is copied from the resource folder.
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSString * plistName = #"FlowerList";
NSString * finalPath = [basePath stringByAppendingPathComponent:
[NSString stringWithFormat: #"%#.plist", plistName]];
NSFileManager * fileManager = [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:finalPath])
{
NSError *error;
NSString * sourcePath = [[NSBundle mainBundle] pathForResource:#"FlowerList" ofType:#"plist"];
[fileManager copyItemAtPath:sourcePath toPath:finalPath error:&error];
}
Here is the Swift version of Chris's Answer:
if (NSFileManager.defaultManager().fileExistsAtPath(path)) {
// ...
}