Why NSMutableDictionary don't want write to file? - iphone

- (void)viewDidLoad
{
[super viewDidLoad];
if ([[NSFileManager defaultManager] fileExistsAtPath:pathString])
{
infoDict = [[NSMutableDictionary alloc] initWithContentsOfFile:pathString];
}
else
{
infoDict = [[NSMutableDictionary alloc]initWithObjects:[NSArray arrayWithObjects:#"BeginFrame",#"EndFrame", nil] forKeys:[NSArray arrayWithObjects:[NSNumber numberWithBool:YES],[NSNumber numberWithBool:YES], nil]];
if ([infoDict writeToFile:pathString atomically:YES])
{
NSLog(#"Created");
}
else
{
NSLog(#"Is not created");
NSLog(#"Path %#",pathString);
}
}
This is my code. I check if file is created, if not - I create a NSMutableDictionary and I write it to file at path, but writeToFile method returns NO. Where is problem? If I create this file with NSFileManager it works, but doesn't when I want to write a dictionary.

writeToFile:atomically only works if the dictionary you call it on is a valid property list object (see docs).
For a NSDictionary to be a valid property list object, among other things, its keys must be strings, but in your example the keys are NSNumber instances.

You can not control the content you are going to write sometimes. For example, you can't avoid a null value when you are going to write a JSON object that is gotten from a server.
NSData is compatible with these "invalid" values, so converting NSArray or NSDictionary to NSData is an ideal way in these cases.
write:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:jsonObject];
[data writeToFile:path atomically:YES];
read:
NSData *data = [NSData dataWithContentsOfFile:path];
NSDictionary *jsonObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];

Related

Saving NSArray of NSDictionary's to File

I'm trying to save a huge NSArray to a file and then retrieve it...
NSURL *documentsDirectory =
[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
NSURL *fileURL = [documentsDirectory
URLByAppendingPathComponent:#"scorecards.dgs"];
NSMutableArray *savedArrayOfScorecards = [NSMutableArray arrayWithContentsOfURL:fileURL];
[savedArrayOfScorecards addObject:currentScoreCard];
[savedArrayOfScorecards writeToURL:fileURL atomically:YES];
NSMutableArray *mynewArray = [NSMutableArray arrayWithContentsOfURL:fileURL];
NSLog(#"%#, %#",[[mynewArray objectAtIndex:0] objectForKey:#"info"], course);
The NSLog comes back with...
(null), BridgeMill
Which BridgeMill should be returned on both sides of the comma. But array won't save...
Check if it's being saved correctly:
if([savedArrayOfScorecards writeToURL:fileURL atomically:YES])
{
NSLog(#"Was saved");
}
else
{
NSLog(#"Houston we have a problem...");
}
On the documentation:
- (BOOL)writeToURL:(NSURL *)aURL atomically:(BOOL)atomically
You can also use this to check what is going on:
- (BOOL)writeToURL:(NSURL )aURL options:(NSDataWritingOptions)mask error:(NSError *)errorPtr
what class is course?
writeToURL only supports types that can be converted into plist format. Those types are NSString, NSData, NSArray, or NSDictionary.
If you have other types in your NSArray you have to archive the array.

Read/Write plist on iPhone iOS 5

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).

NSDictionary to XML

I'm trying to convert a NSDictionary to XML. (I was successful in transforming NSDictionary to JSON). But now I need to transform NSDictionary to XML. Is there a built-in serializer in Objective-C like the one for JSON?
int r = arc4random() % 999999999;
//simulate my NSDictionary (to be turned into xml)
NSString *name = [NSString stringWithFormat:#"Posted using iPhone_%d", r];
NSString *stock_no = [NSString stringWithFormat:#"2342_%d", r];
NSString *retail_price = #"12345";
NSArray *keys = [NSArray arrayWithObjects:#"name", #"stock_no", #"retail_price", nil];
NSArray *objects = [NSArray arrayWithObjects:name,stock_no,retail_price, nil];
NSDictionary *theRequestDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
NSDictionary *theFinalRequestDictionary = [NSDictionary dictionaryWithObject:theRequestDictionary forKey:#"product"];
...//other irrelevant code omitted
NSData *theBodyData = [NSPropertyListSerialization dataFromPropertyList:theFinalRequestDictionary format:NSPropertyListXMLFormat_v1_0 errorDescription:nil];
NSPropertyListFormat format;
id XMLed = [NSPropertyListSerialization propertyListFromData:theBodyData
mutabilityOption:NSPropertyListImmutable
format:&format
errorDescription:nil];
NSLog(#"the XMLed is this: %#", [NSString stringWithFormat:#"%#", XMLed]);
The NSLog doesn't print a string in XML format. It prints it like a NSDictionary.
What should I use to serialize my NSDictionary to XML?
propertyListFromData:... returns a "property list object", that is, depending on the contents of the data, an array or a dictionary. The thing that you're actually interested in (the xml) is returned by dataFromPropertyList:... and thus stored in your theBodyData variable.
Try this:
NSLog(#"XML: %#", [[[NSString alloc] initWithData:theBodyData encoding:NSUTF8StringEncoding] autorelease]);
There are many different varieties of XML. If you're not picky about the specific tags, and if the contents of your dictionary is limited to types used in property lists (NSString, NSNumber, NSDate, etc.) you can write your dictionary to a property list in one line:
[myDict writeToFile:somePath atomically:YES];
If you'd prefer to keep the XML in memory instead of writing to a file, use NSPropertyListSerialization as you're doing.

Plist Read and Write iPhone

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.

Objective-C arrayWithPlist (that's already in an NSString)

I have an NSString that already contains a pList.
How do I turn it into an NSArray?
(WITHOUT saving it to disk, only to reload it back with arrayWithContentsOfFile, and then have to delete it.)
Where is the make arrayWithPlist or arrayWithString method?
(Or how would I make my own?)
NSArray *anArray = [NSArray arrayWithPlist:myPlistString];
You want to use NSPropertyListSerialization:
NSData *data = [plistString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSArray *plist = [NSPropertyListSerialization
propertyListWithData:plistData
options:/*unused*/0
format:NULL
error:&error];
if (!plist) {
NSLog(#"%s: Failed to create plist: %#",
__func__, error ?: #"(unknown error)");
}
That particular method was introduced with iOS 4.0/Mac OS X 10.6. Prior to those releases, you would use:
NSData *data = [plistString dataUsingEncoding:NSUTF8StringEncoding];
NSString *errorText = nil;
NSArray *plist = [NSPropertyListSerialization
propertyListFromData:plistData
mutabilityOption:NSPropertyListImmutable
format:NULL
errorDescription:&errorText];
if (!plist) {
NSLog(#"%s: Failed to create plist: %#",
__func__, errorText ?: #"(unknown error)");
/* Part of the reason this method was replaced:
* It is the caller's responsibility to release the error description
* if any is returned. This is completely counter-intuitive.
*/
[errorText release], errorText = nil;
}