I am trying to figure out how to save a NSDate value into my plist file thats in my application.
I am currently doing this but am stuck on the actual part where I have to save it.
NSString *datePlistPath = [[NSBundle mainBundle] pathForResource: #"my-Date" ofType: #"plist"];
NSMutableDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: datePlistPath];
// to be saved to plist
NSDate *date = [NSDate date];
// this is where I start to get abit lost, I want to set the date to the right plist value then commit the changes
[dict setObject:date forKey:#"my-Date"];
[dict writeToFile:datePlistPath atomically:YES]; // error happening here.
any help would be appreciated
UPDATE: once it hits the last line of code there this is the error that is generated...
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
Use NSMutableDictionary dictionaryWithContentsOfFile
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile: datePlistPath];
it provide u NSDictionary not NSMutableDictionary if u use NSDictionary dictionaryWithContentsOfFile
Also u cannot update plist in application bundle instead store in Document Directory.
Refer dit-objects-in-array-from-plist link
I think solution in your case is simple like
Replace
NSMutableDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: datePlistPath];
with
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithContentsOfFile: datePlistPath];
NSMutableDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: datePlistPath];
replace NSDictionary with NSMutableDictionary.
You cannot write a file to
NSBundle
. Files can be stored only in Documents directory, temp directory and some pre defined locations. you can try the following code. It worked fine for me.
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0]
stringByAppendingPathComponent:#"file.plist"];
NSDate *currentDate = [NSDate date];
NSMutableDictionary *d = [NSMutableDictionary new];
[d setObject:currentDate forKey:#"my-date"];
[d writeToFile:filePath atomically:YES];
You can't write to files in your own bundle. You don't say exactly what you are using the date for, but if you just want to persist this one NSDate across launches, you probably want to write this date to NSUserDefaults.
Docs here:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/
Your code to write it would just look like:
[[NSUserDefaults standardUserDefaults] setObject:date forKey:#"my-Date"];
and to read it back you would do
NSDate* myDate = (NSDate*)[[NSUserDefaults standardUserDefaults] objectForKey:#"my-Date"];
// Be prepared for a nil value if this has never been set.
Related
I got a very strange bug when I'm trying to read my plist.
My plist looks like :
Root (Array)
Item 0 (Dictionary)
title (String)
I want to display title in the log, so I did the code bellow:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSString *path = [basePath stringByAppendingPathComponent:#"data.plist"];
NSMutableDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path] mutableCopy];
NSLog(#"%#", [dict objectForKey:#"title"]);
NSLog(#"Path to plist: %#", path);
With this code, NSLog(#"%#", [dict objectForKey:#"title"]); is equal to "(null)"...
My plist is in my app's documents folder, and the log of path return the good path to my plist.
Help me please :)
The root of your plist is an array but you read it into a dictionary, thats won't work.
You should:
read the plist into an array
get item 0 (objectAtIndex:0) -> this is a dictionary
on this dictionary you can perform objectForKey ...
As #Tom said, (and as you said yourself in your first code block) the root of the plist is an array.
You can load an array very similarly using:
NSArray *array = [NSArray arrayWithContentsOfFile:path];
Then you can access the item at index 0 (the dictionary) like this:
NSDictionary *dict = [array objectAtIndex:0];
Or since Xcode 4.4 with thew new array literals:
NSDictionary *dict = array[0];
And then log the title as you already tried:
NSLog(#"%#", [dict objectForKey:#"title"]);
Or with the new syntax:
NSLog(#"%#", dict[#"title"]);
I am studying iPhone development and facing a problem with a reading/writing plist file. I followed an example from a iPhone development book but keep getting an error message when running.
The error message says : 2012-04-26 00:21:09.759 FileHandling[5915:207] -[__NSCFDictionary addObject:]: unrecognized selector sent to instance 0x685ac40
Here is the example code (it seems fine to me...though):
NSString *plistFileName = [[self documentPath] stringByAppendingPathComponent: #"Apps.plist"];
NSLog(#"Where is the file? => %#", plistFileName);
if ([[NSFileManager defaultManager] fileExistsAtPath:plistFileName]) {
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plistFileName];
for (NSString *category in dict) {
NSLog(#"%#", category);
NSLog(#"=========");
NSArray *titles = [dict valueForKey:category];
for (NSString *title in titles) {
NSLog(#"%#", title);
}
}
} else {
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Apps" ofType: #"plist"];
NSLog(#"%#", plistPath);
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile: plistPath];
NSLog(#"Let's take a look : %#", dict);
NSMutableDictionary *copyOfDict = [dict mutableCopy];
NSLog(#"Let's look at the mutable dictationary : %#", copyOfDict);
NSArray *categoriesArray = [[copyOfDict allKeys] sortedArrayUsingSelector: #selector(compare:)];
for (NSString *cateogry in categoriesArray) {
NSArray *titles = [dict valueForKey: cateogry];
NSMutableArray *mutableTitles = [titles mutableCopy];
[mutableTitles addObject: #"New App Title"];
[copyOfDict setObject: mutableTitles forKey:cateogry];
}
NSString *fileName = [[self documentPath] stringByAppendingPathComponent: #"Apps.plist"];
[copyOfDict writeToFile: fileName atomically:YES];
}
According to the error message, the problem is occurring in the call to addObject: on an __NSCFDictionary. This means that, at runtime, a dictionary received a message to add an object.
However, in this code snippet, addObject: is apparently being sent to an NSMutableArray. This probably means that each object titles you're retrieving from dict in the last for-loop is not an array, but in fact another dictionary, that your code is simply referring to as an array.
Indeed, your code does seem well-formed, so check the well-formedness of your source plist; open it up in a plain text editor. Also, you use a ton of logging, so confirm this way: in the output, dictionaries (including the root entry) are denoted by {curly = braces}, where arrays are denoted by (round parentheses).
I am having troubles with my class which reads and writes data to a plist. Here is some code:
This first chunk is from my custom class with all my plist read and write methods.
-(NSString *) dataFilePath{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [path objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:#"userInformation.plist"];
}
-(bool)readUserIsMale{
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSDictionary *boolDict = [[NSDictionary alloc] initWithContentsOfFile:[self dataFilePath]];
return [[boolDict objectForKey:#"boolUserIsMale"] boolValue];
}
return nil;
}
-(void)writeUserIsMale:(bool)boolValue{
NSDictionary *boolDict = [[NSDictionary alloc] init];
[boolDict setValue:[NSNumber numberWithBool:boolValue] forKey:#"boolUserIsMale"];
[boolDict writeToFile:[self dataFilePath] atomically:YES];
}
I then in another class where desired import, create and use the class methods:
#import "plistReadWrite.h"
plistReadWrite *readWrite;
If I try and see its value in the console I get (null) return.
NSLog(#"%#",[readWrite readUserIsMale]);
This is of course after I have written some data like so:
[readWrite writeUserIsMale:isUserMale];
isUserMale being a bool value.
Any help would be massively appreciated, if you need anymore info let me know. Thanks.
I think this is mostly correct. In your writeUserIsMale: method you want a mutable dictionary, so you can actually set that key (this should have crashed for you as is, so I'm guessing a copy/paste problem?)
//NSDictionary *boolDict = [[NSDictionary alloc] init];
//should be:
NSMutableDictionary *boolDict = [[NSMutableDictionary alloc] init];
And then when you log the value, remember that bool (or BOOL) are primitives, not objects so:
NSLog (#"%d",[readWrite readUserIsMale]); // Will print 0 or 1
// or if you prefer:
NSLog (#"%#", ([readWrite readUserIsMale]? #"YES":#"NO")); // print YES or NO
Lastly, since this is objective-c, I would probably use BOOL instead of bool.
I'm assuming this is just a simple example, and that you know about NSUserDefaults for this sort of thing.
Hope that helps.
Recently, I'm a senior in high school, and I'm interested in making apps for iPhone. Recently, one of my apps came out: NBlock. It's a puzzle app and it's very challenging. However, it has a few problems. The high scores are not saved. I've been told to use a plist. Any tips?
The URL based method for this:
// Get the URL for the document directory
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSURL *documentDirectoryURL = [[fileManager URLsForDocumentDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0];
// Turn the filename into a string safe for use in a URL
NSString *safeString = [#"scores.plist" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// Create an array for the score
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:[NSNumber numberWithInt:score]];
// Write this array to a URL
NSURL *arrayURL = [NSURL URLWithString:safeString relativeToURL:documentDirectoryURL];
[array writeToURL:arrayURL atomically:YES];
I'd avoid using a plist. The easiest way to save simple data in an application, by far, is NSUserDefaults.
Check out this tutorial for a simple guide on how to use NSUserDefaults. Always be sure to synchronize NSUserDefaults when you're done writing to them.
If you're looking for a more powerful (but more complex) way to save data, check out Apple's guide to using Core Data.
Heres what you want:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"scores.plist"];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:[NSNumber numberWithInt:score]];
[array writeToFile:path atomically:YES];
And to add new scores do initWithContentsOfFile:#"scores.plist" instead of init in the declaration of array. You can optionally use NSUserDefaults.
Take a look into NSKeyedArchiver/Unarchiver. You can save pretty much anything you want; NSUserDefaults, in my experience, dumps your data if you kill your app from the tray. Core data is really used better if you're managing large amounts of data with databases such as sqlite.
I would say the below code will work and pretty straight forward unless custom object data types(Its a different story again) are used:
NSString* plistPath = nil;
NSFileManager* manager = [NSFileManager defaultManager];
if ((plistPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"PathTo.plist"]))
{
if ([manager isWritableFileAtPath:plistPath])
{
NSMutableDictionary* infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
[infoDict setObject:#"foo object" forKey:#"fookey"];
[infoDict writeToFile:plistPath atomically:NO];
[manager setAttributes:[NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate] ofItemAtPath:[[NSBundle mainBundle] bundlePath] error:nil];
}
}
setting the date attribute might be helpful to check when is the last time score was updated.
I have a main dictionary where each entry is a dictionary. I need to save this to a plist and then later retrieve its contents.
This is what I am doing to save the dictionary
// create a dictionary to store a fruit's characteristics
NSMutableDictionary *fruit = [[NSMutableDictionary alloc] init];
[fruit setObject:quantity forKey:#"quantity"];
[fruit setObject:productID forKey:#"productID"];
[fruit setObject:nameID forKey:#"nameID"];
// create a dictionary to store all fruits
NSMutableDictionary *stock = [[NSMutableDictionary alloc] init];
[stock setObject:fruit forKey:#"nameID"];
... after adding all fruits to the stock dictionary, write the stock to a plist
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"stock.plist"];
NSMutableDictionary *stock = [NSMutableDictionary dictionaryWithContentsOfFile:path];
[stock writeToFile:path atomically:YES];
... to restore the dictionary, I use
NSMutableDictionary *stock = [NSMutableDictionary dictionaryWithContentsOfFile:path];
... but this is not saving anything to the file... what am I missing?
thanks for any help.
You write:
... after adding all fruits to the
stock dictionary, write the stock to a
plist
but your code is reading from disk before you write the stock dictionary to disk. So with the assumption that stock.plist doesn't actually exist at that path, you've just set stock to nil, so after that you're sending the writeToFilePath message to nil.
try this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"stock.plist"];
// write plist to disk
[stock writeToFile:path atomically:YES];
// read it back in with different dictionary variable
NSMutableDictionary *savedStock = [NSMutableDictionary dictionaryWithContentsOfFile:path];
if( savedStock==nil ){
NSLog(#"failed to retrieve dictionary from disk");
}
Finally, what data types are quantity and productID? you cannot serialize non-object data types, so if quantity is an integer, you would need to wrap it like so:
[fruit setObject:[NSNumber numberWithInt:quantity] forKey:#"quantity"];
Spend some time reading about property list serialization.
dictionaryWithContentsOfFile doesn't save, it reads a file. I don't see any code which writes to the file.
You are going to need something like this in your save code:
[stock writeToFile:path atomically:YES];
You're (re-)creating stock with the contents of the file immediately before writing it. Since the file doesn't exist, the dictionary is now nil. When you attempt to write that out, it doesn't produce anything. Instead, you should use the version of stock that you already populated.
(Assuming the saving bit is in the same scope, just delete the line starting NSMutableDictionary *stock above the call to writeToFile.)
(Although, come to think of it, it can't be in the same scope or the compiler would have complained in the first place.)