3
class AddElementVC: UIViewController {

    // textfields and some other functions are defined here 

    @IBAction func addElement(sender: UIBarButtonItem) {

        let newElement = Element(/* properties are defined here */)
        var theDict = NSUserDefaults.standardUserDefaults().dictionaryForKey(tableViewData) as [String: [Element]]

        if var theArray = theDict[newElement.someProperty] {
            theArray += newElement
            theDict[newElement.someProperty] = elementArray
        } else{
            theDict[newElement.someProperty] = [newElement]
        }

        NSUserDefaults.standardUserDefaults().setObject(theDict, forKey: tableViewData)

    }
}

EXPLAINATION OF THE CODE ABOVE:

My tableView-app receives its data from a dictionary of type [String: [Element]] (Element is a custom class), which is loaded from the userDefaults. In a second viewController you can fill out some textFields and finally press a UIBarButtonItem. From the textFields' data a new instance of the class "Element" is created and added to "theDict" at the key of one of the "newElement"'s properties. All that works fine as I checked it with console-outputs, but I am not able to finally save the edited dictionary in the userDefaults. There are no syntax-errors, but when I use the app and try to to add another element to the Dictionary via the second viewController, the following error is displayed:

ERROR-MESSAGE:

NSForwarding: warning: object 0xdcb002c of class '_TtC12myApp7Element' does not implement methodSignatureForSelector: -- trouble ahead

Researches didn't help me to understand the error-message.

EDIT

import Foundation

class Element: NSCoding {

enum State: String {
    case state1 = "state1"
    case state2 = "state2"
}

let state: State
// some more properties

init(state: String /* something more */ ){
    self.state = State.fromRaw(state)!
    // something more
}

init(coder aDecoder: NSCoder!) {
    self.state = aDecoder.decodeObjectForKey("state") // displays error: 'AnyObject' is not convertible to 'Element.State'
    // something more
}

func encodeWithCoder(aCoder: NSCoder!) {
    aCoder.encodeObject(self.state, forKey: "state") // displays error: Extra Argument 'forKey' in call
    // something more
}

}
Nick Podratz
  • 652
  • 9
  • 18

1 Answers1

14

From the NSUserDefaults documentation:

The value parameter can be only property list objects: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and NSDictionary objects, their contents must be property list objects. See “What is a Property List?” in Property List Programming Guide.

You cannot store a dictionary with elements of Element in the dictionary.

One option is to make sure Element implements the NSCoding protocol and then use NSKeyedArchiver to convert your dictionary into NSData:

// Storing the dictionary
var data = NSKeyedArchiver.archivedDataWithRootObject(theDict)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: tableViewData)

// Retrieving the dictionary
var outData = NSUserDefaults.standardUserDefaults().dataForKey(tableViewData)
var dict = NSKeyedUnarchiver.unarchiveObjectWithData(outData)

Your protocol implementation would look like something like this:

init(coder aDecoder: NSCoder!) {
    self.state = State.fromRaw(aDecoder.decodeObjectForKey("state") as String)
}

func encodeWithCoder(aCoder: NSCoder!) {
    aCoder.encodeObject(self.state.toRaw(), forKey: "state")
}

You cannot store an Enum, but you can store the raw representation of it which is a string.

drewag
  • 93,393
  • 28
  • 139
  • 128
  • thank you very much. But as I am really new to iOS dev I seem to be incapable of implementing this protocol correctly. Might you take a look at my attempt which is attached to my question above, please – Nick Podratz Aug 03 '14 at 23:32
  • @NickPodratz What error / problems are you running into with implementing the protocol? – drewag Aug 03 '14 at 23:34
  • state = State.fromRaw(aDecoder.decodeObjectForKey("state") as String)! this way it works. But the initial error message remains and the app crashes. – Nick Podratz Aug 03 '14 at 23:47
  • You must bridge with `@objc`, see http://stackoverflow.com/questions/24659609/how-to-archive-and-unarchive-custom-objects-in-swift-or-how-to-save-custom-obje?rq=1 – Rikkles Aug 24 '14 at 09:44
  • Remark: the raw representation of an Enum is not always a String. – Frederic Adda Sep 23 '16 at 12:05