Refreshing userdefaults in Swift 4 - swift

Is it possible to refresh the value of the user defaults in Swift4? The values change after scanning a new product and they are stored in a user default. So when u scan a new product the user default has to refresh.
I hope someone has a solution for me

Set userdefaults as you are setting first time with same key.
UserDefaults.standard.set(value, forKey: key)

Try this one. before setting remove and synchronise it.this might solve your issue
func setValueToDefaults(key:String, value:AnyObject){
self.removeKeyValueFromDefaults(Key:key)
UserDefaults.standard.set(value, forKey: key)
}
func getValueFromDefaults(key:String) ->AnyObject?{
if((UserDefaults.standard.value(forKey: key)) != nil){
let value:AnyObject = UserDefaults.standard.value(forKey: key)! as AnyObject
return value
}else{
return nil
}
}
func removeKeyValueFromDefaults(Key:String){
let defaults = UserDefaults.standard
defaults.removeObject(forKey: Key)
defaults.synchronize()
}

As a workaround to achieve it, you could declare a computed property for getting/setting the value from the user defaults, example:
var flag: Bool {
get {
let value = UserDefaults.standard.bool(forKey: "flag_key")
return value
}
set {
UserDefaults.standard.set(newValue, forKey: "flag_key")
UserDefaults.standard.synchronize()
}
}
reading the value of flag returns the Boolean value from the user defaults (of "flag_key" key) and editing its value would be also reflected to the user defaults.

Related

How to use UserDefaults to keep track of which posts a user has voted on?

I am trying to keep track of which posts a user has voted on to avoid users being able to close the app and revote on a post that they have already voted on.
Here is where I initialize and load my userDefaults:
let userDefaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
self.reloadInputViews()
tableView.dataSource = self
tableView.register(UINib(nibName: K.cellNibName, bundle: nil), forCellReuseIdentifier: K.cellIdentifier)
if let leftPostArray = userDefaults.array(forKey: leftKey) as? [String]{
votedLeftPosts = leftPostArray
}
if let rightPostArray = userDefaults.array(forKey: rightKey) as? [String]{
votedRightPosts = rightPostArray
}
}
And then where I update userDefaults:
//Run when user presses the button on the left in a post
func userVotedLeft(_ postCell: PostCell) {
print(votedLeftPosts)
guard let indexPath = tableView.indexPath(for: postCell) else { return }
let post = posts[indexPath.row]
if(votedLeftPosts.contains(post.firstImageUrl)){
return
}else if(votedRightPosts.contains(post.firstImageUrl)){
increment(fbLeftKey, post)
decrement(fbRightKey, post)
votedRightPosts.remove(at: votedRightPosts.firstIndex(of: post.firstImageUrl)!)
}else{
increment(fbLeftKey, post)
}
votedLeftPosts.append(post.firstImageUrl)
userDefaults.set(votedLeftPosts, forKey: fbLeftKey)
userDefaults.set(votedRightPosts, forKey: fbRightKey)
}
//Run when the user presses the button on the right in a post
func userVotedRight(_ postCell: PostCell) {
print((userDefaults.array(forKey: fbRightKey) ?? []) as [String])
guard let indexPath = tableView.indexPath(for: postCell) else { return }
let post = posts[indexPath.row]
if(votedRightPosts.contains(post.firstImageUrl)){
return
}else if(votedLeftPosts.contains(post.firstImageUrl)){
increment(fbRightKey, post)
decrement(fbLeftKey, post)
votedLeftPosts.remove(at: votedLeftPosts.firstIndex(of: post.firstImageUrl)!)
}else{
increment(fbLeftKey, post)
}
votedRightPosts.append(post.firstImageUrl)
userDefaults.set(votedLeftPosts, forKey: fbLeftKey)
userDefaults.set(votedRightPosts, forKey: fbRightKey)
}
Right now I am still able to vote on a post, then restart the app and vote on the same post.
You're reading from UserDefaults using the keys leftKey and rightKey, and then saving to UserDefaults using the keys fbLeftKey and fbRightKey. If those variables contain different strings then the values you save won't be loaded.
If they contain the same strings, why are there different variable names? That's a bug waiting to happen.
You should also be aware that if you kill your app in the debugger, it likely won't save UserDefaults changes to "disk." You have to press the home button to switch your app to the background in order to get your UserDefaults changes to be saved.

How do I check if userDefault is empty?

I'm trying to deny access to a certain view controller if the userDefault is empty, but the code doesn't seem to work. To be a bit more clear, I'm saving a favorite-list to a userDefault. This is my code:
if UserDefaults.standard.array(forKey: "favorites") == nil {
navigationController?.popToRootViewController(animated: true)
return
}
The error is Index out of range, which means that the whole block is ignored (the code after this block runs and since the user default is empty it crashes when trying to retrieve information that isn't there).
The funny thing is, the code works the first time I try to enter the viewController (it denies me access). But if I favorite mark an object (save to userDefault), then un-favorite the same object (userDefault becomes empty), and enter the viewController, the program crashes.
I have tried:
if let favExist = UserDefaults.standard.array(forKey: "favorites") {
print("")
print("FAV EXISTS")
print("")
}else {
print("")
print("NOPE")
print("")
navigationController?.popToRootViewController(animated: true)
return
}
...and the same problem persists. In print() the log tells me FAV EXISTS after I favorite mark, then un-favorite mark, then try accessing the page (even though the userDefault now should be empty).
I have also tried code from other threads. The suggested code to solve my problem from the other thread was:
let defaults = UserDefaults.standard
if (!defaults.bool(forKey: "favorites")) {
defaults.set(true, forKey: "favorites")
}
I'm not really sure how to implement it though? Where do I use this? And what does it do?
Any idea what's wrong?
It´s enough to do this:
if let favorites = UserDefaults.standard.array(forKey: "favorites") {
// userDefault has a value
} else {
// userDefault is nil (empty)
}
Update:
You need to make a check within the if-statement if your arrat has any values too:
if let favorites = UserDefaults.standard.array(forKey: "favorites") {
print("Favorites exists")
if favorites.isEmpty {
print("Favorites is empty")
} else {
print("Favorites is not empty, it has \(favorites.count) items")
}
} else {
print("Favorites is nil")
}
When you set the UserDefaults Array also set a BOOL to UserDefaults. When you recover the Bool it won't crash even if it hasn't been set.
var favouritesset = UserDefaults.standard.bool(forKey: "favoritesset")
if favouritesset == true {
//Then Recover the Array
var array = UserDefaults.standard.array(forKey: "favorites")
}
OK, Rashwan L solved it for me. Thing was, my code (and suggested code by others) checked whether or not userDefault existed or not - it didn't bother whether there was a value stored or not. To work around the problem, I had to test if favExist.count == 0 was true. If true, user is blocked from the page and prevented from accessing the rest of the code. Se below:
if let favExist = UserDefaults.standard.array(forKey: "favorites") {
if(favExist.count == 0)
{
navigationController?.popToRootViewController(animated: true)
return
}
}else {
navigationController?.popToRootViewController(animated: true)
return
}
You do like this:
if UserDefaults.standard.array(forKey: "favs") != nil {
// userDefault has a value
} else {
// userDefault is nil (empty)
}

Why cant I unwrap a UserDefault type string? [Swift 3.0 - Xcode 8]

I've stored data sent back by delegate as a user default and I'm trying to prepare it to send in a segue. I am able to send it in a segue, the problem I'm having is the data comes out in the form "optional[Data]" I know what optional means, but it doesn't let me unwrap UserDefault like an optional, even though it says it is optional?
Here's the code:
func DataToPass(ArrayName: [String]) {
print("Check ArrayContent--->",ArrayName)
var DCollect = [String]()
var CCollect = [String]()
DCollect.append(ArrayName[0])
CCollect.append(ArrayName[1])
let defaults = UserDefaults.standard
if let unwrap = defaults{
unwrap.set(DCollect, forKey: "DCollect")
unwrap.set(CCollect, forKey: "CCollect")
}
}
The error is:
"Initializer for conditional binding must have Optional type, not
'UserDefaults'" on line if let unwrap = defaults{
Why can't I unwrap it like this?
Another Attempt:
func DataToPass(ArrayName: [String]) {
print("Check",ArrayName)
UserDefaults.standard.set(ArrayName, forKey: "ToCollect")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "SegueInfo"{
let firstController = segue.destination as! ViewController
if let Tosend = UserDefaults.standard.value(forKey:
"ToCollect") as? String {
Collect.append(Tosend)
}
firstController.AllData = Collect
}
Nothing is appended this way, Tosend is empty?
To save something to user defaults:
UserDefaults.standard.set("It is a string", forKey: "meaningfulIdentifier")
To get something out from user defaults:
if let anyString = UserDefaults.standard.value(forKey: "meaningfulIdentifier") as? String {
// Do something with anyString
}
You only need optional binding when you are dealing with getting something out from user defaults. For the data part you need this optional binding, not the User Defaults itself.
Why does it tell me that I can't unwrap UserDefaults?
The error message is trying to explain to you that UserDefaults.standard is not of an Optional type. That means that it is not of type Optional<UserDefaults>, which means it can't use the if-let statement to unwrap it.
What you want to unwrap is not the UserDefaults object which is already provided as a singleton through the UserDefaults.standard property.
What can I unwrap?
What you want to unwrap are the variables you are trying to retrieve.
Those would be the optional types you'll have to deal with. It is Optional because it may already exist in the UserDefaults or it may not yet exist.
How do I retrieve a string array that I stored into UserDefaults?
Using the key DCollect, use the appropriate method (i.e. stringArray(forKey:) to retrieve it.
let defaults = UserDefaults.standard
if let dcollect = defaults.stringArray(forKey: "DCollect") {
// it already exists; now use the dcollect variable
} else {
// it does not exist; do something (i.e. add dcollect value?) if it does not exist yet
}
How do I store a string array into UserDefaults?
let defaults = UserDefaults.standard
let value : [String] = ["My", "Very", "Eager", "Pluto"]
standard.set(value, forKey: "DCollect")
I am not sure why you are trying to unwrap UserDefaults.standard because you don't need to. It is not an optional, therefore you get error from Xcode.
You can straight add the values to the de UserDefaults like this:
let u = UserDefaults.standard
u.set(["a", "b", "c"], forKey: "yourKey")
First, don't forget to import Foundation
Then try this code
func DataToPass(ArrayName: [String]) {
UserDefaults.standard.set(ArrayName, forKey: "ToCollect")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "SegueInfo"{
let firstController = segue.destination as! ViewController
if let Tosend = UserDefaults.standard.stringArray(forKey: "ToCollect") as? [String] {
Collect += Tosend
}
firstController.AllData = Collect
}
}

Make a table listing all keys and values from a dictionary

My AppDelegate contains a dictionary. I have an NSDictionaryController bound to dict. I then bind the 2 columns of an NSTableView to the Dictionary Controller:
Column 1 (Key): Controller Key = arrangedObjects. Model Key Path = key
Column 2 (Value): Controller Key = arrangedObjects. Model Key Path = value
But my table is blank. Any idea how's to correct this?
My code:
class AppDelegate {
dynamic var dict = NSMutableDictionary()
func applicationDidFinishLaunching(aNotification: NSNotification) {
dict.setValue(1, forKey: "One")
dict.setValue(2, forKey: "Two")
}
}
I also tried:
let newObject = dictController.newObject()
newObject.setKey("One") // deprecated
newObject.setValue(1) // deprecated
dictController.addObject(newObject)
But Xcode said setKey and setValue are deprecated in 10.10.3. How do I add an object to an NSDictionaryController? I'm running Mac OS X 10.11.4.
The dictionary controller doesn't see the changes inside dict. You can solve this by first contructing the dictionary and then assigning to dict.
func applicationDidFinishLaunching(aNotification: NSNotification) {
var tempDict = NSMutableDictionary()
tempDict.setValue(1, forKey: "One")
tempDict.setValue(2, forKey: "Two")
dict = tempDict
}
Another solution is implementing key-value observer compliance.
func applicationDidFinishLaunching(aNotification: NSNotification) {
willChangeValueForKey("dict")
dict.setValue(1, forKey: "One")
dict.setValue(2, forKey: "Two")
didChangeValueForKey("dict")
}
Thanks to #Code Different for making me realize what the problem is.
The little secret of NSDictionaryController is that you must specify a list of included keys via the includedKeys property. Otherwise, it will not have anything inside arrangedObjects. The easiest way is to bind the includedKeys to your dict.keys property.
In Interface Builder:
Select the Dictionary Controller, go to the Binding Inspector
For the Included Keys property: set Bind To = Delegate, Model Key Path = dict.keys
Programmatically:
dictController.bind(NSIncludedKeysBinding, toObject: self, withKeyPath: "dict.keys", options: nil)

Swift2: return a optional type object

I'm new in the swift2 world and I currently struggle with a simple function :
// Get all moves for the first category
func getMuscles() -> BodyPart {
let bpart:BodyPart?
if let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext {
do{
let fetchRequest = NSFetchRequest(entityName: "BodyPart")
let fetchResults = try managedObjectContext.executeFetchRequest(fetchRequest) as! [BodyPart]
bpart = fetchResults[0]
} catch let error as NSError {
print(error)
bpart = nil
}
}
return bpart
}
How can I solve this issue ? And what are the 'best-practices' in swift2 for defining a function ?
Thank you
EDIT 1
I've tried to change the signature of the method, but error is still here :
The question you should be asking yourself is whether getMuscles() must always return an object or if it's fine for it to return a nil.
By changing the method signature to func getMuscles() -> BodyPart?,
you're basically stating that a nil might be returned from that method,
thus solving your immediate compile time issue.
In that particular context, because you're fetching objects from CoreData,
it might be wise to allow getMuscles() to return a nil.
The way you define your functions (if they return optionals ?, or not) entirely depends on the calling code.
Change your method signature to :
func getMuscles() -> BodyPart?
But be careful while unwrapping the return value when the this function is being called.
Just return:
func getMuscles() -> BodyPart? { }
Thats nothing to do with SWIFT2.. The return type is expecting some value BodyPart not an optional value BodyPart?...But you are returning a optional value bpart
func getMuscles() -> BodyPart {
let bpart:BodyPart?
....
return bpart
}
If you want to return bpart as it is you need to create the return type as optional
func getMuscles() -> BodyPart? {
let bpart:BodyPart?
....
return bpart
}
or if you want to just return the value try this
func getMuscles() -> BodyPart {
let bpart:BodyPart = ()//initialize here dont make optional
....
return bpart
}