I'm pretty stuck with localization inside the app. What is idea:
You choose language inside the app, and depends on which language you choose it save value in NSUserDefaults. Because I didn't find material for this kind of localization, my idea is to make class that will have class method that return string depending on which language is saved in NSUserDefaults. Example:
+(NSString *) helloString
{
NSString *hello = [NSString new];
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"language"] isEqualToString:#"en"]) {
hello = #"Hello";
}
else if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"language"] isEqualToString:#"es"]) {
hello = #"Holla!";
}
return hello;
}
Is this legit way, is there any better solution?
I found solution for my problem. I used LocalizationSystem class, which simulated changed language from system settings.
Related
I can't get NSUserDefaults to save a BOOL value. It's really simple and people do it but it just simply will not save. I tried everything I know. The most recent attempt being this:
if (_EndHide == YES) {
NSDictionary *aProperties = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]forKey:#"EndHide"];
BOOL boolValue;
if ([aProperties valueForKey:#"EndHide"])
boolValue = [[aProperties valueForKey:#"EndHide"] boolValue];
} else if (_EndHide == NO) {
NSDictionary *aProperties = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO]forKey:#"EndHide"];
BOOL boolValue;
if ([aProperties valueForKey:#"EndHide"])
boolValue = [[aProperties valueForKey:#"EndHide"] boolValue];
}
What happens is I am making an iOS app game for iPhone and when you dodged these well objects for period of time, you end the game and unlock a button on the start menu named endless, after the story.
Now I need this button to be constantly unlocked. I managed to unlock it through a scene. It's all working but it just won't stay unlocked. Does anyone have any advice to help me with this?
Here is the endless button configuration and bool configuration:
#property(nonatomic, getter=isActive) bool EndHide;
#property(nonatomic) IBOutlet UIButton *endless;
That's all the code for the buttons and bools anyway to keep it unlocked.
I know it's simple but it won't work. Maybe a deep bug I tried it on another more updated version of Xcode but still to no avail. The issue is persistent and a real problem. I even tried switch saving that didnt work as well.
I cannot actually see any code that tries to save anything to the user defaults. You first need to get the user defaults object
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
To write a bool value:
[defaults setBool:YES forKey:#"EndHide"];
To read a bool value:
BOOL boolValue = [defaults boolForKey:#"EndHide"];
Fix any mistakes that I may have made yourself.
PS. Do NOT use valueForKey unless you really understand what it does. Use objectForKey or array syntax.
After days of searching, I found it. What happens is, instead of keep the bool changed aka always YES i just saved another bool with this:
in view did load top part:
if (_EndHide == YES) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"boolSet"];
} else if (_EndHide == NO) { }
so when the bool is yes you finished the game than this code:
in view did load top:
bottom:
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"boolSet"]) {
NSLog(#"not workin -.-");
} else {
NSLog(#"finallyyyy workin !! YAeh");
_endless.hidden = NO;
}
code simply always remembers :D thank you everyone for trying to help me this method was best, endless is a button, create another bool that save in memory use it to unlock the button.
try this in the view did load
NSUserDefaults* prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:#"0" forKey:#"test"];
NSString * testValue = [prefs ObjectforKey:#"test"];
NSLog(testValue);
can you please tell me what is the console printing out?
I added a manual language section inside my iOS App where you can change the language the app should be displayed in. If someone chooses to select a manual language I override the "AppleLanguages" standardUserDefaults like so
NSString *language = [[[NSUserDefaults alloc] initWithSuiteName:kAppGroup] objectForKey:kManualLanguageKey];
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithObjects:language, nil]
forKey:#"AppleLanguages"];
Now when the user restarts the app (after terminating it) the App automatically loads the correct LocalizedStrings.
Now here is my problem:
even though this solves my problem of changing the language of the app it does not display numbers etc. correctly. Number/DateFormatter and localizedStringWithFormat depend on the [NSLocale currentLocale]. I know I could just overrite the current lokale as well like so
[[NSUserDefaults standardUserDefaults] setObject:language
forKey:#"AppleLocale"];
but then I can't get the real language/region selected in the system settings once the user decides to disable the manual language. I could store the currentLocale inside my own userDefaults before I override it but then if the user decides (for whatever reason) to change the system language while the manual language in the app is active I won't be able to get this new selected system language.
Is there any way to get the right format of Numbers and Dates without manually changing the locale property of NumberFormatter etc.?
Ok, I found a way to reset the current locale after the user disables the manual language on http://www.thetawelle.de/?p=3800
In main.m if the bool for manual language is false, I reset the current language like this:
NSArray *keysToRemove = #[#"AppleLanguages",#"NSLanguages",#"AppleLocale"];
NSLog( #"RESETTING TO USE SYSTEM LOCALE" );
#try {
for( NSString *currentKey in keysToRemove ) {
if( [defaults objectForKey:currentKey] ) {
[defaults removeObjectForKey:currentKey];
}
}
}
#catch (NSException *exception) {
// NOTHNG TO CATCH HERE
}
#finally {
[defaults synchronize];
}
after that, the current locale and language are back to the language and region selected in the system settings.
The app's current behavior is, the user logged in once will not be logged out unless the user explicitly clicks on the logout.
I keep the user logged in, even if the user closes the app and opens it again.
When newer version of my app is released in appstore, I want to find out whether the user updated my app and opened it for the first time.
At that point I want to make them login again.
Is there a way to find out at the first time launch of the app after its been updated to latest version?
Create some kind of version #'s scheme. Note: You can enable Xcode to create backups and versions whenever you make substantial changes to the code.
There are a number of ways one could create a version constant, save it, and read it back.
When you update an app from the store, there is app data that persists from the previous installed version of the app, which you can read back to determine the version and, then update that persistent data to be ready for the next update cycle.
This answer was a very popular solution in another similar question.
Or, try something like #JitendraGandhi's ObjC answer below, or if you use Swift, try something like my port of #JitendraGandhi's ObjC example to Swift:
func hasAppBeenUpdatedSinceLastRun() -> Bool {
var bundleInfo = Bundle.main.infoDictionary!
if let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String {
let userDefaults = UserDefaults.standard
if userDefaults.string(forKey: "currentVersion") == (currentVersion) {
return false
}
userDefaults.set(currentVersion, forKey: "currentVersion")
userDefaults.synchronize()
return true
}
return false;
}
You can save your currentversion to NSUserDefaults and use this method to check your version every time the app awakes:
#pragma mark - NSBundle Strings
- (NSString *)currentVersion
{
return [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleShortVersionString"];
}
if the currentversion is different from stored... its time to show the login!
Hope it helps you.
Use NSUserDefaults to store the CFBundleVersion. Then check against it every time the application is launched.
// Check if new version
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *currentAppVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"];
if ([defaults objectForKey:#"savedAppVersionKey"] != nil) {
// Key exists
NSString *savedAppVersion = [defaults objectForKey:#"savedAppVersionKey"];
if ([currentAppVersion isEqualToString:savedAppVersion]) {
// Still running the same app version
// Do nothing
NSLog(#"App version: SAME");
}
else {
// The app version changed from the last launch
// Do something here
NSLog(#"App version: UPDATED");
}
}
// Set the key & synchronize
[defaults setObject:currentAppVersion forKey:#"savedAppVersionKey"];
If you want simple and easy solution, Use this function :
-(BOOL)isAppUpdated
{
NSDictionary *bundleInfo = [[NSBundle mainBundle] infoDictionary];
NSString *currentVersion = [bundleInfo objectForKey:#"CFBundleShortVersionString"];
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"currentVersion"] isEqualToString:currentVersion])
{
return NO ;
}
else
{
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:#"currentVersion"];
return YES ;
}
}
Following code will return NO / YES. You can call this method multiple times to know whether app was updated before this launch or not.
- (BOOL)launchedFirstTimeAfterUpdate
{
static NSString *lastVersion;
NSString *currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleShortVersionString"];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *versionKeyName = #"lastLaunchedVersion";
lastVersion = [[NSUserDefaults standardUserDefaults] stringForKey:versionKeyName];
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:versionKeyName];
[[NSUserDefaults standardUserDefaults] synchronize];
});
if (!lastVersion.length)
{
// No last version means, launched first time
return NO;
}
if ([lastVersion compare:currentVersion options:NSNumericSearch] == NSOrderedAscending)
{
// Last version is less than current version
return YES;
}
return NO;
}
in Xcode 4.6.3 I set the "Localization native development region" = en.
I provide my app with Spanish, English and Russian locales.
Unfortunately when I set the Language of the Iphone Simulator to for example "German"
my app uses then the Spanish locale.
I suspect because I have a "Spanish Computer".
But I would like that the customers of my app get English if their locale is not provided by the app.
How can I ensure this?
One thing I noticed. In the "copy bundle resources" tab I see my other Localizable.strings files appear red. But I know that they are being despite red deployed...
Actual language which the app will have has a dynamic nature because it depends on a dynamic list in device settings. iOS will fall down through it and choose the 1st supported language.
Let's consider some app supports "en" and "ru". Despite that fact that "en" is much more preferable language for Portuguese locale, "ru" will be chosen because it situated higher than "en" in device settings on the screenshot below.
I had the same issue with one of my apps. What I did is force en as the second language in AppleLanguages and it's working now. You have to do that before the application starts (main). It's a bit of an ugly hack but works for now (NSLocalizedString is looking at that list to determine the language)... You have to be careful though, because you may get some weird stuff from NSLocale after you do that. For example if you want to use the - displayNameForKey:value: it won't be in sync and you'll have to restart the app so you get the correct result. so what you can do is initialize NSLocale with the first element in the [NSLocale preferredLanguages] (NSLocale *currentLocale = [[NSLocale alloc] initWithLocaleIdentifier:[NSLocale preferredLanguages][0]];) and then it's going to return the expected results.
Here's what you have to do to swap the languages:
// remove what was previously stored in NSUserDefaults (otherwise the previously selected language will be still the first one in the list and your app won't be localized in the language selected in settings)
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; // make the change immediate
// reorder the languages so English is always the default fallback
NSMutableArray *mutableLanguages = [[NSLocale preferredLanguages] mutableCopy];
NSInteger enIndex = NSNotFound;
for (NSString *lang in mutableLanguages) { if ([lang isEqualToString:#"en"]) { enIndex = [mutableLanguages indexOfObject:lang]; break; } }
#try {
if ((enIndex != 0) && (enIndex != 1)) { [mutableLanguages exchangeObjectAtIndex:1 withObjectAtIndex:enIndex]; }
} #catch (NSException *exception) {
}
// save the changes
[[NSUserDefaults standardUserDefaults] setObject:mutableLanguages forKey:#"AppleLanguages"];
[[NSUserDefaults standardUserDefaults] synchronize]; // to make the change immediate
return UIApplicationMain(argc, argv, nil, NSStringFromClass([YourAppDelegate class]));
I wrote these lines in my code:
CFUUIDRef identifier = CFUUIDCreate(NULL);
NSString *identifierString = (NSString*)CFUUIDCreateString(NULL, identifier);
NSLog(#"%#",identifierString);
[self setValue:identifierString forKey:kParamNameDeviceId];
But these lines are getting called every time when the app launches.
Now my question is, how can the following be achieved?
Create CFUUID.
Store it in some variable in such a way that, when next time my app starts, it should not create a new CFUUID. It should look for the previously created CFUUID and return it.
In short, I want a CFUUID be created ONCE and used throughout the life of the app (till it gets uninstalled).
easiest way is to store it in NSUserDefaults
NSString *identifierString = [[NSUserDefaults standardUserDefaults] objectForKey:#"myID"];
if (!identifierString) {
CFUUIDRef identifier = CFUUIDCreate(NULL);
identifierString = (NSString*)CFUUIDCreateString(NULL, identifier);
[[NSUserDefaults standardUserDefaults] setObject:identifierString forKey:#"myID"];
}
NSLog(#"%#",identifierString);
/* ... */
Create once. Add it in NSUSerDefaults and check whether you have a UUID stored already before creating one. Simple Pseudo code,
NSString *CFUUID = nil;
if (![[NSUserDefaults standardUserDefaults] objectForKey:#"UUID"]) {
//create CFUUDID.
[[NSUserDefaults standardUserDefaults] setObject:CFUUID forKey:#"UUID"];
}
else
{
CFUUID = [[NSUserDefaults standardUserDefaults] objectForKey:#"UUID"];
}
If you are storing identifier on server to keep track of important information regarding unique user than i suggest you store identifierString in "KeyChain" which keeps that identifier even if you application has been deleted by user.
NSUserDefaults keep data until app is deleted. Keychain keeps data even if app is deleted.
I had similar case so this might help if your case is equivalent to mine.