Andy Lebo Andy Lebo - 1 year ago 55
Swift Question

NSUserDefaults Loading Issue

Whenever I open my app, it doesn't load my array values because the != nil function isn't called. Is there anything I can do about this?


override func viewDidLoad() {
// Do any additional setup after loading the view, typically from a nib.

var toDoData = NSUserDefaults.standardUserDefaults()
if (toDoData.valueForKey("TDDATA") != nil){
todos = toDoData.valueForKey("TDDATA") as! NSArray as! [TodoModel]

if todos.count != 0{
toDoData.setValue(todos, forKeyPath: "TDDATA")

Don't worry about the table. It populates perfectly. I just need the loading data issue fixed.

Code included in your answer helps a lot!



Here is the TodoModel:

import Foundation
import UIKit

class TodoModel : NSObject, NSCoding {
var id: String
var image: String
var title: String
var desc: String
var scores: String

init (id: String, image: String, title: String, desc: String, scores: String) { = id
self.image = image
self.title = title
self.desc = desc
self.scores = scores



Answer Source

valueForKey and setValue:forKeyPath are KVC (Key Value Coding) methods (read here and here). It will not help you read/write to the user defaults database.

Looking in the NSUserDefaults documentation, there are a number of methods available for getting and setting values in the defaults database. Since you are using arrays, we will use:

  • arrayForKey to get.
  • setObject:forKey to set. (There is no array-specific setter)

EDIT: Try this in your viewDidAppear. Here we check if we have data, and if we do, we store it. If we don't have data, then check if the defaults database has some saved. If it does, use it instead. It would be advantageous to only load data from the defaults database in viewDidLoad, and then save in viewDidAppear or even better, a function which is called when a todo is added.

override func viewDidAppear(animated: Bool) {

    let defaults = NSUserDefaults.standardUserDefaults()
    if todos.count > 0 {
        // Save what we have
        let data = NSKeyedArchiver.archivedDataWithRootObject(todos)
        defaults.setObject(data, forKey: "TDDATA")
        print("saved \(todos.count)")
    } else if let storedTodoData = defaults.dataForKey("TDDATA"),
        storedTodos = NSKeyedUnarchiver.unarchiveObjectWithData(storedTodoData) as? [TodoModel] {
        // There was stored data! Use it!
        todos = storedTodos
        print("Used \(todos.count) stored todos")

In addition, we must implement the NSCoding protocol in your model. This should be something like this:

class TodoModel: NSObject, NSCoding {
    var myInt: Int = 0
    var myString: String?
    var myArray: [String]?

    required init?(coder aDecoder: NSCoder) {
        myInt = aDecoder.decodeIntegerForKey("myInt")
        myString = aDecoder.decodeObjectForKey("myString") as? String
        myArray = aDecoder.decodeObjectForKey("myArray") as? [String]

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeInteger(myInt, forKey: "myInt")
        aCoder.encodeObject(myString, forKey: "myString")
        aCoder.encodeObject(myArray, forKey: "myArray")

(Of course, replace myInt, myString, myArray, etc, with whatever properties your model might have.)