Persisting data in iOS app - ios

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)
}
}
}

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

Passing data from completion block to view controller swift 3

!I have been stuck for 2 days on this issue. Here is my scenario.
User clicks login button, if email address and password are stored in UserDefaults, the app silently login the user in, first presenting an UIAlertController while retrieving the user's data from the server. Once the data is returned to the app, a completion handler assigns a local property var User: [String: AnyObject] the results. I can print(self.User) at this point and the data is properly assigned. All good. The problem occur when I try to pass this data to the next controller that I am presenting via present. Here is my code:
LoginViewController.swift
-----
dismiss(animated:true, completion: {
let tenantViewController = storyboard.instantiateViewController(withIdentifier :"tenantDashboardViewController") as! TenantDashboardViewController
tenantViewController.User = user
print(tenantViewController.User) //Works
self.present(tenantViewController, animated: true)
})
Here is my destination viewcontroller
import Foundation
import SideMenuController
class TenantDashboardViewController: SideMenuController {
var User: [String: AnyObject]?
override func viewDidLoad() {
super.viewDidLoad()
performSegue(withIdentifier: "showTenantDashboardHomeViewController", sender: nil)
performSegue(withIdentifier: "containTenantSideMenu", sender: nil)
print(self.User!) //Always returns nil, crashes app
}
}
Thanks in advance!
If you're storing a user object app-side, you should store the User Object in NSUserDefaults. Make it NSCoding compliant so you can encode/decode the User class as you need to.
Then, as Matthew states, store your username/password combo in Keychain, because it is extremely sensitive and Keychain is designed to protect your users' credentials in a secure way.

Parse User Auto Login

I'm trying to get my app to segue directly to the Profile Page after start is a PFUser is already logged in that way they won't have to keep logging in unless the completely stop the app. It's not working. Maybe I'm not checking for PFUser nil correctly. Please help. Here's what I'm putting in viewDidLoad.....
override func viewDidLoad()
{
super.viewDidLoad()
if PFUser.currentUser()?.username != nil
{
self.performSegueWithIdentifier("loginToProfileSegue", sender: self)
}
Please help.

Update cached copy of Parse currentUser() in Swift

I have an ios application, I have implemented Facebook login through Parse. Everything works great in the first trial.
I can login via Facebook
My details are stored in the PFUser.currentUser()
I am able to pull information from the Graph API
But things start to become a problem when I logout (PFUser.logOut()) , and delete the User row from the Parse class and delete the app from Facebook.
The following methods still show that the user is logged in and authenticated.
if let user = PFUser.currentUser() {
if user.isAuthenticated()
I am guessing it is because current user is still using the cached copy of the User. I try executing
do
{
try PFUser.currentUser().fetch()
}
catch{
print("User refreshed")
}
But it does not refresh the currentUser() and gives an error. How can I refresh the PFUser.currentUser(), so that it stops saying that the user is logged in and authenticated, even though it's not
Thanks
I got an answer here,
Swift & Parse - PFUser currentUser never equals nil
I've been struggling with logging out for a little while and I believe I have finally cracked it!
No matter what I did, when I used "PFUser.logOut()" would never set "PFUser.currentUser()" to nil, but it would set "PFUser.currentUser()!.username" to nil...
Because of this I used
var currentUser = PFUser.currentUser()!.username
as a global variable to track is a user is logged in.
On my login/first page I added
override func viewDidAppear(animated: Bool) {
if currentUser != nil {
self.performSegueWithIdentifier("login", sender: self)
}
}
and finally on my logout button i used
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "logout" {
PFUser.logOut() //Log user out
currentUser = PFUser.currentUser()!.username //Reset currentUser variable to nil
}
}

Performing automatic segue using PFUser.currentUser() on app launch

I have an app that is using Parse to grab the locally stored currently logged in user for my app. When I launch the app from the home screen I want to avoid forcing my users to login everytime so I grab the currently logged in user using PFUser.currentuser() like this:
override func viewDidLoad() {
super.viewDidLoad()
println(PFUser.currentUser())
if PFUser.currentUser()?.username != nil {
self.performSegueWithIdentifier("LoggedIn", sender: self)
}
}
I then just want to perform a segue to a "dashboard" if that user has already logged in. the code
self.performSegueWithIdentifier("LoggedIn", sender: self)
works beautifully everywhere else in my code. However, when I try to use it in ViewDidLoad() is when it is giving me trouble. Can anyone help me with this?
The solution is to perform the code snippet in my question in the ViewDidAppear() func like this:
override func viewDidAppear(animated: Bool) {
println(PFUser.currentUser())
if PFUser.currentUser()?.username != nil {
self.performSegueWithIdentifier("LoggedIn", sender: self)
}
}

Resources