iOS: changing system language doesn't affect the app - ios

I've got a real pain with my current project which has few Localizable.strings files and the problem is that the language doesn't change when user change it in system settings - NSLocalizedString macros continues to return previous language strings. I can't really undestand what wrong, I've been doing everyhing 'like in manual'. Neigher app restarting nor device restarting helps. Only reinstall does.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *languages = [defaults objectForKey:#"AppleLanguages"];
NSString *currentLanguage = [languages objectAtIndex:0];
this always returns the language being set on application first launch.

Related

Replacing an Objective-C iOS app with a brand new Swift app while keeping values from NSUserDefaults

I have a general question when it comes to replacing an old application with a brand new one. The old application is several years old and written in Objective-C and I am developing an update from scratch in Swift.
I am wondering how to preserve a variable from the old app called "email_preference" which is stored in NSUserDefaults. How do I access the variable when the update is complete, and how do I test that I get the variable correctly (I feel like I have to do it correctly the first time or the value will be lost)?
These are the mentions I have found in the old code:
//Saves the email
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:self.email.text forKey:#"email_preference"];
//Gets the email
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *email = [defaults objectForKey:#"email_prefe
Thank you.
If the "new" app has the same bundle identifier as the "old" one, you can get the saved email just like this:
let email = UserDefaults.standard.object(forKey: "email_preference")

How to localize server-side data that i get in my app

I am using Italian language in my app. I have made a Localizable.string file in English and one in Italian.
It's working OK, and everything is working fine with static data, but there are mostly string responses from the server side. How can I convert them in Italian?
If I get data in different-different any string variable then how I will compare it from localizable.string(Italian).
And I also have server side data in English and Italian language. But in app it comes from server.
Thank you..
you can get your application current language like this :
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSArray *appleLanguagesArray = [userDefaults objectForKey:#"AppleLanguages"];
// Get Current language of an application...
NSString *currentLanguage = [appleLanguagesArray objectAtIndex:0];
if(currentLanguage==#"en") {
// English language.
}
The other option is if you have two buttons for two languages, then you can check currently which language is selected.

How to check if current version of the app has launched before?

I'd like to show a user agreement / disclaimer the first time my iOS app is launched. Currently, I'm simply doing it with the following code in didFinishLaunchingWithOptions in the AppDelegate:
//Show disclaimer to user, if he never agreed to it yet
if(![[NSUserDefaults standardUserDefaults] boolForKey:kHasAgreedToDisclaimerKey])
[self showDisclaimer];
However, I need to show a new disclaimer every time a new version of the app is installed and launch for the first time.
One way I was thinking of solving this problem was by creating a different UserDefaults key for each app version, but that looks like it will leave a lot of junk keys on the user's device.
Is there a better way to solve this?
Close. Don't save something for each app version, just store the last opened app version and compare it to the current app version. Something like this:
//Get the last opened and current versions
NSString *lastOpenedV = [[NSUserDefaults standardUserDefaults] objectForKey:#"LastOpenedVersion"];
NSString *currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleShortVersionString"];
//Show an alert if necessary
if (lastOpenedV == nil || ![lastOpenedV isEqualToString:currentVersion]) {
[self showDisclaimer];
//Update the last opened version
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:#"LastOpenedVersion"];
}
Sounds like you have the right idea.
I'd try something like this:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *storedVersion = [prefs objectForKey:#"AppVersion"];
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
NSString *bundleVersion = [infoDict objectForKey:#"CFBundleVersion"];
if (storedVersion) {
if (![storedVersion isEqualToString:bundleVersion]) {
[prefs setObject:bundleVersion forKey:#"AppVersion"];
[prefs synchronize];
// Show disclaimer
}
} else {
[prefs setObject:bundleVersion forKey:#"AppVersion"];
[prefs synchronize];
}
Basically, you are going to copy the bundle version to userDefaults, and compare it on every launch. If it isn't present in defaults, then you know the app was just installed, and you want to display the disclaimer. If it is in the defaults, then you want to check it against the bundle version. Anytime the bundle version doesn't match, you know the app has been updated.
Edit: Looks like took too long to mock up my example. I'm ten minutes too late, and you've found an answer. But I'll leave it here anyways :).
NSUserdefaults will be deleted when the app gets deleted, though data will persist on update. If that is OK for you, you could save a version number of the accepted version there, as mentioned by #Nick in the comment.
To make your information really persistant, write a version key to the keychain when the user accepted.

Get immutable system language list in iOS

If I use [NSLocale preferredLanguages] or [[NSUserDefaults standardUserDefaults] objectForKey:#"AppleLanguages"], the system will return a list of languages. However, this list can be changed by my app (without affecting the rest of the OS). Is there a way to get (a copy of) the actual global list that apps cannot change?
The purpose of this is to be able to override the language settings of the phone for my app, but also be able to revert back to the original settings. Without having access to the immutable global list of languages, I am not able to revert back again (especially if the user changes their language preferences). If I can get the immutable list, I can simply do something like this:
NSMutableArray *systemLanguages = [[someClass getImmutableSystemLanguages] mutableCopy];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (overrideLanguage {
[systemLanguages insertObject:#"languageCode" atIndex:0];
}
[defaults setObject:[systemLanguages copy] forKey"AppleLanguages"];
[defaults synchronize];

NSUserDefaults not saving

I am having a problem in my sprite kit app where my NSUserDefaults variable is not working. In createSceneContents (which I know is being called)
if (![defaults objectForKey:#"obj"]) {
difficultyLabel.text = #"Difficulty: Easy";
[defaults setObject:#"Easy" forKey:#"obj"];
} else {
difficultyLabel.text = [NSString stringWithFormat:#"Difficulty: %#", [defaults objectForKey:#"diff"]];
}
and then when you click on the SKLabelNode to change the difficulty and this code is being called
if ([label.text isEqualToString:#"Difficulty: Easy"]) {
label.text = #"Difficulty: Hard";
[defaults setObject:#"Hard" forKey:#"obj"];
NSLog(#"%#",[defaults objectForKey:#"obj"]);
} else {
label.text = #"Difficulty: Easy";
[defaults setObject:#"Easy" forKey:#"obj"];
NSLog(#"%#",[defaults objectForKey:#"obj"]);
but when I stop the program and run it again, it always just says Difficulty: Easy. Any suggestions?
As rmaddy's answer explains, NSUserDefaults won't immediately write data to long-term storage.
The values are saved in temporary memory when you call setObject:forKey:, so if you ever try to call objectForKey: after setObject:forKey:, it will return the value in memory if it exists, and if not, it goes to look for what's been saved to long-term storage.
In the background, the device will eventually save these values to permanent storage. It's something the OS handles, but in normal operation of your app, it shouldn't be necessary to immediately store these values to permanent storage most of the time, so let the OS do this at times when its been optimized to do so (I imagine the OS probably synchs every app's user defaults at once on some regular schedule, and as much as possible, tries to do this when the processor is idle probably).
But with that said, as rmaddy explains, if you've got something that crucially needs to be saved to the permanent storage immediately, you can always call synchronize. Keep in mind though... as long as your app isn't killed while in debug mode, the values you've set to be stored in user defaults will be stored.
But for your own sake, if you want to be absolutely certain, you can put it a call to synchronize in applicationDidEnterBackground as rmaddy suggests. Keep in mind though, this method isn't called if you kill the app.
When you say you "stop the program", what do you mean? Are you running in the debugger and killing the app and rerunning it again? NSUserDefaults doesn't persist your changes immediately. Either call [defaults synchronize] after writing important data or at least add such a call to your app delegate's applicationDidEnterBackground method. And then be sure you put your app in the background before killing it to ensure the data is written first.
To store:
NSString * str = #"name123";
NSUserDefaults *def=[NSUserDefaults standardUserDefaults];
[def setObject:str forKey:#"Key"];
[def synchronize];
NSLog(#"str = = %#",str);
To Retrieve:
NSUserDefaults *def=[NSUserDefaults standardUserDefaults];
NSString *str2 = [def valueForKey:#"Key"];
NSLog(#" Saved str = = %#",str2);
I found another reason why NSUserDefaults not saving,following code can make value in NSUserDefaults not saving:
NSUserDefaults *def= [[NSUserDefaults standardUserDefaults] init];
...
NSUserDefaults *def= [[NSUserDefaults standardUserDefaults] initWithSuiteName:#"123"];
You can search NSUserDefaults in your project to catch those init twice bug.
I had this issue where it was working previously but then wasn't. I couldn't figure it out.
It turned out restarting my mac fixed it. There must be some issue with the prefsd daemon that runs in the background that can cause this I'm guessing

Resources