Remember the high score in 2 ViewControllers in swift - iphone

Iv'e got 2 ViewControllers, In the first one the user get the points and the high score label.
In the second ViewController, the code calculates if the user has broke the old high score, if yes - it changes, of course. BUT! when I quit the app and start playing again, the high score label show my last score and not the highest score.
Here is my code:
First ViewController:
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
defaults.synchronize()
highScore.text = String(stringInterpolationSegment: oldScore)
Second ViewController:
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
if score > oldScore {
defaults.setInteger(score, forKey: "oldScore")
defaults.synchronize()
var money = defaults.valueForKey("oldScore") as! Int
oldScore = money
} else {
defaults.setInteger(oldScore, forKey: "oldScore")
defaults.synchronize()
var money = defaults.valueForKey("oldScore") as! Int
oldScore = money
}
I also got var oldScore = Int() in the view controller code but outside the class of the view controller.
In addition, I have a "Switch" button that starts with state of "ON". But sometimes me/the user want to change it to off, how can I save it to be the user's choice after I quit the app and relaunch?

First of all check if NSUserDefault for oldScore is set. If it is not set only then initialise the oldScore, otherwise don't. Just fetch the value of oldScore and compare it with the new score and set the value accordingly
if (NSUserDefaults.standardUserDefaults().objectForKey(oldScore) == nil) {
oldScore = Int()
}
In your Second View Controller, first of all put NSUserDefault's oldScore's value into a variable and then check if it is equal to your Score. If it is only then set it like this :
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
var oldScoreCheck = defaults.valueForKey("oldScore") as! Int
if score > oldScoreCheck {
defaults.setInteger(score, forKey: "oldScore")
defaults.synchronize()
}

In your first view controller make the following change.
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
if defaults.integerForKey("oldScore") != nil
{
highScore.text = defaults.integerForKey("oldScore")
}
else
{
highScore.text = 0
}
There might be some changes required dependent on the variable type of highScore.
Hope this might be helpful.

According Apple's documentation you should register all default values before using them.
In AppDelegate in application:didFinishLaunchingWithOptions: add this code to register the key oldScore to have a reliable default value. A key switchState is also registered for the mentioned "Switch" button.
let defaults = NSUserDefaults.standardUserDefaults()
let defaultValues = ["oldScore" : 0, "switchState" : "ON"]
defaults.registerDefaults(defaultValues)
In the first view controller retrieve the high score value. If no value has been ever written to disk, the registered default value (0) is taken. You could read the value for the "Switch" button respectively.
let defaults = NSUserDefaults.standardUserDefaults()
let oldScore = defaults.integerForKey("oldScore")
let buttonState = defaults.stringForKey("switchState")
highScore.text = "\(oldScore)"
In the second view controller save the high score if changed
let defaults = NSUserDefaults.standardUserDefaults()
let oldScore = defaults.integerForKey("oldScore")
if score > oldScore {
defaults.setInteger(score, forKey: "oldScore")
defaults.synchronize()
oldScore = score
}

Related

Saving Date in UserDefaults of the first time the App was opened

I am trying to set the Date() the very first time an App is opened. This will be set in UserDefaults.
I used register() to save the date in UserDefault. The problem is.. this value seems to be saving on every launch, which defeats the purpose of UserDefaults.register.
Here is my code:
let initialOpen: NSDictionary = ["FirstOpen" : Date()]
UserDefaults.standard.register(defaults: initialOpen as! [String : AnyObject])
let firstOpenDate = UserDefaults.standard.value(forKey: "FirstOpen")
print("First Opened: \(firstOpenDate)")
I am calling this within didFinishLaunchingWithOptions.
How can I record the time that the App is launched for the first time in UserDefaults?
There's no need for register(defaults:).
Don't use value(forKey:) to read data from UserDefaults.
All you need to do is first check if the date has been set, if not, set it.
if let firstOpen = UserDefaults.standard.object(forKey: "FirstOpen") as? Date {
print("The app was first opened on \(firstOpen)")
} else {
// This is the first launch
UserDefaults.standard.set(Date(), forKey: "FirstOpen")
}

Using User Defaults to store data passed from another View Controller

I'm storing an integer using User Defaults, but the number I'm trying to store was transferred via segue (ageFromSettings) and needs to have a default value.
The problem is that the data that was transferred via segue gets stored when presented, but when the app restarts it stores the default value.
Any ideas how to save an item that was passed via segue?
class ViewController: UIViewController {
#IBOutlet weak var youHaveLabel: UILabel!
#IBOutlet weak var daysLeftLabel: UILabel!
#IBOutlet weak var leftToLiveLabel: UILabel!
#IBAction func settingsButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "toSettings", sender: nil)
}
var daysLeft = 0
var ageFromSettings = 36500
override func viewDidAppear(_ animated: Bool) {
UserDefaults.standard.set(ageFromSettings, forKey: "persistedAge")
if let age = UserDefaults.standard.object(forKey: "persistedAge") as? Int{
print("age: \(age)")
daysLeft = age
}
if daysLeft == 36500 {
daysLeftLabel.alpha = 0
youHaveLabel.alpha = 0
leftToLiveLabel.text = "Set your age in Settings"
} else if daysLeft == -1{
leftToLiveLabel.text = "Day On Borrowed Time"
} else if daysLeft == 0{
leftToLiveLabel.text = "Statistically, you died today"
} else if daysLeft == 1{
leftToLiveLabel.text = "Day Left to Live"
} else if daysLeft < 0 {
leftToLiveLabel.text = "Days on Borrowed Time"
}
if daysLeft > -1 {
daysLeftLabel.text = String(daysLeft)
} else {
daysLeftLabel.text = String(daysLeft * -1)
}
}
}
The problem is that you are saying:
UserDefaults.standard.set(ageFromSettings, forKey: "persistedAge")
if let age = UserDefaults.standard.object(forKey: "persistedAge") as? Int{
That's nutty. In the second line, we know that UserDefaults contains a persistedAge key, and we know what its value is — because you just put it there, in the first line. And you're doing that even if you have already set a different persistedAge value on a previous run of the app.
Delete the first line. If your goal is to supply an initial default value, use register(defaults:_). That's what it's for. It will be used only just in case you have not called set(_:forKey:) yet.
In your case, you are setting the value of the persistedAge key every single time in the viewDidAppear(_:) method of your view controller. Right afterwards, you are retrieving the data and trying to read it, which is somewhat redundant. In the latter case, you will also be resetting the value of the persistedAge key every time your view controller appears.

Refreshing userdefaults in Swift 4

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.

Can I use willSet and/or didSet to trigger a transition to a new view controller based on the value of a variable?

I'm new to programming and today I've been researching Property Observers in Swift. This got me wondering if it would be possible to use one to trigger an app to change screens when the value of a variable reaches a certain point.
For example, let's say I have a game that uses the variable 'score' to save and load the user's score. Could I use willSet or didSet to trigger a change in views based on the fact that the score will reach a certain value?
What I was thinking was using something like this:
var maxscore : Int = 0 {
didSet{
if maxscore == 5{
switchScreen()
}}
}
... would call the switchScreen function. Should this work? I haven't been able to find any info on this, so don't know if that's because it's not possible or I just haven't found it.
But I have attempted this with no success. It all compiles and runs, but when the score hits that magic number of 5 nothing happens.
For the sake of completeness, my switchScreen function code is below:
func switchScreen() {
let mainStoryboard = UIStoryboard(name: "Storyboard", bundle: NSBundle.mainBundle())
let vc : UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier("HelpScreenViewController") as UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
And the code I've used for setting the value to 5 is below:
func CheckAnswer( answerNumber : Int)
{
if(answerNumber == currentCorrectAnswerIndex)
{
// we have the correct answer
labelFeedback.text = "Correct!"
labelFeedback.textColor = UIColor.greenColor()
score = score + 1
labelScore.text = "Score: \(score)"
totalquestionsasked = totalquestionsasked + 1
labelTotalQuestionsAsked.text = "out of \(totalquestionsasked)"
if score == 5 { maxscore = 5}
// later we want to play a "correct" sound effect
PlaySoundCorrect()
}
else
{
// we have the wrong answer
labelFeedback.text = "Wrong!"
labelFeedback.textColor = UIColor.blackColor()
totalquestionsasked = totalquestionsasked + 1
labelTotalQuestionsAsked.text = "out of \(totalquestionsasked)"
if score == 5 { maxscore = 5}
// we want to play a "incorrect" sound effect
PlaySoundWrong()
}
SaveScore()
buttonNext.enabled = true
buttonNext.hidden = false
}
This the method is inside the class than you should be able to do!. Check this answer!
//True model data
var _test : Int = 0 {
//First this
willSet {
println("Old value is \(_test), new value is \(newValue)")
}
//value is set
//Finaly this
didSet {
println("Old value is \(oldValue), new value is \(_test)")
}
}

Nsuserdefaults highscore saving

var score = 0
var highscore = 0
var defaults = NSUserDefaults.standardUserDefaults()
#IBOutlet weak var scoreResult: UILabel!
#IBOutlet weak var highScoreResult: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if let hscore = defaults.valueForKey("highscore") {
highScoreResult.text = String(hscore)
}
}
#IBAction func tapPressed(sender: AnyObject) {
score++
scoreResult.text = String(score)
if score > highscore {
highscore = score
highScoreResult.text = String(highscore)
defaults.setValue(highscore, forKey: "highscore")
defaults.synchronize()
}
}
When i press "tapPressed" button, score++ , then it saves as highscore, the problem is that when i restart app and i have highscore saved. When i press "tapPressed" highscore starts from zero again.
Change viewDidLoad to:
override func viewDidLoad() {
super.viewDidLoad()
if let hscore = defaults.valueForKey("highscore") {
highscore = hscore
highScoreResult.text = String(hscore)
}
}
That should load the highscore from the defaults once you're app launches.
In your viewDidLoad you need to change your statement to this:
if let hscore = defaults.valueForKey("highscore") {
highscore = hscore
highScoreResult.text = String(hscore)
}
The other answers are describing the reason for the error. In addition I'd like to describe also to use NSUserDefaults properly.
The recommended way is to register the key/value pairs to have reliable default values in case the preference file will be deleted or the app launches the first time.
In AppDelegate (applicationDidFinishLaunching or earlier) register highscore with a value of 0.
let defaultValues = ["highscore" : 0]
defaults.registerDefaults(defaultValues)
Then in viewDidLoad just write
let highscore = defaults.integerForKey("highscore")
highScoreResult.text = String(highscore)
There is no optional binding needed because the value in user defaults is always a non optional.
To save highscore back to disk use the dedicated method setInteger:forKey:
defaults.setInteger(highscore, forKey: "highscore")
valueForKey: is a KVC method with a special meaning and should not be used with NSUserDefaults.