UserDefaults removeObjectForKey not working? - ios

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

Related

User Default Values Changing to Previous Values after some time - Swift

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.

Saving data (numbers) in an iOS app?

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

Shared UserDefaults between app and extension not working correctly

So I've been looking around and following all the steps to setup shared UserDefaults correctly but I should be missing something.
I have App Groups capability activated on both my app and my extension. Both use the same suite name ("group.TestSharedPreferences") and I write this way:
struct Preferences {
static let shared = UserDefaults(suiteName: "group.TestSharedPreferences")!
}
On viewDidLoad:
Preferences.shared.set(1, forKey: "INT")
And to read:
Preferences.shared.integer(forKey: "INT") // Returns 1 in Container App
Preferences.shared.integer(forKey: "INT") // Returns 0 in Today Extension
Even using synchronize() just after setting "INT", the value retrieved in the extension is not the one saved in the container App. Any ideas on what might I be missing? Thank you!
I would recommend to dig down step by step here.
First, make sure that both the main app and the widget extension have app group capability enabled and use the same and activated (the checkmark must be set) app group name:
Main App:
Today Widget Extension:
Then make a simple test with direct set/get access. In your main app's AppDelegate.didFinishLaunchingWithOptions method (change the app group name and the keys to your needs):
if let userDefaults = UserDefaults(suiteName: "group.de.zisoft.GPS-Track") {
userDefaults.set("test 1" as AnyObject, forKey: "key1")
userDefaults.set("test 2" as AnyObject, forKey: "key2")
userDefaults.synchronize()
}
In your Today Widget Extension's ViewController:
if let userDefaults = UserDefaults(suiteName: "group.de.zisoft.GPS-Track") {
let value1 = userDefaults.string(forKey: "key1")
let value2 = userDefaults.string(forKey: "key2")
...
}
If this works, the problem must be related in your Preferences singleton.

Cannot access NSUserDefaults using app groups one to another

I'm working on an app and a widget that the widget needs to get data from app. I've used the following codes to read and write on NSUserDefaults. And also I used $(PRODUCT_BUNDLE_IDENTIFIER).widget for widget and $(PRODUCT_BUNDLE_IDENTIFIER) referring to this post. But widget cannot get the data from app or NSUserDefaults. How can I make it work?
func addTask(name: String?) {
let key = "keyString"
tasks.append(name!)
let defaults = NSUserDefaults(suiteName: "group.Mins")
defaults?.setObject(tasks, forKey: key)
defaults?.synchronize()
}
///////
let defaults = NSUserDefaults(suiteName: "group.Mins")
let key = "keyString"
if let testArray : AnyObject = defaults?.objectForKey(key) {
let readArray : [String] = testArray as! [String]
timeTable = readArray
timeTable = timeTable.sort(<)
print("GOT IT")
print("timetable: \(timeTable)")
}
To read and save from the same set of NSUserDefaults you need to the the following:
In your main app, select your project in the project navigator.
Select your main app target and choose the capabilities tab.
Switch on App Groups (this will communicate with the developer portal, as it is generating a set of entitlements, and relevant App Id and so forth).
Create a new container. According to the help, it must start with “group.”, so give it a name like “group.myapp.test”.
Select your Today Extension target and repeat this process of switching on app groups. Don’t create a new one, rather select this newly created group to signify that the Today Extension is a member of the group.
Write to your NSUserDefaults:
// In this example I´m setting FirstLaunch value to true
NSUserDefaults(suiteName: "group.myapp.test")!.setBool(true, forKey: "FirstLaunch")
Read from NSUserDefaults:
// Getting the value from FirstLaunch
let firstLaunch = NSUserDefaults(suiteName: "group.myapp.test")!.boolForKey("FirstLaunch")
if !firstLaunch {
...
}
Swift 4.x:
Write:
UserDefaults(suiteName: "group.myapp.test")!.set(true, forKey: "FirstLaunch")
Read:
UserDefaults(suiteName: "group.myapp.test")!.bool(forKey: "FirstLaunch")

How to save local data in a Swift app?

I'm currently working on a iOS app developed in Swift and I need to store some user-created content on the device but I can't seem to find a simple and quick way to store/receive the users content on the device.
Could someone explain how to store and access local storage?
The idea is to store the data when the user executes an action and receive it when the app starts.
The simplest solution for storing a few strings or common types is UserDefaults.
The UserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Boolean values, and URLs.
UserDefaults lets us store objects against a key of our choice, It's a good idea to store these keys somewhere accessible so we can reuse them.
Keys
struct DefaultsKeys {
static let keyOne = "firstStringKey"
static let keyTwo = "secondStringKey"
}
Setting
let defaults = UserDefaults.standard
defaults.set("Some String Value", forKey: DefaultsKeys.keyOne)
defaults.set("Another String Value", forKey: DefaultsKeys.keyTwo)
Getting
let defaults = UserDefaults.standard
if let stringOne = defaults.string(forKey: DefaultsKeys.keyOne) {
print(stringOne) // Some String Value
}
if let stringTwo = defaults.string(forKey: DefaultsKeys.keyTwo) {
print(stringTwo) // Another String Value
}
Swift 2.0
In Swift 2.0 UserDefaults was called NSUserDefaults and the setters and getters were named slightly differently:
Setting
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("Some String Value", forKey: DefaultsKeys.keyOne)
defaults.setObject("Another String Value", forKey: DefaultsKeys.keyTwo)
Getting
let defaults = NSUserDefaults.standardUserDefaults()
if let stringOne = defaults.stringForKey(DefaultsKeys.keyOne) {
print(stringOne) // Some String Value
}
if let stringTwo = defaults.stringForKey(DefaultsKeys.keyTwo) {
print(stringTwo) // Another String Value
}
For anything more serious than minor config you should consider using a more robust persistent store:
CoreData
Realm
SQLite
They Say Use NSUserDefaults
When I was implementing long term (after app close) data storage for the first time, everything I read online pointed me towards NSUserDefaults. However, I wanted to store a dictionary and, although possible, it was proving to be a pain. I spent hours trying to get type-errors to go away.
NSUserDefaults is Also Limited in Function
Further reading revealed how the read/write of NSUserDefaults really forces the app to read/write everything or nothing, all at once, so it isn't efficient. Then I learned that retrieving an array isn't straight forward. I realized that if you're storing more than a few strings or booleans, NSUserDefaults really isn't ideal.
It's also not scalable. If you're learning how to code, learn the scalable way. Only use NSUserDefaults for storing simple strings or booleans related to preferences. Store arrays and other data using Core Data, it's not as hard as they say. Just start small.
Update: Also, if you add Apple Watch support, there's another potential consideration. Your app's NSUserDefaults is now automatically sent to the Watch Extension.
Using Core Data
So I ignored the warnings about Core Data being a more difficult solution and started reading. Within three hours I had it working. I had my table array being saved in Core Data and reloading the data upon opening the app back up! The tutorial code was easy enough to adapt and I was able to have it store both title and detail arrays with only a little extra experimenting.
So for anyone reading this post who's struggling with NSUserDefault type issues or whose need is more than storing strings, consider spending an hour or two playing with core data.
Here's the tutorial I read:
http://www.raywenderlich.com/85578/first-core-data-app-using-swift
If you didn't check "Core Data"
If you didn't check "Core Data"when you created your app, you can add it after and it only takes five minutes:
http://craig24.com/2014/12/how-to-add-core-data-to-an-existing-swift-project-in-xcode/
http://blog.zeityer.com/post/119012600864/adding-core-data-to-an-existing-swift-project
How to Delete from Core Data Lists
Delete Data from Coredata Swift
Okey so thanks to #bploat and the link to http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/
I've found that the answer is quite simple for some basic string storage.
let defaults = NSUserDefaults.standardUserDefaults()
// Store
defaults.setObject("theGreatestName", forKey: "username")
// Receive
if let name = defaults.stringForKey("username")
{
print(name)
// Will output "theGreatestName"
}
I've summarized it here http://ridewing.se/blog/save-local-data-in-swift/
Using NSCoding and NSKeyedArchiver is another great option for data that's too complex for NSUserDefaults, but for which CoreData would be overkill. It also gives you the opportunity to manage the file structure more explicitly, which is great if you want to use encryption.
For Swift 4.0, this got easier:
let defaults = UserDefaults.standard
//Set
defaults.set(passwordTextField.text, forKey: "Password")
//Get
let myPassword = defaults.string(forKey: "Password")
Swift 5+
None of the answers really cover in detail the default built in local storage capabilities. It can do far more than just strings.
You have the following options straight from the apple documentation for 'getting' data from the defaults.
func object(forKey: String) -> Any?
//Returns the object associated with the specified key.
func url(forKey: String) -> URL?
//Returns the URL associated with the specified key.
func array(forKey: String) -> [Any]?
//Returns the array associated with the specified key.
func dictionary(forKey: String) -> [String : Any]?
//Returns the dictionary object associated with the specified key.
func string(forKey: String) -> String?
//Returns the string associated with the specified key.
func stringArray(forKey: String) -> [String]?
//Returns the array of strings associated with the specified key.
func data(forKey: String) -> Data?
//Returns the data object associated with the specified key.
func bool(forKey: String) -> Bool
//Returns the Boolean value associated with the specified key.
func integer(forKey: String) -> Int
//Returns the integer value associated with the specified key.
func float(forKey: String) -> Float
//Returns the float value associated with the specified key.
func double(forKey: String) -> Double
//Returns the double value associated with the specified key.
func dictionaryRepresentation() -> [String : Any]
//Returns a dictionary that contains a union of all key-value pairs in the domains in the search list.
Here are the options for 'setting'
func set(Any?, forKey: String)
//Sets the value of the specified default key.
func set(Float, forKey: String)
//Sets the value of the specified default key to the specified float value.
func set(Double, forKey: String)
//Sets the value of the specified default key to the double value.
func set(Int, forKey: String)
//Sets the value of the specified default key to the specified integer value.
func set(Bool, forKey: String)
//Sets the value of the specified default key to the specified Boolean value.
func set(URL?, forKey: String)
//Sets the value of the specified default key to the specified URL.
If are storing things like preferences and not a large data set these are perfectly fine options.
Double Example:
Setting:
let defaults = UserDefaults.standard
var someDouble:Double = 0.5
defaults.set(someDouble, forKey: "someDouble")
Getting:
let defaults = UserDefaults.standard
var someDouble:Double = 0.0
someDouble = defaults.double(forKey: "someDouble")
What is interesting about one of the getters is dictionaryRepresentation, this handy getter will take all your data types regardless what they are and put them into a nice dictionary that you can access by it's string name and give the correct corresponding data type when you ask for it back since it's of type 'any'.
You can store your own classes and objects also using the func set(Any?, forKey: String) and func object(forKey: String) -> Any? setter and getter accordingly.
Hope this clarifies more the power of the UserDefaults class for storing local data.
On the note of how much you should store and how often, Hardy_Germany gave a good answer on that on this post, here is a quote from it
As many already mentioned: I'm not aware of any SIZE limitation
(except physical memory) to store data in a .plist (e.g.
UserDefaults). So it's not a question of HOW MUCH.
The real question should be HOW OFTEN you write new / changed
values... And this is related to the battery drain this writes will
cause.
IOS has no chance to avoid a physical write to "disk" if a single
value changed, just to keep data integrity. Regarding UserDefaults
this cause the whole file rewritten to disk.
This powers up the "disk" and keep it powered up for a longer time and
prevent IOS to go to low power state.
Something else to note as mentioned by user Mohammad Reza Farahani from this post is the asynchronous and synchronous nature of userDefaults.
When you set a default value, it’s changed synchronously within your
process, and asynchronously to persistent storage and other processes.
For example if you save and quickly close the program you may notice it does not save the results, this is because it's persisting asynchronously. You might not notice this all the time so if you plan on saving before quitting the program you may want to account for this by giving it some time to finish.
Maybe someone has some nice solutions for this they can share in the comments?
Swift 3.0
Setter :Local Storage
let authtoken = "12345"
// Userdefaults helps to store session data locally
let defaults = UserDefaults.standard
defaults.set(authtoken, forKey: "authtoken")
defaults.synchronize()
Getter:Local Storage
if UserDefaults.standard.string(forKey: "authtoken") != nil {
//perform your task on success }
For Swift 3
UserDefaults.standard.setValue(token, forKey: "user_auth_token")
print("\(UserDefaults.standard.value(forKey: "user_auth_token")!)")
For someone who'd not prefer to handle UserDefaults for some reasons, there's another option - NSKeyedArchiver & NSKeyedUnarchiver. It helps save objects into a file using archiver, and load archived file to original objects.
// To archive object,
let mutableData: NSMutableData = NSMutableData()
let archiver: NSKeyedArchiver = NSKeyedArchiver(forWritingWith: mutableData)
archiver.encode(object, forKey: key)
archiver.finishEncoding()
return mutableData.write(toFile: path, atomically: true)
// To unarchive objects,
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
let object = unarchiver.decodeObject(forKey: key)
}
I've write an simple utility to save/load objects in local storage, used sample codes above. You might want to see this.
https://github.com/DragonCherry/LocalStorage
NsUserDefaults saves only small variable sizes.
If you want to save many objects you can use CoreData as a native solution, or I created a library that helps you save objects as easy as .save() function. It’s based on SQLite.
SundeedQLite
Check it out and tell me your comments
This gives a great explanation for how to do this in Swift 5: https://www.hackingwithswift.com/example-code/system/how-to-save-user-settings-using-userdefaults
Summary:
To set a value:
let defaults = UserDefaults.standard
defaults.set("value", forKey: "key")
To get a String value:
let key = defaults.object(forKey: "StringKey") as? [String] ?? [String]()
To get integer value:
let key = defaults.integer(forKey: "IntegerKey")
I found this answer and it enabled me to save data, but since Swift 4.1 there has been a much easier way to do this using appstorage.
#AppStorage("studentNames") var studentName: String = "Put name here"
Each item must be unique, but using String you can store a large variety of data in here.
I've made a video tutorial to help you do this: http://youtube.com/watch?v=nLsJD6yL9Ps

Resources