0

I'm making an app which uses the Blogger API. In the first tab, I can search for posts and display their contents. Also, I can add posts to the "Favorites" section in the second tab. All is working, until I close the app. After re-launching, the Favorites section is gone. I tried to implement UserDefaults so that the Favorites section does not become empty after killing the app, but it does not work.

This is the code for the button which adds the post to Favorites:

vc.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addTapped))

func addTapped() {
    offlineTitles.append(cellText)
    titlesArray.append(cellText)
    subtitlesArray.append(cellSubtitle)
    let defaults = UserDefaults.standard
    defaults.set(titlesArray, forKey: "title")
    defaults.set(subtitlesArray, forKey: "subtitle")
    NotificationCenter.default.post(name: .reload, object: nil)
    let ac = UIAlertController(title: "Added!", message: "Post added to favorites", preferredStyle: .alert)
    ac.addAction(UIAlertAction(title: "Great!", style: .default))
    present(ac, animated: true)
}

and this for the FavoritesViewController.swift :

import UIKit

var offlineTitles = [String]()
var titlesArray = [String]()
var subtitlesArray = [String]()


extension Notification.Name {
static let reload = Notification.Name("reload")
}

class OfflineViewController: UITableViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(reloadTableData(_:)), name: .reload, object: nil)
    self.tableView.allowsMultipleSelectionDuringEditing = false
}

func reloadTableData(_ notification: Notification) {
    self.tableView.reloadData()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return titlesArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "OfflineCell", for: indexPath)
    let defaults = UserDefaults.standard
    let userDefaultsTitleArray = defaults.array(forKey: "title") as? [String] ?? [String]()
    let userDefaultsSubtitleArray = defaults.array(forKey: "subtitle") as? [String] ?? [String]()
    let title = userDefaultsTitleArray[indexPath.row]
    let subtitle = userDefaultsSubtitleArray[indexPath.row]
    cell.textLabel?.text = title
    cell.detailTextLabel?.text = subtitle
    return cell
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let vc = FavouritesViewController()
    navigationController?.pushViewController(vc, animated: true)
}

override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true
}

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == UITableViewCellEditingStyle.delete {
        offlineTitles.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
    }
}
}
  • Possible duplicate of [(Swift) Storing and retrieving Array to NSUserDefaults](http://stackoverflow.com/questions/30118735/swift-storing-and-retrieving-array-to-nsuserdefaults) – MathewS Jan 08 '17 at 18:23
  • It's not a duplicate. In that post, there was an error. In my post, there is no error, but the code does not do what I want it to do. – user7391376 Jan 08 '17 at 18:29
  • BTW, after you fix this problem, I see that you have a delete operation. You probably should be updating your model objects and updating user defaults, there, too. – Rob Jan 08 '17 at 18:32
  • Yes, thank you for reminding! I have almost forgotten about that. – user7391376 Jan 08 '17 at 18:34
  • sorry, wrong answer URL, it's dup of this question: http://stackoverflow.com/questions/25179668/how-to-save-and-read-array-of-array-in-nsuserdefaults-in-swift (you need to use the `stringArray(forKey:)` method instead of `array(forKey:)`) – MathewS Jan 08 '17 at 18:41

1 Answers1

1

It appears that you're reading user defaults in cellForRowAt. This is not only inefficient (if you had five favorites, you'd be reading it in five times), but is at the wrong time. For example, what will numberOfRowsInSection return? By the time that's called, you haven't yet read the user defaults into your arrays.

You should read user defaults into your arrays in viewDidLoad (as well as possibly in your reloadTableData, too).

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • So how can I set the cell's title and subtitle in `viewDidLoad`? – user7391376 Jan 08 '17 at 18:33
  • Sorry. I asked you to write the code because I did not understand what should I do. – user7391376 Jan 08 '17 at 18:39
  • You have code to read user defaults into some local `userDefaultsTitleArray`. So (a) move that to `viewDidLoad`; and (b) rather than updating `userDefaultsTitleArray`, update `titlesArray`. Then `cellForRowAt` no longer needs any of that user defaults stuff, and just set the title and subtitle using `titlesArray` and `subtitlesArray`, accordingly. – Rob Jan 08 '17 at 18:42
  • Sorry again. I finally understood. Thank you so much! – user7391376 Jan 08 '17 at 18:42
  • Sorry for asking again. How should update user defaults in the delete operation? – user7391376 Jan 08 '17 at 19:54
  • @user7391376 - Just remove the entries from your various arrays and then re-save those arrays to user defaults. – Rob Jan 08 '17 at 20:09
  • 1
    Solved it. I have forgotten to use `defaults.set`. Thank you so much! – user7391376 Jan 08 '17 at 20:15
  • Last question. After I submit the app to the App Store and release an update, when users install the update the UserDefaults data will be lost? If yes, how can I prevent this? – user7391376 Jan 08 '17 at 21:12
  • If the user merely upgrades the app, user defaults (and all persistent storage like the app's Documents folder), is preserved. If, however, they manually delete the app at any point, this persistent storage (including user defaults) is lost. – Rob Jan 09 '17 at 00:56