15
class Item
{
    var name: String
    var collectionId: Int

    init(name: String, collectionId: Int) {
        self.name = name
        self.collectionId = collectionId

    }
}

class ViewController:UIViewController
{
    var itemList = [Item]()

    func recievedResults()
    {
        let defaults = NSUserDefaults.standardUserDefaults()
        defaults.setObject(self.itemList, forKey: "myList")
    }        
}

I get an error "property lists cannot contain objects of type 'CFType'"

How do I store and get the itemList to NSUserDefaults?

2
  • 3
    In Objective-c, NSUserdefaults accepts only primitif datas: NSArray, NSString ,... To save custom object in NSUserdefaults, you have to use NSKeyedArchiver (and NSKeyedUnarchiver) with adding Coder & Decoder method to your class. I think it applies the same rule in Swift. Commented Mar 23, 2015 at 16:58
  • You should not store an NSArray in NSUserDefaults. Use a plist instead. Commented Mar 23, 2015 at 17:30

4 Answers 4

9

Since Swift 4 it is so much more easy using Codable:

import Foundation

//Declaration
struct Item: Codable {
    var name: String
    var collectionId: Int
}

//Initialization
let itemA: Item = Item.init(name: "Bob A.", collectionId: 1)
let itemB: Item = Item.init(name: "Bob B.", collectionId: 1)
let items: [Item] = [itemA, itemB]

//Storing Items
if let encoded = try? JSONEncoder().encode(items) {
    UserDefaults.standard.set(encoded, forKey: "items")
}

//Retrieving Items
do {
    let storedObjItem = UserDefaults.standard.object(forKey: "items")
    let storedItems = try JSONDecoder().decode([Item].self, from: storedObjItem as! Data)
    print("Retrieved items: \(storedItems)")
} catch let err {
    print(err)
}
Sign up to request clarification or add additional context in comments.

Comments

5

First, you have to inherit Item from NSObject, with coder & decoder methods:

class Item :NSObject
{
    var name: String = ""
    var collectionId: Int = 0

    init(name: String, collectionId: Int) {
        self.name = name
        self.collectionId = collectionId
    }

    init(coder decoder: NSCoder) {
        self.name = decoder.decodeObjectForKey("name") as String
        self.collectionId = decoder.decodeIntegerForKey("collectionId")
    }

    func encodeWithCoder(coder: NSCoder) {
        coder.encodeObject(self.name, forKey: "name")
        coder.encodeInt(Int32(self.collectionId), forKey: "collectionId")
    }
}

Then, these are functions to insert & retrieve from NSUserDefaults.

func insertItems()
    {
        let defaults = NSUserDefaults.standardUserDefaults()

        let data = NSKeyedArchiver.archivedDataWithRootObject(itemList)
        NSUserDefaults.standardUserDefaults().setObject(data, forKey: "myList")
    }

func retrieveItems()
{
    let defaults = NSUserDefaults.standardUserDefaults()

    if let data = NSUserDefaults.standardUserDefaults().objectForKey("myList") as? NSData {
        let _mySavedList = NSKeyedUnarchiver.unarchiveObjectWithData(data) as [Item]
    }
}

Example:

let item1 = Item(name: "Item 1", collectionId: 1)
let item2 = Item(name: "Item 2", collectionId: 2)

itemList.append(item1)
itemList.append(item2)


insertItems()
retrieveItems()

Comments

3

You need to implement protocol NSCoding for your custom class. Please see this Swift example.

Comments

2

As pointed out in this stack overflow question you can't store custom objects in NSUserDefaults.

But you can make your class NSCoding compliant and store the NSData from NSKeyedArchiver.archivedDataWithRootObject(...):

class Item : NSObject, NSCoding {
    var name: String
    var collectionId: Int

    init(name: String, collectionId: Int) {
        self.name = name
        self.collectionId = collectionId
    }

    required init(coder aDecoder: NSCoder) {
        collectionId = aDecoder.decodeIntegerForKey("collectionId")
        name = aDecoder.decodeObjectForKey("name") as String
    }

    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(name, forKey: "name")
        aCoder.encodeInteger(collectionId, forKey: "collectionId")
    }
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        recievedResults()
    }

    var itemList = [Item]()

    func recievedResults()
    {
        self.itemList.append(Item(name: "test", collectionId: 2))

        let defaults = NSUserDefaults.standardUserDefaults()
        defaults.setObject(NSKeyedArchiver.archivedDataWithRootObject(itemList), forKey: "myList")

        let data = defaults.objectForKey("myList") as NSData
        if let decodedList = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? [Item]{
            print("first item: \(decodedList)")
        }
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.