3

So I have an array of custom objects. When I try to save the array to NSUserDefaults, I have to archive them. Here is what I am doing to archive the array of my custom objects :

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    //3

    var allMessages = defaults.objectForKey(UserDefaultsMesssageKey) as! [AddMessageViewController.harassText]
    var aMessage = AddMessageViewController.harassText(phoneNumber: 0, message: "1", frequency: 1, active: 1)
    allMessages.append(aMessage)
    saveMessage(allMessages)


    return (allMessages.count)
}

func archiveMessage(message:[AddMessageViewController.harassText]) -> NSData {
    let archivedObject = NSKeyedArchiver.archivedDataWithRootObject(message as NSArray)
    return archivedObject
}
func saveMessage(messages: [AddMessageViewController.harassText]) {
    let archivedObject = archiveMessage(messages)
    defaults.setObject(archivedObject, forKey: UserDefaultsMesssageKey)
    defaults.synchronize()
}
func retrieveData()-> [AddMessageViewController.harassText]? {
    if let unarchiveObject = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaultsMesssageKey) as? NSData {
        return NSKeyedUnarchiver.unarchiveObjectWithData(unarchiveObject) as? [AddMessageViewController.harassText]
    }
    return nil
}

The flow is :

  1. On saveMessage call, pass the custom array in to get archived. Here is what the array looks like to GDB. You can see that it is an array, and it has 1 class object contained in it.

    ([Harass_Your_Kate.AddMessageViewController.harassText]) $R0 = 1 value { [0] = 0x00007fa5ab409600 { ObjectiveC.NSObject = { isa = Harass_Your_Kate.AddMessageViewController.harassText } phoneNumber = 0 message = "1" frequency = 1 active = 1 } }

  2. The array gets passed into archiveMessage. The code immediately fails on the NSKeyedArchiver method. The Error message is as follows :

    [_TtCC16Harass_Your_Kate24AddMessageViewController10harassText encodeWithCoder:]: unrecognized selector sent to instance 0x7fa5ab409600
    

    2016-07-15 16:31:26.019 Harass Your Kate[5858:7847495] * -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it. 2016-07-15 16:31:26.023 Harass Your Kate[5858:7847495] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_TtCC16Harass_Your_Kate24AddMessageViewController10harassText encodeWithCoder:]: unrecognized selector sent to instance 0x7fa5ab409600

I appreciate all of the help, thank you in advance!

EDIT: Here is the definition of my object:

class Text : NSObject {
    var phoneNumber = 0             //Text address to send to
    var message: String = ""        //Message to be sent
    var frequency = 24              //Number of hours between messages (usually a multiple of 24 - 24 = daily)
    var active = 0                  // 0 if cancelled, 1 if active

    init(phoneNumber: Int, message: String, frequency: Int, active: Int ){
        self.phoneNumber = phoneNumber
        self.message = message
        self.frequency = frequency
        self.active = active
    }
}
Code Wiget
  • 1,540
  • 1
  • 23
  • 35
  • 2
    We need to see the NSCoding methods for your custom object. (`initWithCoder` and `encodeWithCoder`). – Duncan C Jul 15 '16 at 20:57
  • I don't have either of those things. – Code Wiget Jul 15 '16 at 22:44
  • 1
    That's the problem then. Show us the definition of your custom class then. (Edit your question to add the code, don't post it in a comment.) – Duncan C Jul 16 '16 at 00:31
  • 1
    You can't use `NSCoding` to convert an "object graph" to `NSData` unless every object conforms to NSCoding (which means that it has to implement `initWithCoder` and `encodeWithCoder`.) If you try to encode a container (like an array or dictionary) of objects, all the objects in the container must conform to `NSCoding` or it won't work. – Duncan C Jul 16 '16 at 00:38
  • @DuncanC Thank you for your help. I included in my edit the definition of my custom class. I am very confused about what you mean with NSCoding, as I have never used that before, and certainly not in the context of saving a custom class array. But again, thank you for taking the time to help me. I look forward to hearing from you again – Code Wiget Jul 18 '16 at 15:17

1 Answers1

3

NSKeyedArchiver serializes objects by taking advantage of methods you have to provide by conforming to the NSCoding protocol. It can't automatically infer how to save your custom classes; you have to implement that yourself as follows:

class Text : NSObject, NSCoding {
    var phoneNumber = 0             //Text address to send to
    var message: String = ""        //Message to be sent
    var frequency = 24              //Number of hours between messages (usually a multiple of 24 - 24 = daily)
    var active = 0                  // 0 if cancelled, 1 if active

    init(phoneNumber: Int, message: String, frequency: Int, active: Int ){
        self.phoneNumber = phoneNumber
        self.message = message
        self.frequency = frequency
        self.active = active
    }

    required init?(coder aDecoder: NSCoder) {
        phoneNumber = aDecoder.decodeInteger(forKey: "phoneNumber")
        message = aDecoder.decodeObject(forKey: "message") as! String
        frequency = aDecoder.decodeInteger(forKey: "frequency")
        active = aDecoder.decodeInteger(forKey: "active")
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(phoneNumber, forKey: "phoneNumber")
        aCoder.encode(message, forKey: "message")
        aCoder.encode(frequency, forKey: "frequency")
        aCoder.encode(active, forKey: "active")
    }
}

As you can see, the initializer reads from the coder and creates and instance, whereas the encode method saves relevant information to the coder. In a simple class like this, their implementations may seem obvious, but in a more complex custom type, there are non-trivial encoding decisions to make.

andyvn22
  • 14,696
  • 1
  • 52
  • 74
  • Thank you! This works great. I am also implementing it with CoreData, which seems much simpler for storing custom information – Code Wiget Jul 18 '16 at 19:23