Issue Storing "Favourite" button state in NSUserDefaults

Viewed 53

Not sure why my button state is not being stored. The image changes from unfilled to filled however when I click out of the view controller and back in it is reset to unfilled.

var recipeFav = UserDefaults.standard.bool(forKey: "recipeFav")

@IBAction func FavButtonTapped(_ sender: Any) {

        if recipeFav {
            let image = UIImage(named: "starNonfill")
            (sender as AnyObject).setImage(image, for: .normal)
        } else {
            let image = UIImage(named: "starFilled")
            (sender as AnyObject).setImage(image, for: .normal)
        }

        recipeFav = !recipeFav
        UserDefaults.standard.set(recipeFav, forKey: "recipeFav")
        UserDefaults.standard.synchronize()
    }
1 Answers

Your main problem is that you don't seem to have any code that sets the button image based on the boolean when the view appears - you retrieve it from user defaults but only set the image once the user taps on the button.

You can make a few other changes too:

  • By convention, in Swift, functions and properties start with a lower case letter - this helps you distinguish them from types.
  • You can declare your action function as receiving sender as a UIButton, avoiding the need to downcast.
  • You can use .toggle() to toggle a boolean
  • You don't need to call synchronize().

I would refactor the image setting into its own function and use a didSet handler to call it and save the boolean whenever the value changes. You also need to fetch the current value from viewDidAppear to ensure the initial state is set correctly.


var recipeFav = false {
    didSet {
        UserDefaults.standard.set(recipeFav,forKey:"recipeFav")
        self.updateFavouriteButton()
    }
}

@IBOutlet weak var favouriteButton: UIButton!

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.recipeFav = UserDefaults.standard.bool(forKey:"recipeFav")
}

@IBAction func favButtonTapped(_ sender: UIButton) {
    self.recipeFav.toggle()
}

private func updateFavouriteButton() {
    let image = self.recipeFav ? UIImage(named: "starNonfill") : UIImage(named: "starFilled")
    self.favouriteButton.setImage(image, for: .normal)
}
Related