NSUserDefaults: how to get only the keys I've set - macos

All of the methods to get keys from NSUserDefaults return heaps of keys from domains other than the app itself (e.g., NSGlobalDomain). I just want the keys and values that my app has set. This is useful for debugging and verifying that there are no orphaned keys, etc.
I could ignore the keys that aren't mine (if I know all of them -- during development I may have set keys I'm no longer using), but there might be a collision of keys in other domains and I'll not see my app's value.
Other discussions suggest looking at the dictionary file associated with the app, but that's not very elegant.
How can I get only my app's keys form NSUserdefaults?

Elegant approach
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
NSDictionary *dict = [defaults persistentDomainForName:bundleIdentifier];
File approach:
NSString *bundleIdentifier = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleIdentifier"];
NSString *path = [NSString stringWithFormat:#"~/Library/Preferences/%#.plist",bundleIdentifier];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[path stringByExpandingTildeInPath]];
NSArray *keys = [dict allKeys];
Tested with sandboxing.

Marek's code updated to Swift 5:
guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return }
let dict = UserDefaults.standard.persistentDomain(forName: bundleIdentifier)

Related

Read plist into nsmutabledictionary, update the dictionary and then save it again

I am really new to saving files in objective C but what I'm trying to accomplish is reading a plist file located in the documents directory on launch or creating it if it doesn't exist.
It should be read in to a NSMutableDictionary. Later on in the app I should be able to save items to the NSMutableDict with categories as keys + text.
The before launch in the viewWillUnload the NSMutableDictionary should be saved into the plist file again.
I have created the plist but I need a way to write to the NSMutableDictionary the right way (category and my result.text string.
And I also need to save the NSMutableDictionary to the plist file and read the plist into the dictionary on launch.
Some help with this would be awsome :D
Thanks guys.
In the savefile void I am doing this:
storeDict = [[ NSMutableDictionary alloc]
init];
[storeDict setObject:resultText.text forKey:#"kvitto"];
[storeDict setObject:kategori forKey:#"kategori"];
[storeDict writeToFile:[self saveFilePath] atomically:YES];
saveFilePath looks like this:
- (NSString *) saveFilePath {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[path objectAtIndex:0] stringByAppendingString:#"savefile.plist"];
}
The values are strings collected from a code that the user have scanned so don't worry bout them.
Well so how do I save this correctly keeping the data that already exists in the savefile.plist.
Thanks again
If you want dictionaries and arrays contained in the dictionary to be mutable as well then do the following:
NSData *data = [NSData dataWithContentsOfFile:path];
NSError *error;
storeDict =
[NSPropertyListSerialization
propertyListWithData:data
options:NSPropertyListMutableContainersAndLeaves
format:nil
error: &error];
Why can't you use writeToFile api? Before writing question here you must google or check apple documentation for NSMutableDictionary.
[dict writeToFile:path atomically:YES];
To load saved dictionary
dict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
Happy coding!

Saving multiple objects to plist and somehow sorting them in to categories

So I am currently making a scanApp where it is necessarry to sort different objects in to different categories. So when the user scans a code and gets the result text the result should be saved into the plist file with that category.
This is my approach currently.
values = [[NSMutableArray alloc]initWithObjects:resultText.text, nil];
[values writeToFile:[self saveFilePath] atomically:YES];
- (NSString *) saveFilePath {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[path objectAtIndex:0] stringByAppendingString:#"savefile.plist"];
}
It is the way that I've learnt, but how can I save things with (keys maybe) so that I can sort them.
Also how can I read the items with just that specific category.
Thanks everyone :)

Find out if .png file is a screenshot?

When I get the attributes of a specific file with the following code:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSDictionary *attributes = [fileManager attributesOfItemAtPath:p error:nil];
Is there an attribute to know if it's a screenshot or not?
Right now I'm using this awkward piece of code:
if ([fileExtension isEqualToString:#"png"]) {
NSMutableArray *separatetFilename;
separatetFilename = [NSMutableArray arrayWithArray:[fileName componentsSeparatedByString:#" "]]; /* screenshots have multiple spaces in their names, I split them up to use the information */
if ([separatetFilename count] == 4) { /* screenshots names have 4 parts when splittet by spaces */
if ([[separatetFilename objectAtIndex:0] isEqualToString:#"Bildschirmfoto"]) { /* Bildschirmfoto = Screenshot in German */
/* Do Something */
}
}
}
With this solution I would have to put in localized Strings for the "Screenshot" string... I searched the documentary but didn't find anything. Is there a "isScreenshot" flag or something I could read?
Your question "Is there a "isScreenshot" flag or something I could read?" and the answer is "YES".
I know of two ways to get the answer:
a) ask the NSFile~Manager
b) ask for metadata.
You already used:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSDictionary *attributes = [fileManager attributesOfItemAtPath:p error:nil];
If you now have a look at the keys of this dictionary e.g. with
NSLog( #"the keys: %#", [attributes allKeys] );
you will find the key #"NSFileExtendedAttributes*". The value of this key is a dictionary with keys like com.apple.metadata:kMDItemIsScreenCapture or com.apple.metadata:kMDItemScreenCaptureType or .... This is what you asked for. The corresponding values are NSData-objects with a binary property list. Evaluating these plist give something like 1 (it is a screenshot) and window for a window screenshot or selection for a part of the window.
Looking at the metadata can be done with:
MDItemRef item = MDItemCreate( kCFAllocatorDefault, (CFStringRef)p );
If you only need the above mentioned values create a list (an NSArray) with the corresponding keys:
NSArray *ary = [NSArray arrayWithObjects:
#"kMDItemIsScreenCapture", #"kMDItemScreenCaptureType", nil];
NSDictionary *dict =
(NSDictionary *)MDItemCopyAttributes( item, (CFArrayRef)ary );
and test what it returns;
NSLog( #"the metadata are %#", dict );
In this case the returned values are __NSCFBoolean (i.e. NSNumber) and __NSCFString (i.e. NSString) which is a bit simpler to work with than a binary plist.

Reading a plist

I'm trying to read ~/Library/Preferences/com.apple.mail.plist (on Snow Leopard) to get the email address and other information to enter into the about dialog. I'm using the following code, which is obviously wrong:
NSBundle* bundle;
bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"~/Library/Preferences/com.apple.mail.plist" ofType:#"plist"];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSString *item = [plistData valueForKeyPath:#"MailAccounts.Item 2.AccountName"];
NSLog(#"Result = %#", item);
Moreover, the value I need to read is MailAcounts -> Item 2 -> AccountName and I am not sure I am doing this correctly (due to the space in the Item 2 key).
I tried reading Apple's developer guide to plist files but no help there.
How can I read a plist and extract the values as an NSString?
Thanks.
The first level is an array, so you need to use "MailAccounts.AccountName" and treat it as NSArray*:
NSString *plistPath = [#"~/Library/Preferences/com.apple.mail.plist" stringByExpandingTildeInPath];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSArray *item = [plistData valueForKeyPath:#"MailAccounts.AccountName"];
NSLog(#"Account: %#", [item objectAtIndex:2]);
Alternatively you can go by keys and pull the array from "MailAccounts" first using valueForKey: (which will yield NSArray*) and then objectAtIndex: to get the dictionary of that particular account (useful if you need more than the name).
Two things:
You don't want or need to use NSBundle to get the path to the file. The file lies outside of the app bundle. So you should just have
NSString *plistPath = #"~/Library/Preferences/com.apple.mail.plist";
You have to expand the tilde in the path to the user directory. NSString has a method for this. Use something like
NSString *plistPath = [#"~/Library/Preferences/com.apple.mail.plist" stringByExpandingTildeInPath];

NSUserDefaults and registerDefault, a standard behavior?

I need to store a numeric parameter in NSUserDefault, this parameter must be as default 1
So i write this code :
NSNumber *one = [NSNumber numberWithInt:100];
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
one,#"my-param",
nil];
[def registerDefaults:dict];
This parameter has a bind over a checkbox in IB so i can check that it's correctly set and when i start my app i see checkbox with state "on".
On the other side i must check this value programmatically so... i do something like :
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSInteger my_param = [defaults integerForKey:#"my-param"];
I excepted that since the value is not set by the user this function return value that i set as default (in my case "1") but with my surprise i found that if value has never been set by user it returns 0 ... as you can understand this's terrible :P because now i can't understand if this "0" is obtained by user choice of this's a consequence of a non-set value... how can i write code to manage this situation ?
To store the numeric values in NSUserDefaults you can simply directly use as follows:
To set the integer Value in NSUserDefault use as follows below:
NSInteger lInteger = 10;
[[NSUserDefaults standardUserDefaults] setInteger:lInteger forKey:#"integerkey"];
[[NSUserDefaults standardUserDefaults] synchronize];
And to use that NSInteger value anywhere in your project use as below:
NSInteger linteger = [[NSUserDefaults standardUserDefaults] integerForKey:#"integerkey"];
And i didnt understand your question exactly, but anyhow i think this above code may help you.

Resources