Accessing UserDefaults in Swift from other viewControllers - ios

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

Related

UserDefaults Auto-Login isn't Working

So when the user first opens my app they will see a view with a button to proceed with logging in or registering. In my Login view, I have a button handler (attached via action) which is called when a user presses a login button. Despite me using UserDefaults to store logged in state, my app does not go straight to my Dashboard view automatically. Any ideas why?
// Firebase auth in doLogin action (Login VC)
Auth.auth().signIn(withEmail: un, password: pw)
{
(resp, err) in if (err == nil && resp != nil)
{
if (resp!.user.isEmailVerified)
{
// segue from Login VC to Dashboard VC will be exectuted
self.performSegue(withIdentifier: "loggedin", sender: nil)
// store the logged in state as true for the future
UserDefaults.standard.set(true, forKey: "loggedin")
UserDefaults.standard.synchronize()
}
}
}
Here is the code for the Main view controller to determine if it should proceed directly to dashboard.
func loggedIn() -> Bool {
// determine if the user is still logged into the app
return UserDefaults.standard.bool(forKey: "loggedin")
}
override func viewDidLoad() {
super.viewDidLoad()
if (loggedIn())
{
// logged in, return to the dashboard view controller
performSegue(withIdentifier: "persisted", sender: nil)
}
}
I've tried using print statements and either the loggedIn function isn't being executed for whatever reason or the console is just too flooded with Firebase debug log statements to even see these.
You have to embed your main view controller inside a UINavigationController to make the persisted segue work.

Swift: How to check if UserDefaults exists and if not save a chosen standard value?

I am trying to check if a UserDefaults Key exists and if not set it to a standard value of my choice, but the answers here on stack overflow didn't help me to get this working.
Essentially I have a couple of UISwitches, one is on and the rest is set to off from the beginning. Now my problem is that I don't know how to save these initial states into UserDefaults when the viewController is loaded and those keys do not exists.
This is how I tried to check if the key for the UISwitch exists and if not set it to true (because that's the state I want it to be) and then check again what the bool for the key is and set the UISwitch to it (this is essentially important when the viewController is opened another time):
func setupSwitches() {
let defaults = UserDefaults.standard
//check if the key exists
if !defaults.bool(forKey: "parallax") {
switchParallax.setOn(true, animated: true)
}
//check for the key and set the UISwitch
if defaults.bool(forKey: "parallax") {
switchParallax.setOn(true, animated: true)
} else {
switchParallax.setOn(false, animated: true)
}
}
When the user presses the corresponding button I set the UserDefaults key like this:
#IBAction func switchParallax_tapped(_ sender: UISwitch) {
let defaults = UserDefaults.standard
if sender.isOn == true {
defaults.set(true, forKey: "parallax")
} else {
defaults.set(false, forKey: "parallax")
}
}
This is obviously working, but the problem is in the first code above.
First of all I am not sure how to check if it exists and if not set it to "true" and since the function is called setupSwitches() it is runs every time the viewController is shown.
So I don't know if there is a better way (eg. because of the memory issues) to check if a key exists, if not set it to true and if it already exists get the bool from UserDefaults and set the switch to the right state.
The problem is you can't determine exists with UserDefaults.standard.bool(forKey: key). UserDefaults.standard.object(forKey: key) returns Any? so you can use it to test for nil e.g.
extension UserDefaults {
static func exists(key: String) -> Bool {
return UserDefaults.standard.object(forKey: key) != nil
}
}

Persisting data in iOS app

I am new to iOS. I want to save authentication token recieved from a REST API to use in further api calls without requiring a login. Currently I am using UserDefaults to store this token. This token works fine unless app is completely closed. Relaunching the app again takes me to login screen.
Saving the token like this
UserDefaults.standard.setValue(authToken, forKey: "auth_token")
UserDefaults.standard.synchronize() // Now this call is derpecated. Framework handles this call at proper places.
LoginViewController
override func viewDidLoad(){
super.viewDidLoad()
if UserDefaults.standard.string(forKey: "auth_token") != nil {
self.performSegue(withIdentifier: "login_success", sender: self)
}
}
But the issue is how can I persist this token even after the app was completely closed ?
EDIT
I have also tried syncing UserDefaults within applicationWillTerminate methods of AppDelegate class just to make sure but that even doesn't work.
Nothing wrong with the UserDefaults. Just wrapping the triggering segue call in main queue was required to properly work.
override func viewDidLoad() {
super.viewDidLoad()
if UserDefaults.standard.string(forKey: "auth_token") != nil {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "login_success", sender: self)
}
}
}

First time launch / segue swift

what i am trying to accomplish is once the app is launched it will check for first time use. if it is the first time use it will take you to a view controller to enter credentials, else it will take you to to the main menu of the app. this is what i have so far but every time i launch it will give me a blank page with the error message of "A segue must either have a performHandler or it must override -perform.
" i have both segues linked on storyboard. can any one please steer me in the right direction.
let defaults = UserDefaults.standard
if defaults.string(forKey: "isAppAlreadyLaunchedOnce") != nil{
print("first time")
self.performSegue(withIdentifier: "toToken", sender: nil)
}else{
defaults.set(true, forKey: "isAppAlreadyLaunchedOne")
defaults.synchronize()
print("not first")
self.performSegue(withIdentifier: "toMainMenu", sender: nil)
}
If your segue type is set to be Custom in Storyboard — you have to subclass UIStoryboardSegue with your own logic in order for it to work.
class MySegue: UIStoryboardSegue {
override func perform() {
// your custom transition logic
}
}
Otherwise just use one of the existing presets from iOS SDK.
Your method is very confusing. The way I go about this problem is shown below.
override viewDidLoad() {
super.viewDidLoad
let checkForFirstTimeLaunch = Userdefaults.standard.string(forKey: "Flag")
if checkForFirstTimeLaunch == false {
print("First time launch")
//if user launches app for the first time, it will go here
} else {
//otherwise, it will go here
}
}

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.

Resources