Search code examples
iosswiftuserdefaults

Login credentials missing in swift UserDefaults


I have an application which uses a rest api for authentication. The problem I am facing now is that I save user's token in my UserDefaults and username too because those are the two main parameters needed to get user details. so if the application is closed by the user he should still be able to view the view his profile when he opens the application back but instead the profile returns empty details. this is the UserDefaults codes that I have

let defaults = UserDefaults.standard

var isLoggedIn : Bool {

    get {
        return defaults.bool(forKey: LOGGED_IN_KEY)
    }
    set {
        defaults.set(newValue, forKey: LOGGED_IN_KEY)
    }
}

//Auth Token

var authToken: String {
    get {
        return defaults.value(forKey: TOKEN_KEY) as? String ?? ""
    }
    set {
        defaults.set(newValue, forKey: TOKEN_KEY)
    }
}

var userUsername: String {
    get {
        return defaults.value(forKey: USERNAME_KEY) as? String ?? ""
    }
    set {
        defaults.set(newValue, forKey: USERNAME_KEY)
    }
}

I have no idea why it isnt retrieving the user data.

My second question is when I logout the user, all the users details are cleared as expected but the moment I try loging in with a different user, the new user's authToken and details gets printed in the console but the user profile returns the profile of the previous person. which is not supposed to be. my code is shown below

func logoutUser() -> Void {
    pk = 0
    username = ""
    email = ""
    firstName = ""
    lastName = ""
    AuthService.instance.isLoggedIn = false
    AuthService.instance.authToken = ""
    AuthService.instance.userUsername = ""

}

@IBAction func logoutPressed(_ sender: Any) {
    UserDataService.instance.logoutUser()
    dismiss(animated: true, completion: nil)
}

I would also like to add that when i run the api using postman i get a response that "detail": "Signature has expired." so i had to input the new token in the header so it displays the user details again


Solution

  • enum SettingKeys: String {
        case authToken
        //...
    }
    
    struct Settings {
    
        static var authToken: String? {
            get { return UserDefaults.standard.string(forKey: SettingKeys.authToken.rawValue) }
            set(value) { UserDefaults.standard.set(value, forKey: SettingKeys.authToken.rawValue) }
        }
    
        static func deleteAll(exclude: [SettingKeys] = []) {
            let saveKeys = exclude.map({ $0.rawValue })
            for key in UserDefaults.standard.dictionaryRepresentation().keys {
                if !saveKeys.contains(key) {
                    UserDefaults.standard.removeObject(forKey: key)
                }
            }
        }
    }
    

    I recommend storing keys as Enum, because then u can use it like that:

    //Read
    if let token = Settings.authToken {
        //do something
    }
    
    //Write
    Settings.authToken = "123456"
    
    //Delete settings
    Settings.deleteAll()
    
    //or choose what to leave
    Settings.deleteAll(exclude: [.authToken])
    

    And it's worth to mention that defaults.synchronize() is deprecated.