NSUserDafaults reading Root.plist got a nil value - objective-c

This is the code in my AppDelegate:
NSString *pathStr = [[NSBundle mainBundle] bundlePath];
NSString *settingsBundlePath = [pathStr stringByAppendingPathComponent:#"Settings.bundle"];
NSString *finalPath = [settingsBundlePath stringByAppendingPathComponent:#"Root.plist"];
NSDictionary *settingsDict = [NSDictionary dictionaryWithContentsOfFile:finalPath];
NSArray *prefSpecifierArray = [settingsDict objectForKey:#"PreferenceSpecifiers"];
prefSpecifiersArray is setted to 0x0 < nil >. I really don't know how is it possible!
This is my Root.plist:

The values from settings.bundle do not actually load to NSUserDefaults until the user opens the settings for the first time. By default, they are off. Once the user opens the settings bundle, they will fill in for you.

Related

NSBundle crash when setting string name as number?

I am trying to play some sound, that its name is a number . so i create an NSString as number, and when i try to set it to the NSBundle, it is Null!
It does work with a word, such as #"yes" , as the name parameter
//name parameter only works as a string in words. when its a number it doesn't.
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:name ofType:type];
NSLog(#"%#",name); //logs 16 !
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath]; //crash!
the crash is because the soundFilePath is nil .
NSString *number=[NSString stringWithFormat:#"%d",16];
Ok. Problem is that i had to add the sounds into the build phase-copy bundle resources, so he can find them.

Convert CFStringRef to NSString

I'm trying to retrieve some info from the main bundle:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
CFStringRef bundleVer = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleVersionKey);
NSString *appVersion = (__bridge NSString *)bundleVer;
}
I can get the CFStringRef (in debug I can see the proper value associate to the variable), but when I try to cast it to NSString my appVersion variable has "null" value (previously it was nil).
What am I doing wrong?
I'm using ARC.
EDIT: It seems that I have problem with my project, every NSString object can't be assigned even with a simple static assignment, the value of the test variable is (null)
NSString *test = #"";
CFStringRef boundleVer = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleVersionKey);
NSString *appVersion = (__bridge NSString *)(boundleVer);
Its working fien on my side,, it is returning "2"
I would use that code instead of your current one to fetch the same information:
NSString *_shortVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleShortVersionString"];
or
NSString *_version = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleVersion"];
NOTE: I'm not sure which information you need eventually from the bundle.

How to write data in .plist?

I'm trying to save some comments in a plist, that's OK cause its just a prototype. The problem is that i can read from plist but when I try to write and read after that, it throws an "array out of bounds" exception. I can't figure it out what I'm doing wrong here.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Comments" ofType:#"plist"];
NSMutableArray *plistArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
NSMutableDictionary *newComment = [NSMutableDictionary dictionary];
[newComment setValue:commentTitle.text forKey:#"title"];
[newComment setValue:comment forKey:#"comment"];
[plistArray addObject:newComment];
[plistArray writeToFile:filePath atomically:NO];
That works fine, then i try to read:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Comments" ofType:#"plist"];
NSMutableArray *plistArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
NSMutableDictionary *dictionary = (NSMutableDictionary *) [plistArray objectAtIndex:0];
NSLog(#"%#", [dictionary objectForKey:#"title"]);
And it throws the exception.
If I add the item manually to the plist, it works fine, i guess it means that my reading code its fine.
Could it be the structure of my plist?
<?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">
<array>
</array>
</plist>
* Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (1) beyond bounds (1)'
I added the "description" to the array before writing to the plist. If i use the following code:
NSString *aDocumentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// NSString *aFilePath = [NSString stringWithFormat:#"%#/Comments.plist", aDocumentsDirectory];
//
// NSMutableArray *plistArray = [[NSMutableArray alloc] initWithContentsOfFile:aFilePath];
The return is (null)
But if i use:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Comments" ofType:#"plist"];
NSMutableArray *plistArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
i can see the contents of the array, and its all working properly.
The problem is: In both ways i cant write to the file, it keeps returning "NO". And i already checked the permissions
You are trying to write the file into mainBundle. Definitely not possible.
You will have to write the plist file to Documents or Application Support folder of the app.
Create File Path in Documents Directory :
NSString *aDocumentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *aFilePath = [NSString stringWithFormat:#"%#/Comments.plist", aDocumentsDirectory];
Write to FilePath
[plistArray writeToFile:aFilePath atomically:YES];
Read From FilePath
NSMutableArray *plistArray = [[NSMutableArray alloc] initWithContentsOfFile:aFilePath];
I see two problems with your code:
(May or may not be a problem). If the file does not exist initially, the initWithContentsOfFile: selector will return nil, causing the rest of your code to be no-ops.
(Probably the cause). You may not write to the bundle resources directory. Store your file in the Documents or Caches directory instead.
To locate your documents directory, use something like this:
- (NSString*) pathForDocument:(NSString*)documentName {
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if(documentDirectories.count < 1) return nil;
return [[documentDirectories objectAtIndex:0] stringByAppendingPathComponent:documentName];
}
First of all, why are you writing a file into your bundle?
Then, to address your problem, check if you actually did write the file.
if ([plistArray writeToFile:filePath atomically:NO])
NSLog (#"Written");
else
NSLog (#"Not Written");
Also, log your array when you're read it using -(void)description to check the contents of the dictionary.
Edit
As you said that you're not writing to your plist. For now, just create a test plist on your desktop.
NSString *testPath = [[NSString stringWithString:#"~/Desktop/Comments.plist"] stringByExpandingTildeInPath];
if ([plistArray writeToFile:testPath atomically:NO])
NSLog (#"Written");
else
NSLog (#"Not Written");
If that still returns Not Written, then there's something wrong with your dictionary. Which I doubt because it's just strings (Though they could be placeholders for asking your question on stackoverflow. The docs states that the classes in the dictionary must be of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary). If that says written though, I'm guessing it doesn't write to your bundle because of permissions, which then you have to change your plist location to somewhere else other than your bundle, which I highly recommend.
If you only put one item in the array, you should obviously use index 0 instead of 1 when reading from it:
NSMutableDictionary *dictionary = (NSMutableDictionary *) [plistArray objectAtIndex:0];

Populate an NSArray via an NSDictionary which is populated via a .plist

Here is the code:
-(void)viewDidLoad {
[super viewDidLoad];
//Verb data read, sorted and assigned to a dictionary
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"VerbDictionary" ofType:#"plist"];
NSDictionary *verbDictionary = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
NSArray *verbs = [verbDictionary allKeys];
NSArray *vSorted = [verbs sortedArrayUsingSelector:#selector(compare:)];
NSString *selectedVerb = [vSorted objectAtIndex:0];
NSArray *vArray = [verbDictionary objectForKey:selectedVerb];
self.verbArrayData = [[NSArray alloc] initWithArray:vArray];
}
Here is a screenshot of the Error message I'm getting:
(from https://plus.google.com/u/0/113629344949177123204/posts/SwHzXL6kvvJ)
The self.verbArrayData is not populating from the vArray. self.verbDataArray is nil and it shouldn't be.
I have tried this from scratch and I have done this before, actually, in the past, but via iOS 4 and Release/retain memory management. This is the first pure iOS 5 ARC app I have started.
Any ideas?
I figured it out... But I am not sure why it works. Self.verbArrayData is invalid. However, just verbArrayData works just fine. So, I changed self.verbArrayData = [[NSArray...] TO just verbArrayData = [[NSArray...] and it compiled and ran fine. So, self.verbArrayData is the getter not the setter in this case; I think. THanks - –

Deep mutable copy of a NSDictionary into a NSMutableDictionary

I have a property list which I read into an NSDictionary, from which I would like to create a mutable copy into an NSMutableDictionary to edit the contents and - later be able to reset the mutable dictionary by copying back the original contents.
NSDictionary *defaultRows;
NSMutableDictionary *rows;
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Config" ofType:#"plist"];
defaultRows = [[[NSDictionary alloc] initWithContentsOfFile: plistPath] objectForKey:#"rows"];
This is how I try to create a copy of the original dictionary (according to Apple's recommendation):
rows = [NSMutableDictionary dictionaryWithDictionary: defaultRows];
When I try to change the contents of the dictionary rows with:
[[[rows objectForKey:self.company.coaTypeCode] objectForKey:statementType] removeObjectsAtIndexes:rowsToDelete];
I get the runtime error *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'.
I have read the article here, but wonder if there is a more elegant method to implement a deep copy?
Since you are loading defaultRows from a property list, the easiest way to do this is to just deserialize the property list with mutable containers.
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Config" ofType:#"plist"];
NSData *rowsData = [NSData dataWithContentsOfFile:plistPath];
NSMutableDictionary *rows = [NSPropertyListSerialization propertyListWithData:rowsData
options:NSPropertyListMutableContainers format:NULL error:NULL];
Did you consider using
NSMutableDictionary *rows = [[[NSMutableDictionary alloc] initWithContentsOfFile:plistPath] objectForKey:#"rows"];