2

Please don't mark my question duplicate. Gone through the following links and tried what they mentioned but no luck.

UserDefaults in IOS 10 is sometimes showing old value

User Default Values Changing to Previous Values Seemingly Randomly - Swift

UserDefaults in IOS 10 is sometimes showing old value

When a user login into the app I am storing some values from login API response in user defaults.

UserDefaults.standard.set(val, forKey: "XYZ")

When the user log out of the app I'm deleting user defaults.

logOutAlert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (action: UIAlertAction!) in
    // Mark:- Function that remove user defaults data
                        self.resetDefaults()
                        BackgroundTask().stopUpdate()

                            let loginVC = self.storyboard?.instantiateViewController(withIdentifier: "LoginVC") as! LoginVC

                            self.showAlert(title: Constant.projectTitle, message: "Successfully loged out.")

                            let appDel:AppDelegate = UIApplication.shared.delegate as! AppDelegate

                            appDel.window?.rootViewController = loginVC

                        }))

        // Reset userdefaults
        func resetDefaults() {
            //let domain = Bundle.main.bundleIdentifier!
            //UserDefaults.standard.removePersistentDomain(forName: domain)
            UserDefaults.standard.dictionaryRepresentation().keys.forEach(UserDefaults.standard.removeObject(forKey:))
    //        let defaults = UserDefaults.standard
    //        let dictionary = defaults.dictionaryRepresentation()
    //        dictionary.keys.forEach { key in
    //            defaults.removeObject(forKey: key)
    //        }
        }

Commented lines are the ways I tried to solve a problem. Please go through following scenario

  • Install app
  • Log in (user A)
  • Log out (user A)
  • Log in (user B)
  • Kill app from memory (not uninstall)
  • Wait for around 20 mins.
  • Relaunch app.

Issue - user A's user defaults data restored automatically.

Thanks in advance. Any help surely appreciated, sorry for my English.

teja_D
  • 383
  • 3
  • 18
  • Do you experience the same problem if you explicitly use `UserDefaults.standard.set(_:forKey:)` and `UserDefaults.standard.removeObject(forKey:)`? (instead of `.removePersistentDomain()`) – DonMag Feb 20 '20 at 12:56
  • Put a breakpoint where you attempt to remove the persistentDomain. Is it called? When do you call it? – Starsky Feb 20 '20 at 13:02
  • @Starsky I called it on logout click and also checked after removing persistent domain if there is value for any user defaults key but it returns nil. Thanks. – teja_D Feb 24 '20 at 05:03
  • Maybe [this post](https://stackoverflow.com/a/43402290/1987726) is the solution? – Reinhard Männer Feb 24 '20 at 08:25
  • @ReinhardMänner I referred that post also. No luck. – teja_D Feb 24 '20 at 09:43
  • Do you have the issue on real device or simulator (or both)? – rraphael Feb 24 '20 at 10:00
  • @rraphael both. – teja_D Feb 24 '20 at 11:48
  • @teja_D Can't think of why this might happen. Can you please post your code where you actually delete the persistentDomain, but please post the entire code of the "sign out" button action, to be aware of what is happening when tapping that button. – Starsky Feb 24 '20 at 22:43
  • @Starsky updated question with logout code. – teja_D Feb 25 '20 at 06:04
  • https://stackoverflow.com/a/52448581/341994 ? In my case the issue was I wasn’t waiting long enough between modifying user defaults and running the app again. – matt Feb 25 '20 at 15:21
  • `UserDefaults.standard.dictionaryRepresentation()` returns you composite snapshot of all values, not only yours, but including system, SDK, etc, available for your app. And you try to remove them all. Just in case. – Asperi Feb 26 '20 at 02:41
  • Maybe you can use the user name/id as a prefix to the keys, so you have different keys for this times when removeObject doesnt actually removes it. – Tharak Feb 27 '20 at 14:47

5 Answers5

4

Try this maybe it could work .. Use this removeobject on the logout button Action method .

let defaults = UserDefaults.standard
defaults.synchronize()

        UserDefaults.standard.removeObject(forKey: "email")
        UserDefaults.standard.removeObject(forKey: "name")
        UserDefaults.standard.removeObject(forKey: "userid")
        UserDefaults.standard.removeObject(forKey: "mobno")
        UserDefaults.standard.removeObject(forKey: "profileimage")
        UserDefaults.standard.removeObject(forKey: "iphoneid")


Deven Nazare
  • 538
  • 5
  • 24
0

You might want to try another way around:

//Set an empty dictionary for the main domain, instead of removing the old one
guard let domain = Bundle.main.bundleIdentifier else { return }
let emptyDomain = [String : Any]()
UserDefaults.standard.setPersistentDomain(emptyDomain, forName: domain)

I have a theory that the removePersistentDomainForName method is buggy, and it deletes the old domain, but fails to instantiate an empty domain dictionary after the deletion, so it just keeps the old one for that case.

Starsky
  • 1,829
  • 18
  • 25
  • Sorry, no luck. – teja_D Feb 25 '20 at 09:47
  • Just curious: could you delay the execution of this line of code ``` appDel.window?.rootViewController = loginVC``` by some 3-4 sec? And put a breakPoint on this line of code, and when the app pauses at this breakpoint, write in the XCode console ```print UserDefaults.standard.dictionaryRepresentation()```. Let's see if your userDefaults is actually empty at this point. – Starsky Feb 25 '20 at 10:57
0

I think this solution would be helpful in your situation. This will clean all user userdefaults and give you something like the app is just installed. I would recommend storing all user related sensitive data in keychain.

Try

NSUserDefaults.standardUserDefaults().removePersistentDomainForName(NSBundle.mainBundle().bundleIdentifier!)
NSUserDefaults.standardUserDefaults().synchronize()

It is best to add synchronize if your target version is iOS 11 or lower, which will help to write to disc immediately.

shinoy
  • 146
  • 1
  • 8
  • 1
    The documentation says "this method is unnecessary and shouldn't be used" (about `synchronize` method). Not sure I would recommend using it. – rraphael Feb 26 '20 at 16:37
  • There were complaints about the data persistence if the app get killed and recommended to use synchronize. But in [**iOS 12** release notes](https://developer.apple.com/documentation/ios_ipados_release_notes/ios_12_release_notes/foundation_release_notes?language=objc) it says now we dont need to use synchronize method and it will soon deprecated. – shinoy Feb 27 '20 at 05:53
  • This was something that the OP already tried, and didn't work. Check his ```resetDefaults()``` function, and the commented out code. Read more carefully the whole problem description. – Starsky Feb 27 '20 at 10:47
0

I think the problem is that you're trying to remove all keys from UserDefaults.standard without any filter. UserDefaults.standard contains more keys than you think and some of them are managed by iOS itself. In order to identify the keys you manage you should put a prefix or (example: "MYAPP_username") and remove only these keys. Here is the code:

let keys = UserDefaults.standard.dictionaryRepresentation().keys.filter { return $0.starts(with: "MYAPP_") }
    for key in keys {
        UserDefaults.standard.removeObject(forKey: key)
    }

This should work and hope it does ;-)

Shkelzen
  • 178
  • 1
  • 11
0

Add Observer For changes in Defaults

Debug if you are actually changing from your code unintensionaly using below approach.

In your AppDelegate Add below code:

 UserDefaults.standard.addObserver(self, forKeyPath: "XYZ", options: NSKeyValueObservingOptions.new, context: nil)

And observe using method

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    // Check when It's getting trigged. 
}

Apple's Synchronize method

If you are working this Asynchronous tasks you may have to try calling this method UserDefaults.standard.synchronize().

Check what value it returns. If it is false, (I have never seen this method returning false) You can conclude something internal things are blocking you from saving to disk.

func synchronize() -> Bool

Return value

true if the data was saved successfully to disk, otherwise false.

Community
  • 1
  • 1
Saranjith
  • 11,242
  • 5
  • 69
  • 122