Language is not stored correctly - objective-c

I'm doing an app that lets user to choose language and change it during runtime. I have an issue trying to store desired language. It runs correctly during the same execution the user changes language, but then in next execution, it always gets spanish language (system language). Here is the code I use to store language:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (sender.tag==1){
[defaults setObject:[NSArray arrayWithObjects:#"ca",#"es", nil] forKey:#"AppleLanguages"];
}else{
[defaults setObject:[NSArray arrayWithObjects:#"es",#"ca", nil] forKey:#"AppleLanguages"];
}
[defaults synchronize];
Many thanks

The system will override the value of AppleLanguages every time your app gets started.
A solution would be to store your desired languages in a separate NSUserDefaults entry and load them (write them into AppleLanguages) at every app startup.
Hope this helps

Related

How to keep information from reseting

I have a check box type application, kinda like notes on the iPhone. When the user closes the application and double tabs the home button and deletes the application from the background, the things they have typed also get deleted or reset. I was thinking of calling
[self saveContext];
But that didn't work. WHat should I be calling?
You can usual use something such as NSUserDefaults to save information. Or, you can go another route and save the information in a data structure and write that to a file. There are many easy ways of doing this. For instance, if you want to preserve a checked value, you could do the following:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:YES forKey:"checked"];
[defaults synchronize];
Very simple, and then can be queried across app exits, like so:
if( [defaults boolForKey:"checked"] ) {
//do stuff here
}

Saving NSUserDefaults Issue?

i have been working on a app, and needed to store a string. I used this code to set the default:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:CertificateKey.stringValue forKey:#"SavedKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"%#",[defaults objectForKey:#"SavedKey"]);
I loged it, so i know it saved...well, it showed me the value.
When i open my application, I use this to retrieve the default:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[CertificateKey setStringValue:[defaults objectForKey:#"SavedKey"]];
[CertificateKey setTitleWithMnemonic:[defaults objectForKey:#"SavedKey"]];
[[NSUserDefaults standardUserDefaults] synchronize];
Why will it not get the default value? Did i not completely save it?
Don't quit the application by pressing the stop button in xcode. Instead, quit it by right clicking on the application icon and selecting "Quit".
Edit
Maybe the first time that you execute the application, you want to save some defaults but you don't want to set them the second+ time that the application runs.
For this purpose in some class initialize method register the defaults, like this:
+ (void) initialize
{
NSUserDefaults* defaults= [NSUserDefaults standardUserDefaults];
[defaults registerDefaults: #{} ];
// This case it's an empty dictionary, but you can put whatever you want inside it.
// just convert it to data if it's not an object storable to a plist file.
}
Also, you're using setValue:forKey: , that method is inherited from NSObject. Use setObject:forKey: .
And use finalize if you want to save the defaults at the end:
- (void) finalize
{
// Save the defaults here
}
You might have a problem because you are creating an instance of NSUserDefaults. From what I understand you are supposed to access it like so: [[NSUserDefaults standardUserDefaults] setValue:#"Pikachu" forKey:#"Best Pokemon Ever"]; [[NSUserDefaults standardDefaults] objectForKey:#"Best Pokemon Ever"]; Rather than actually creating an instance of it.

Check for first launch of my application

How would I check if it is the first launch of of my application using NSUserDefaults and running some code for the first time my app opens?
This should point you in the right direction:
static NSString* const hasRunAppOnceKey = #"hasRunAppOnceKey";
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
if ([defaults boolForKey:hasRunAppOnceKey] == NO)
{
// Some code you want to run on first use...
[defaults setBool:YES forKey:hasRunAppOnceKey];
}
The NSUserDefaults answer is the first thing that popped in my head, but upon reflection I will make another suggestion. A bit more work, but it's worth considering. The motive is: sometimes when troubleshooting an app, Apple recommends deleting that app's plist file. It's a fairly ubiquitous troubleshooting technique. I would recommend storing your boolean in your plist file instead of NSUserDefaults.
Disclaimer: I only do iOS development, so I'm not sure how NSUserDefaults and plists interact on the Mac, and I don't know what all is involved in getting your plist to live in ~/Library/Application\ Support/Preferences/com.mycompany.MyAppName.plist
Anyway, I imagine what this requires is having some code which can actually author a "fresh" plist (probably a copy from a template file in your bundle), and you app does this if it launches and does not see a plist. The default plist should not include the flag which lets your users skip the 'first time' code, but if they have opened the app before, and then delete the plist, they should get default behavior back.
This is an important behavior to support where possible, to aide our users if our app ever gives them trouble.
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"hasBeenLaunched"]) {
// Run code on the first launch only ...
[defaults setBool:YES forKey:#"hasBeenLaunched"];
}
You can use NSUserDefaults to save bools, integers, objects into the program and have them available whenever you open it. You can use 'boolForKey' to set a flag called "hasBeenLaunched". By default, this value will be NO when not set. Once you change it to YES, the code in the if condition will never be executed again.
In your main controller class, implement something like this:
static NSString * const MDFirstRunKey = #"MDFirstRun";
#implementation MDAppController
+ (void)initialize {
NSMutableDictionary *defaults = [NSMutableDictionary dictionary];
[defaults setObject:[NSNumber numberWithBool:YES] forKey:MDFirstRunKey];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
// the following if on Mac and is necessary:
[[NSUserDefaultsController sharedUserDefaultsController] setInitialValues:defaults];
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
BOOL firstRun = [[[NSUserDefaults standardUserDefaults]
objectForKey:MDFirstRunKey] boolValue];
if (firstRun) {
// do something
[[NSUserDefaults standardUserDefaults] setObject:
[NSNumber numberWithBool:NO] forKey:MDFirstRunKey];
} else {
// do something else
}
}
#end
The +initialize class method is called before an instance of the class it's found in is created; in other words, it is called very early on, and is a good place to set up your default values.
See Preferences and Settings Programming Guide: Registering Your App's Default Preferences for more info.

NSUserDefaults. setValue works, Not setBool

I trying to store some settings in NSUserDefaults, but It seems that the app won't store the setBool values.
This works:
[[NSUserDefaults standardUserDefaults] setValue: #"hello" forKey: #"test"];
[[NSUserDefaults standardUserDefaults] synchronize];
When I terminate the app and restart it, the value have been saved. However, when I do this:
[[NSUserDefaults standardUserDefaults] setBool: YES forKey: #"test"];
[[NSUserDefaults standardUserDefaults] synchronize];
It won't save after I close the app and restart it.
Should I file a bug report, or is there something I'm missing here?
Thanks
Edit:
I figure what I did wrong. In AppDelegate, I wanted to check if the boolForKey was set, and it it wasn't I did this:
if (![defaults boolForKey: #"test123"])
[defaults setBool: YES forKey: #"test123"];
... however, when it comes to boolWithKey, the "!" just check if the bool is YES or NO, not if its nil.
How can you be sure its not working? I tried your code and it works for me. Are you sure you are reading the Boolean in the correct way AFTER you write it?
This code SHOULD work:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:NO forKey:#"test"];
[defaults synchronize];
BOOL myBool = [defaults boolForKey:#"test"];
I had the same problem by having the following code in my AppDelegate to keep track of whether the user had seen a particular viewController to display a walkthrough, and then setting this Bool to NO after the user had seen it.
if (![standardUserDefaults boolForKey:#"firstViewOfVC"]) {
[standardUserDefaults setBool:YES forKey:#"firstViewOfVC"];
}
But then when you set it to NO later on and check if it "exists", you are actually seeing the NO boolean value and setting it back to yes. The quick fix is just to store the boolean value in an NSNumber object so that you can check for it's existence, independent of its value being YES or NO. See below:
if (![standardUserDefaults objectForKey:#"firstViewOfVC"]){
[standardUserDefaults setValue:[NSNumber numberWithBool:YES] forKey:#"firstViewOfVC"];
}
I had the exact same problem. Everything EXCEPT BOOLs were persisting correctly; but i was using some old coding styles from ios 3. recoded this way, everything works.
If anybody else is using old books.... here is an example
Bad stuff:
//////////// set / get bL2R
if (![[NSUserDefaults standardUserDefaults]
boolForKey:kL2RomanizationChoice]) {
[[NSUserDefaults standardUserDefaults]
setBool:YES
forKey:kL2RomanizationChoice];
bL2R = YES;
NSLog(#"L2Rom not found, set to YES.");
}
else {
bL2R = [[NSUserDefaults standardUserDefaults]
boolForKey:kL2RomanizationChoice];
NSLog(#"L2Rom found.");
if (bL2R) {
NSLog(#"L2Rom found to be YES.");
}
}
Good stuff:
if (![defaults boolForKey:kL2RomanizationChoice])
[defaults setBool:YES forKey:kL1RomanizationChoice];
L2String_Setting = [defaults objectForKey:kSecondLangChoice];
bL2R = [defaults boolForKey:kL2RomanizationChoice];
Update: sadly this only seemed to work briefly, and now is failing again... using Xcode 4.5.2. may just swap out bools for integers...
XCode 4.6 seems to have the same problem highlighted by hangzhouharry. A useful call is [[NSUserDefaults standardUserDefaults] dictionaryRepresentation] to see if your key values are looking the way they should.
For example -> autoLogout = 0;
which was set as a Bool, [settings boolForKey:#"autoLogout"] returns nothing
[settings integerForKey:#"autoLogout"] returns 0 (as, sort of, expected)

How to know if window is minimizable when titlebar was double clicked?

This image is from SystemPreferences > Appearance
I want to know How do I get that value programmatically?
I ask because I am drawing a window with a customized titlebar and I want it to resemble (in behavior) as much as possible to normal (non-customized) cocoa windows.
Maybe a terminal command I can pipe or is there an cocoa API that does this?
EDIT:
Answer (thanks to NSGod)
- (void)mouseUp:(NSEvent *)event{
if ([event clickCount] == 2) {
//Get settings from "System Preferences" > "Appearance" > "Double-click on windows title bar to minimize"
NSString *const MDAppleMiniaturizeOnDoubleClickKey = #"AppleMiniaturizeOnDoubleClick";
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
// [userDefaults addSuiteNamed:NSGlobalDomain]; // unnecessary
BOOL shouldMiniaturize = [[userDefaults objectForKey:MDAppleMiniaturizeOnDoubleClickKey] boolValue];
if (shouldMiniaturize) {
[self miniaturize:self];
}
}
}
Later I found that Appearance (Aqua/Graphite) can be found:
NSString * const kAppleAquaColorVariant = #"AppleAquaColorVariant";
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
// [userDefaults addSuiteNamed:NSGlobalDomain]; // unnecessary
NSNumber *color = [userDefaults objectForKey:kAppleAquaColorVariant];
if ([color intValue] == 6) {//graphite is 6
imageName = [imageName stringByAppendingFormat:#"_graphite"];
}else{//defaults to aqua, (aqua is 1)
imageName = [imageName stringByAppendingFormat:#"_colorsryg"];
}
Which can be helpful too :)
The way I would do it is probably read the value in from user defaults.
NSString * const MDAppleMiniaturizeOnDoubleClickKey = #"AppleMiniaturizeOnDoubleClick";
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
// [userDefaults addSuiteNamed:NSGlobalDomain]; // unnecessary
NSNumber *miniaturize = [userDefaults
objectForKey:MDAppleMiniaturizeOnDoubleClickKey];
NSLog(#"AppleMiniaturizeOnDoubleClick == %#",
([miniaturize boolValue] ? #"YES" : #"NO"));
(This preference setting is stored in the invisible .GlobalPreferences.plist in your ~/Library/Preferences/ folder).
Note that by default, the "double-click to minimize" option is turned off, so if you check for the presence of the AppleMiniaturizeOnDoubleClick and it returns nil, that means it is off. (User defaults only start to store values if they differ from the defaults).
This key is the same in Leopard as it is in Snow Leopard. (It hasn't changed in Lion or Mountain Lion either).
Of course, there is a secret (private) method in NSWindow, -(BOOL)_shouldMiniaturizeOnDoubleClick, but I wouldn't recommend using private methods.
[UPDATE] Regarding Catfish_Man's comment: you are correct in that the line [userDefaults addSuiteNamed:NSGlobalDomain]; is unnecessary, as NSUserDefaults already has the ability to read global preferences. (I modified the code above to reflect this).
"Additionally, NSGlobalDomain is not translated to
.GlobalPreferences.plist for that method."
I'm not sure I follow you there. NSUserDefaults is built on top of CFPreferences which defines the following 6 constants:
Application:
kCFPreferencesAnyApplication,
kCFPreferencesCurrentApplication
Host:
kCFPreferencesAnyHost,
kCFPreferencesCurrentHost
User:
kCFPreferencesAnyUser,
kCFPreferencesCurrentUser
Given a fictional application bundle identifier of "com.markdouma.App" and a single host (based on your current network location that won't change for this example), there are generally 8 locations where preference information could be stored on your disk. (NOTE: The paths shown are for demonstration purposes only; the exact file path locations are subject to change). The 8 different locations arise from the different combination of the CFPreferences constants:
/Library/Preferences/.GlobalPreferences.plist
(kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesAnyHost)
/Library/Preferences/com.markdouma.App.plist
(kCFPreferencesCurrentApplication, kCFPreferencesAnyUser,
kCFPreferencesAnyHost)
/Library/Preferences/ByHost/.GlobalPreferences.UNIQUE_HOST_IDENTIFIER.plist (kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesCurrentHost)
/Library/Preferences/ByHost/com.markdouma.App.UNIQUE_HOST_IDENTIFIER.plist (kCFPreferencesCurrentApplication,
kCFPreferencesAnyUser, kCFPreferencesCurrentHost)
~/Library/Preferences/.GlobalPreferences.plist
(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser,
kCFPreferencesAnyHost)
~/Library/Preferences/com.markdouma.App.plist
(kCFPreferencesCurrentApplication, kCFPreferencesCurrentUser,
kCFPreferencesAnyHost)
~/Library/Preferences/ByHost/.GlobalPreferences.UNIQUE_HOST_IDENTIFIER.plist (kCFPreferencesAnyApplication,
kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)
~/Library/Preferences/ByHost/com.markdouma.App.UNIQUE_HOST_IDENTIFIER.plist (kCFPreferencesCurrentApplication,
kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)
While NSUserDefaults can only write to the domain combination shown in italics, it automatically has read access to the domain combinations shown in bold. In other words, without having to do anything, I can automatically run the following code and print the results:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSNumber *miniaturize = [userDefaults
objectForKey:#"AppleMiniaturizeOnDoubleClick"];
NSNumber *fastUserSwitching = [userDefaults
objectForKey:#"MultipleSessionEnabled"];
NSLog(#"AppleMiniaturizeOnDoubleClick == %#",
([miniaturize boolValue] ? #"YES" : #"NO"));
NSLog(#"MultipleSessionEnabled == %#",
([fastUserSwitching boolValue] ? #"YES" : #"NO"));
Running that code on my system prints the following results:
AppleMiniaturizeOnDoubleClick == YES
MultipleSessionEnabled == YES
This makes sense, since I have both Fast User Switching and Double-click to minimize options enabled. MultipleSessionsEnabled is stored in the local domain at /Library/Preferences/.GlobalPreferences.plist, and AppleMiniaturizeOnDoubleClick is stored in the user domain at ~/Library/Preferences/.GlobalPreferences.plist.
Sample project: NSUserDefaultsFinagler.zip
"Additionally additionally, that's slow. Please don't do this."
Sorry, but that makes no sense (assuming that we've agreed that we're no longer using addSuiteNamed:). User defaults are cached by the application, making calls take in the matter of milliseconds. Wouldn't there be little noticeable difference between asking user defaults for the value for a key that represents a local application value or one that represents a global value?
AFAIK, this is the only "legal" (App-store-compatible) way to achieve the OP's goal. If there's another more efficient means, then please elaborate.