How to change the language at runtime on iOS? - ios

I'm trying to use a language change button, I'm using the following:
NSUserDefaults.standardUserDefaults().setObject(["de"], forKey:
"AppleLanguages") NSUserDefaults.standardUserDefaults().synchronize()
But it only works when the application is restarted.
How I can change the language at runtime?

IN SWIFT 3
1. Download "NSBundle+Language" .h .m classes from here
https://gist.github.com/narikbi/352e93e446e8b1faf283
2. #import "NSBundle+Language.h" in to Objective-c bridging Header file for swift use
3. Put this global method in to your "Appdelegate.swift":-
//MARK:- Language Setter
public func changeLanguage(){
if var arr = (UserDefaults.standard.object(forKey: "AppleLanguages") as? [String]){
let changedKey = arr[0] == "ja" ? "en":"ja"
//this will set current language key for future use
UserDefaults.standard.set([changedKey], forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
//Run time conversion if you want
Bundle.setLanguage(changedKey)//Magic Line ;)
}
}
4. Use any where in your controller with UIButton or Switch toggle Action :-
#IBAction func btnSwitchTaped(_ sender: Any) {
print(UserDefaults.standard.object(forKey: "AppleLanguages") ?? "")
(UIApplication.shared.delegate as! AppDelegate).changeLanguage()
//if you want to check your string programatically on console print when switch toggle value(testing purpose)
let check = NSLocalizedString("Test", comment: "testing")
print(check)
}
Happy coding :)

Related

Accessing UserDefaults in Swift from other viewControllers

In my application, I use UserDefaults to store the user's login status (whether they are logged in or not), and their username. It works fine in that when I login, close the app, and open it again my app skips the login page and recognizes that I am already logged in. Although, I am now trying to install a logout button to a separate viewController. When clicked, this logout button needs to 1.) Reset UserDefaults.loginStatus to "False" 2.) Reset UserDefaults.username to nil 3.) Perform a segue to the login page.
Here is the related code from my ViewController.swift file. This is the first viewController which controls the loginPage.
import UIKit
import Firebase
let defaults = UserDefaults.standard
class ViewController: UIViewController {
func DoLogin(username: String, password: String) {
//I Am not including a lot of the other stuff that takes place in this function, only the part that involves the defaults global variable
defaults.setValue(username, forKey: "username")
defaults.setValue("true", forKey: "loginStatus")
defaults.synchronize()
self.performSegue(withIdentifier: "loginToMain", sender: self) //This takes them to the main page of the app
}
override func viewDidLoad() {
super.viewDidLoad()
if let stringOne = defaults.string(forKey: "loginStatus") {
if stringOne == "true" { //If the user is logged in, proceed to main screen
DispatchQueue.main.async
{
self.performSegue(withIdentifier: "loginToMain", sender: self)
}
}
}
}
Below is my code in SecondViewController.swift, particularly the logout function.
import UIKit
import Firebase
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let username = defaults.string(forKey: "username") {
checkAppSetup(username: username) //This is an unrelated function
//I included this because this works fine. Proving that I am able to read the defaults variable fine from this other viewController
}
}
#IBAction func logout(_ sender: Any) {
defaults.setValue("false", forKey: "username")
defaults.setValue("false", forKey: "loginStatus")
defaults.synchronize()
performSegue(withIdentifier: "logoutSegue", sender: nil)
}
When the logout function is run, the segue performs fine but the default values do not change. Can someone explain why and what I can do to get around this?
**Side note, I am not actually going to set the defaults to "false" and "false". That is just temporary for while I am debugging this issue.
Several things.
You should be using set(_:forKey:) and object(_:forKey) to read and write key/value pairs to defaults, not setValue(_:forKey). (Your use of defaults.string(forKey: "loginStatus") is correct, however.)
You should probably be writing a nil to the userName key:
defaults.set(nil, forKey: "username")
And your logout IBAction should almost certainly be setting loginStatus to false, not true.
Try changing those things.
Also, there is no reason to call synchronize unless you are terminating your app in Xcode rather than pressing the home button on the device/simulator in order to let it exit normally.
Hey i used the exactly same concept recently :
1) In your initial view, in the viewDidLoad() , check whether somebody is already logged in or not, and only one user can be logged in one device at a time, so we check like
let defaults = UserDefaults.standard
if defaults.object(forKey: "userName") != nil && defaults.object(forKey: "userPassword") != nil
{
let loginObject = self.storyboard?.instantiateViewController(withIdentifier: "YourSecondViewController") as! YourSecondViewController
//As someone's details are already saved so we auto-login and move to second view
}}
2) In your sign in button function , check whatever condition you want to check and then, inside the same, if condition satisfies then save data to userDefaults.
// If no details are saved in defaults, then control will come to this part, where we will save the entered userName and Password
let defaults = UserDefaults.standard
defaults.set(self.enteredUseName, forKey: "userName")
defaults.set(self.enteredPassword, forKey: "Password")
defaults.synchronize()
3) On logout button , delete the userDefaults and load the login view again :
let defaults = UserDefaults.standard
defaults.removeObject(forKey: "userName") //We Will delete the userDefaults
defaults.removeObject(forKey: "userPassword")
defaults.synchronize() //Sync. the defaults.
navigationController?.popToRootViewController(animated: true) //Move Back to initial view.
4) If you are using a navigation control, that you must be using :P then you will surely see the back button which will open the second view if clicked, for that you can hide the navigation bar in viewDidLoad() of your login view
self.navigationController?.navigationBar.isHidden = true

Swift UserDefaults [duplicate]

This question already has an answer here:
Expected Declaration Error using Swift
(1 answer)
Closed 6 years ago.
I'm writing code for a game I am making and am trying to create a User Default so that it saves data after you close the app.
let UserDefaults = NSUserDefaults.standardUserDefaults()
UserDefaults.setBool(false, forKey: "hasStarted")
But on the last line it underlines UserDefaults and says "expected declaration.
What have I done wrong?
In you are using Swift 2, then what you have to do is just putting your code inside a function/method:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(false, forKey: "hasStarted")
like so, in:
func applicationWillTerminate( application: UIApplication) {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(false, forKey: "hasStarted")
}
or here:
func applicationWillResignActive( application: UIApplication) {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(false, forKey: "hasStarted")
}
I've called it there because you said that you want it to save it when the app is closed.
EXPLANATION:
You called your function outside any function/method's body, which is restricted in swift. You are not allowed to do anything outside methods(just in the body of the class), besides for declaring and initialising variables/constants.
Where are you declaring and intializing the variable? If you are initializing the variable outside any method then it will give you the same error you mentioned
Try another name for it.
Try this:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(false, forKey: "hasStarted")

How to save data in swift when changing view controllers?

So I have my main view controller and I have my settings view controller. When I go into the settings and flip a switch and go back to the main, my settings view controller goes back to its default settings and same with the name. How can I make it so it will save the data while the app is open and not go back to its default values?
Thanks
I prefer to use delegates instead of checking the user defaults every time I leave the settings page.
protocol SettingsViewControllerDelegate: class {
func settingsDidChange()
}
class SettingsViewController: UIViewController {
weak var delegate: SettingsViewControllerDelegate?
func someSettingChanged(){
let defaults = UserDefaults.standard
//... get the new settings
defaults.set(newSettingsValue, forKey: "settingsKey")
defaults.synchronize()
delegate?.settingsDidChange()
}
}
class MainViewController: UIViewController {
func showSettingsVC(){
let settingsViewController = //Initialization method
settingsViewController.delegate = self
self.show(settingsViewController, sender: self)
}
}
extension MainViewController: SettingsViewControllerDelegate{
func settingsDidChange() {
let defaults = UserDefaults.standard
if let settingsValue = defaults.value(forKey: "settingsKey"){
//// do the appropriate changes
}
}
}
you can store your button state in userdefault
here is the example for swift 3:
you can get button state in actioin for valuechanged then you can store that in
UserDefaults.standard.set(false, forKey: "buttonState")
let buttonState = UserDefaults.standard.bool(forKey: "buttonState")
if buttonState == true {
}
UserDefaults.standard.synchronize()
1) When view will appear call for setting screen get your data from NSUserDefaults and then fill data to options.
2) When user changes something update your UserDefaults and dont forget to Synchronize it.

uitextfield in swift 3 not saving

My textfield is not saving. My code is listed below. Thanks
class ViewController: UIViewController,UITextFieldDelegate {
#IBOutlet weak var textext: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func savesavesave(_ sender: Any) {
let myText = textext.text
UserDefaults.standard.set(myText, forKey: "myKey")
}
Ensure your textfield is not being first responder. For example, you may call the below code before saving:
#IBAction func savesavesave(_ sender: Any) {
textext.resignFirstResponder()
let myText = textext.text
UserDefaults.standard.set(myText, forKey: "myKey")
}
You should try this for saving textfield into UserDefaults:
let myText = TextFiled1.text
UserDefaults.standard.set(myText, forKey: "myKey")
If you want to get that textfield value then write this:
let value = UserDefaults.standard.string(forKey: "myKey")
print(value!)
Synchronize the user defaults with its method and test if working
I'm writing from my memory:
UserDefaults.standard.set(myText, forKey: "myKey")
UserDefaults.standard.synchronize()
EDIT
from apple documentation
// Note that we don't synchronize with the file system or anything; this will happen
when the app quits. There are times when an explicit synchronize might be necessary,
but often it's not. Synchronizing unecessarily can also be a performance issue.
According apple docs I understands (you may think otherwise) that if you want to get the value during runtime, and not saving it in your variable - you should sync manually
EDIT AGAIN
Thanks to #LeoDabus I understand that maybe it's available during runtime too. So don't count my answer
Yes, it is not necessary synchronize happens, if app is about to exit. For accessing the information during app launches it is necessary that the synchronization happens. For more info you can check: iOS NSUserDefaults access before synchronize completion

Localize storyboard programatically on the fly

My app allows user to change country, so if they change to Thailand, I am supposed to update my storyboard etc's language.
P/S: I've seen a lot of people say if you relaunch the app you'll be fine, but that's really a user experience issue I wanted to prevent.
The following is my current attempt, it works fine for programatically created/modified/defined text but it doesn't update the text on my storyboard :(
import UIKit
class LocalizationHelper: NSObject {
static var bundle: NSBundle?
static let sharedInstance = LocalizationHelper()
func localizedStringForKey(key: String, comment: String) -> String {
return (LocalizationHelper.bundle?.localizedStringForKey(key, value: comment, table: nil))!
}
func setLanguage(language: String) {
NSUserDefaults.standardUserDefaults().setObject(language, forKey: "LocalizedStringUserDefaultsKey")
NSUserDefaults.standardUserDefaults().synchronize()
if let path = NSBundle.mainBundle().pathForResource(language, ofType: "lproj") {
print(language)
print(language)
LocalizationHelper.bundle = NSBundle(path: path)
print(LocalizationHelper.bundle)
} else {
NSUserDefaults.standardUserDefaults().setObject("Base", forKey: "LocalizedStringUserDefaultsKey")
NSUserDefaults.standardUserDefaults().synchronize()
LocalizationHelper.bundle = NSBundle.mainBundle()
}
}
}
The print statement result is
Optional(NSBundle </var/mobile/Containers/Bundle/Application/91776A02-BADA-4EC9-A451-45A453B84C83/Appname.app/th.lproj> (not yet loaded))
Does this matter anything?
For the record, I referenced this
https://github.com/jslim89/JSLocalizedString
P/S: I have also have my Main.string(Thai) in my Main.storyboard(Base) ready, just that they only update when I change System Language :(
I believe you have outlets defined for each UI component in storyboard. You can simply localize them by calling NSLocalizedString:
self.title = NSLocalizedString("MyTitle", nil);
The other option could be to have multiple storyboard one for each supported language. Once user change the country, based on the supported language pick the right storyboard. Something like this:
let storyboard = UIStoryboard(name: "MainStoryboard_English", bundle: nil)

Resources