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.
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")
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.
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 ;-)
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.
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.
Related
I had my app working with Core Data, then CloudKit to sync between devices and now I'd like to share data between users. I watched both Build apps that share data through CloudKit and Core Data and What's new in CloudKit WWDC21 and thought that I got the concepts down. CloudKit uses zone sharing and CKShares to handle sharing and Core Data attaches to this implementation natively in iOS15.
I setup my Core Data stack as such:
/// Configure private store
guard let privateStoreDescription: NSPersistentStoreDescription = persistentContainer.persistentStoreDescriptions.first else {
Logger.model.error("Unable to get private Core Data persistent store description")
return
}
privateStoreDescription.url = inMemory ? URL(fileURLWithPath: "/dev/null") : privateStoreDescription.url?.appendingPathComponent("\(containerIdentifier).private.sqlite")
privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
persistentContainer.persistentStoreDescriptions.append(privateStoreDescription)
/// Create shared store
let sharedStoreDescription: NSPersistentStoreDescription = privateStoreDescription.copy() as! NSPersistentStoreDescription
sharedStoreDescription.url = sharedStoreDescription.url?.appendingPathComponent("\(containerIdentifier).shared.sqlite")
let sharedStoreOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: containerIdentifier)
sharedStoreOptions.databaseScope = .shared
sharedStoreDescription.cloudKitContainerOptions = sharedStoreOptions
persistentContainer.persistentStoreDescriptions.append(sharedStoreDescription)
persistentContainer.loadPersistentStores(...)
Implemented the SceneDelegate user acceptance:
func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
let container = PersistenceController.shared.persistentContainer
let sharedStore = container.persistentStoreCoordinator.persistentStores.first!
container.acceptShareInvitations(from: [cloudKitShareMetadata], into: sharedStore, completion: nil) //TODO: Log completion
}
However after sharing the NSObject as such in my UI using UICloudSharingController as seen below:
let object: NSObject = // Get Object from view context
let container = PersistenceController.shared.persistentContainer
let cloudSharingController = UICloudSharingController { (controller, completion: #escaping (CKShare?, CKContainer?, Error?) -> Void) in
container.share([object], to: nil) { objectIDs, share, container, error in
completion(share, container, error)
Logger.viewModel.debug("Shared \(household.getName())")
}
}
cloudSharingController.delegate = self
self.present(cloudSharingController, animated: true) {}
My SceneDelegate method is never called and I get the following alert when I press the invite from the messages app. I'm not quite sure what is wrong in this case as on the CloudKit developer console I see the object in a private database with the zone of com.apple.coredata.cloudkit.share.[UUID]. I have not released the app yet so I'm not sure where it is getting version information from as both apps were launched from the Xcode debugger(same version & build). Additionally I was unable to find reference this alert on other questions so any advice, suggestions, or help is welcome as I have been stuck on this for a few evenings. Please let me know if there is more information that could shine light on this problem.
I had the same problem and it was solved when I added the CKSharingSupported key with a Bool value of true in the Info.plist
After that I was able to share with no problem.
I have set some value in user default in my app at beginning. Then those values may get overridden by the user and i want to delete those settings.
When i try to delete all default a few key-value removed but a few keys are not removed immediately. When i kill app & again back to app it work as deleted. As i expected it should work immediately after its delete key
Here is my code in swift 3.2 for set default value
let sharedUserDefaultsWithExtension = UserDefaults(suiteName: "GROUP_IDENTIFIER")
sharedUserDefaultsWithExtension?.setValue(newValue, forKey: "My_Key")
sharedUserDefaultsWithExtension?.synchronize()
Code for delete all user default value
let sharedUserDefaultsWithExtension = UserDefaults(suiteName: "GROUP_IDENTIFIER")
for key in (sharedUserDefaultsWithExtension?.dictionaryRepresentation().keys)! {
sharedUserDefaultsWithExtension?.removeObject(forKey: key)
}
sharedUserDefaultsWithExtension?.synchronize()
Any idea?
If you want to delete all data from userdefault then use:
let bundleIdentifier = Bundle.main.bundleIdentifier!
UserDefaults.standard.removePersistentDomain(forName: bundleIdentifier)
And for set the data in user default:
UserDefaults.standard.setValue("jogendar", forKey: "My_Key")
And in your way, you need to synchronize the data just after remove but don't try synchronize() as mention in doc https://developer.apple.com/documentation/foundation/userdefaults/1414005-synchronize
synchronize() :- Waits for any pending asynchronous updates to the defaults database and returns; this method is unnecessary and shouldn't be used.
let sharedUserDefaultsWithExtension = UserDefaults(suiteName: "GROUP_IDENTIFIER")
for key in (sharedUserDefaultsWithExtension?.dictionaryRepresentation().keys)! {
sharedUserDefaultsWithExtension?.removeObject(forKey: key)
sharedUserDefaultsWithExtension?.synchronize()
}
I'm learning application development working on a quiz game. I'd like to add statistics to the game. For example, the average score since the app has been downloaded. How can I store the scores on the device in order to reuse them after the app has been closed?
You should take a look at UserDefault. It's basically a dictionary that persists until the user uninstalls your app. I like to write a wrapper around it to get strong typing and ease of reference:
struct Preferences {
static func registerDefaults() {
UserDefaults.standard.register(defaults: [kAverageScore: 0])
}
// Define your key as a constant so you don't have to repeat a string literal everywhere
private static let kAverageScore = "averageScore"
static var averageScore: Double {
get { return UserDefaults.standard.double(forKey: kAverageScore) }
set { UserDefaults.standard.set(newValue, forKey: kAverageScore) }
}
}
Here's how to use it: before you call it for the first time in your app, you must register the defaults. These are the values that your app ships with. On iOS, it only really matters for the very first time the user launches your app. On OS X, do this every time your app starts because the user can delete the app's preferences from ~/Library/Application Support.
// You usually do this in viewDidLoad
Preferences.registerDefaults()
From then on, getting and setting the property is easy:
let averageScore = Preferences.averageScore
Preferences.averageScore = 5.5
You should take a look at UserDefaults
Example
let defaults = UserDefaults.standard
defaults.set(25, forKey: "Age")
defaults.set(true, forKey: "UseTouchID")
defaults.set(Double.pi, forKey: "Pi")
To read values back
let age = defaults.integer(forKey: "Age")
let useTouchID = defaults.bool(forKey: "UseTouchID")
let pi = defaults.double(forKey: "Pi")
UserDefaults
I am new in software and I have a question.
I have LoginPage called LoginVC(screenshot as below).When the user opened the app first time, if the member login with his username and password or via Facebook account, next time he opened the app he will pass the login screen and show the "NewsVC" directly. If he logged out, he will see the Login Page again.
According to my investigations I must use UserDefault method and create a local database(for example SQLite). Probably it creates a access token for the entered users. But I don't know how I will do. Maybe there is the question about this problem in this site but because of I don't know in a detailed manner couldn't find the topic.
Can you explain this topic and share an example with a simple Swift 3 code.
Thanks in advance
LoginVC ScreenShot
Securitywise, it is considered a bad practice to store login tokens in UserDefaults, I'd suggest using Keychain API instead.
"Hackers" can relatively easy read data from UserDefaults and use your access token.
Keychain API is a bit hard to use, I'd suggest trying a 3rd party library, here is one example:
https://github.com/jrendel/SwiftKeychainWrapper
More info about securing your data on iOS:
https://github.com/felixgr/secure-ios-app-dev
If you are just learning - it is OK to use UserDefaults, but once you consider moving your app to production - refactor it to Keychain.
Try following Helper method
Set User ID
func setCurrentLoginID(_ struserid: String) {
UserDefaults.standard.set(struserid, forKey:"userID")
}
Check User Login or Not
func isUserLoggedIN() -> Bool {
let str = UserDefaults.standard.object(forKey: "userID") as! String
return str.characters.count > 0 ? true : false
}
Get User ID
func loggedUserId() -> String {
let str = UserDefaults.standard.object(forKey: "userID") as? String
return str == nil ? "" : str!
}
For Logout
func logout() {
UserDefaults.standard.set(nil, forKey: "userID")
}
Assuming you wanted to know how to implement this then you can store and get the value like below:-
let default = UserDefaults.standard
default.set(accessToken, forKey: "accessToken")
default.synchronized()
//Now get like this and use guard so that it will prevent your crash if value is nil.
guard let accessTokenValue = default.string(forKey: "accessToken") else {return}
print(accessTokenValue)
I wonder about a simple and good way to save user profil after authentication.
I request a http request which will get a name, surname, mail and the user of its profil picture.
Where saving the name/username/mail ? NSUserDefault ? No, because I want that if the NSUserDefault informations are lost, I can get the user profil without forcing an http request. So I thought about using the Archiving ? But what about the image ? We convert it into NSData using UIImagePNG.. method ?
So, I want some advice about what I want to do if possible
Thanks you !
I think NSUserDefaults is a viable option. Data will only be lost when the user uninstalls the app. All data remains accessible even after an update. When the user uninstalls the app, all data is lost anyway, nothing you can do about that.
Sample implementation below (with getter and setter functions):
// setter
func setLoggedIn(){
userDefaults.setValue(1, forKey: "loggedIn")
userDefaults.synchronize()
}
// getter
func isLoggedIn() -> Bool{
if let loggedIn: AnyObject = userDefaults.valueForKey("loggedIn"){
return true
}
else{
return false
}
}
Or for storing for example the user's first name:
// setter
func setUserFirstName(val:String){
userDefaults.setValue(val, forKey: "userFirstName")
userDefaults.synchronize()
}
// getter
func getUserFirstName() -> AnyObject{
if let val: AnyObject = userDefaults.valueForKey("userFirstName"){
return val
}
else{
return ""
}
}
Hope this helps!
You could use plists quite easily then archive to documents directory.
there's a good tutorial at this link that should help
plist tutorial