Where to implement UserDefaults to save data when viewcontroller is closed

I am working on a tip tracker app that lets the user input as many tip values that they want, while showing them their input and also calculating a total and average. I am trying to use UserDefaults to save two doubles (totalValueOfAllTips, numberOfTipsTracked) and one string (sampleLog.text)


I want it to show the same data in `sampleLog`, `totalLabel`, and `avgLabel` every time the user re-opens the app.


Here is my TipTrackerViewController class, along with protocols that calculate the total, number of tips, and average value:

import UIKit
protocol TipTrackerProtocol {
    var totalValueOfAllTips: Double { get }
    var numberOfTipsTracked: Int { get }
}
extension TipTrackerProtocol {
    var averageTip: Double {
        return round((totalValueOfAllTips / Double(numberOfTipsTracked)) * 100) / 100 
    }
}
class TipTrackerViewController: UIViewController, TipTrackerProtocol {
  
    var numberOfTipsTracked = 0       // store as user default  
    var valDollar = 0                
    var valCent = 0                  
    var totalValueOfAllTips = 0.0     // store as user default
  
   
    @IBOutlet weak var dollarLabel: UILabel!       
    @IBOutlet weak var centLabel: UILabel!
    @IBOutlet weak var sampleLog: UITextView!     // store all values in sampleLog as user defaults            
    @IBOutlet weak var totalLabel: UILabel!       
    @IBOutlet weak var avgLabel: UILabel!
       
    @IBOutlet weak var dollarsStepper: UIStepper!
    @IBOutlet weak var centsStepper: UIStepper!
  
     // Dollar Stepper
    @IBAction func stepper(_ sender: UIStepper) {
        dollarLabel.text = "$\(Int(sender.value))"
        valDollar = Int(sender.value)      
    }
  
     // Cent Stepper
    @IBAction func stepperCent(_ sender: UIStepper) {
        centLabel.text = ".\(Int(sender.value))"
        if sender.value == 0 {
            centLabel.text = ".00"         
        }
        valCent = Int(Double(sender.value)) 
    }
  
    @IBAction func addTipButton(_ sender: UIButton) {
        let numberFormatter = NumberFormatter()
      
       
        numberFormatter.minimumFractionDigits = 2
        numberFormatter.maximumFractionDigits = 2
        numberFormatter.minimumIntegerDigits = 1  /
      
        let tipDollarCent = (dollarLabel.text ?? "$0") + (centLabel.text ?? ".00")
        let tipValue = getTipValue()
        totalValueOfAllTips += tipValue
        numberOfTipsTracked += 1                         
        sampleLog.text! += "\(tipDollarCent)\n"
      
       
        let totalLabelStringValue = numberFormatter.string(from: totalValueOfAllTips as NSNumber)
        let averageLabelStringValue = numberFormatter.string(from: averageTip as NSNumber)
        totalLabel.text! = "$" + totalLabelStringValue!    
        avgLabel.text! = "$" + averageLabelStringValue!   
        dollarLabel.text = "$0"            
        centLabel.text = ".00"             
      
       
        valDollar = 0
        valCent = 0
        dollarsStepper.value = 0
        centsStepper.value = 0
      
    }
  
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
  
    /
    @IBAction func backButtonPressed(_ sender: Any) {
        navigationController?.popViewController(animated: true)
    }
  
   
    private func getTipValue() -> Double {
       
        let valueOfCents = Double(valCent) / 100
        return Double(valDollar) + valueOfCents
    }
  
}


I tried the following implementation inside of my viewDidLoad function, at least for the total tip value:

UserDefaults.standard.set(totalValueOfAllTips, forKey: _totalValueOfAllTips)     // write to user defaults 
UserDefaults.standard.double(forKey: _totalValueOfAllTips)                       // read from user default when view loads


Based on my research and the errors Xcode gives me, I am not putting the UserDefault commands within the proper scope, because I'm sure my syntax is correct.

>and the errors Xcode gives me


Care to show those?


As for saving 'when viewcontroller is closed'...that could mean different things, in which case flags might help cover them all.

Accepted Answer

Where did you define _totalValueOfAllTips ?


That must be a string.


So, you should have


class TipTrackerViewController: UIViewController, TipTrackerProtocol {
    let totalValueOfAllTipsKey = "totalTips"     // Must be visible in the whole class scope
    var numberOfTipsTracked = 0       // store as user default


Then, use:

UserDefaults.standard.set(totalValueOfAllTips, forKey: totalValueOfAllTipsKey)     // write to user defaults
UserDefaults.standard.double(forKey: totalValueOfAllTipsKey)

This fixed my error, but when I press the back button on the view controller, it doesn't save the data from before. I am calling both user defaults in ViewDidLoad()

You should call the set userDefaults from the IBAction of the button.

Where to implement UserDefaults to save data when viewcontroller is closed
 
 
Q