Search code examples
arraysnsuserdefaultsswift4xcode9tabbarcontroller

How to save an Array in UserDefault


I'm using a UITabBarController to create a contact list, but when I'm trying to save the array to load the data when I restart the app is giving me problems where the data isn't displayed. I'm using UserDefaults to save the data and the restore when the app is restarted.

In this code I sent data from a textfield to the array named list.

import UIKit

class NewContactoViewController: UIViewController {

@IBOutlet weak var input: UITextField!

@IBAction func add(_ sender: Any) {
    if (input.text != "") {

        list.append(input.text!)
        UserDefaults.standard.set(list, forKey: "SavedValue")
        input.text = ""
    }
}
}

In this code I'm printing the data in a table, and trying to save it with user defaults.

import UIKit

var list = [String]()

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if let x = UserDefaults.standard.object(forKey: "SavedValue") as? String {
        return (x.count)
    }
    return (0)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
    if let x = UserDefaults.standard.dictionary(forKey: "SavedValue") as? String {
        cell.textLabel?.text = [x[indexPath.row]]
    }
    return(cell)
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == UITableViewCellEditingStyle.delete {
        list.remove(at: indexPath.row)
        myTableView.reloadData()
    }
}

override func viewDidAppear(_ animated: Bool) {
    myTableView.reloadData()
}

@IBOutlet weak var myTableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

}
}

Solution

  • You are saving an array of strings but you are reading a single string (or even a dictionary) which obviously cannot work. There is a dedicated method stringArray(forKey to read a string array.

    Apart from the issue never read from UserDefaults to populate the data source in the table view data source and delegate methods, do it in viewDidLoad or viewWillAppear for example

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if let savedArray = UserDefaults.standard.stringArray(forKey: "SavedValue") {
            list = savedArray
        }
        myTableView.reloadData()
    }
    

    Put the data source array in the view controller. A global variable as data source is very bad programming habit.

    class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
        var list = [String]()
        ...
    

    In numberOfRowsInSection return the number of items in list and return is not a function, there are no parentheses

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return list.count
    }
    

    Same in cellForRow. Get the item from list and use reusable cells and again, return is not a function.

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = list[indexPath.row]
        return cell
    }
    

    Note :

    Consider that UserDefaults is the wrong place to share data between view controllers. Use segues, callbacks or protocol / delegate.