NSKeyedUnarchiver failing to unarchive custom types or Arrays

I'm fit to be tied, so I apologise for any harsh language.


I have an NSCoding compliant class hierarchy (mac os + swift)

And I am adding support for drag n drop.


This requires adding NSPasteboardReading/NSPasteboardWriting

which has been an abysmal experience. I got it working, but not correctly. Now I'm trying to do it right. And something that SHOULD just work, in the context of doing this work is failing to produce expected results. I've already spent too much time trying to guess what the ---- is going on.


upon completion of my drop, the source object is asked to provide a "propertyList." I used to encode my entire object and then in the init(withPasteboard) method I would copy the values from the instantiated resulting object to self. Which is a stupid and horrible way of doing the work, But at the time: after 2 weeks of literally trying random things with the pasteboard (lack of examples and documentation) was the only thing that worked.


Now, I am encoding the properties of the object, which includes 2 arrays of custom objects (which are both NSCoding compliant, and have been observed correctly encoding and decoding thousands of times.)


so it looks like this:

    func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
        do {
            let archiver = NSKeyedArchiver(requiringSecureCoding: false)
            archiver.encode(name, forKey: "name")
            archiver.encode(self.type, forKey: "type")
            archiver.encode(children, forKey: "children")
            // the problem is channels as follows:
            archiver.encode(channels, forKey: "channels")
            archiver.finishEncoding()
            let data = archiver.encodedData
            
           return data
        } catch {
            //debugPrint("archiveFailed for drag of node in toolbox")
        }
        return nil
    }


    required init?(pasteboardPropertyList propertyList: Any, ofType type: NSPasteboard.PasteboardType) {
        super.init()
        if let data = propertyList as? Data{
            do {
                //let newObject = try NSKeyedUnarchiver.unarchivedObject(ofClass: docDataObj.self, from: data)
                    let unarchiver = try NSKeyedUnarchiver.init(forReadingFrom: data)
                        self.name = unarchiver.decodeObject(forKey: "name" ) as! String
                        self.type = unarchiver.decodeObject(forKey: "type" ) as! String
                    
                    if unarchiver.containsValue(forKey: "channels"){
                        let anObj = unarchiver.decodeObject(forKey: "channels" )
                        if anObj != nil{
                            if let objTest = anObj as? [docDataChannel]{
                                self.channels = objTest
                            }
                        }
                    }
                    if unarchiver.containsValue(forKey: "chidlren"){
                        self.children = unarchiver.decodeObject(forKey: "children" ) as! [docDataObj]
                    }
            } catch  {
                //debugPrint("initing from property list failed trying to unarchive")
            }
        }
    }


you'll notice a little cottage industry popping up around the property "channels"

That's where I always recieve a nil value from the unarchiver. I tried unarchiving every single type (Bool, Double, Float...) that unarchiver supports. There's nothing to unarchive for that key.


But I set it. With classes that easily archive elsewhere. I have followed the debugger, and the encode method is called on the contents of the channels array. As far as can be told, the objects are encoded.


AND the unarchiver reports that there is a value for that key. every single time. it returns true.

the children array... that's empty in my testing, but there IS a value for THAT key.


so the unarchiver swears it has a value that it doesn't, that I can confirm is set. what gives?

Replies

more details:

there is no change if I convert the swift array to an NSArray.

Nor ist there any change if I change the type of object IN the Array.

Going to try changing the type and contents of "children" to see if it behaves the same.


children returns nil.


so this is not a problem with my code. It may be a misunderstanding of how NSPasteboardReadable/Writable works... or of what is required to use NSkeyedArchiver. It may be a failure to provide my own Data object for the unarchiver to write to... But ---- that. There's no property or method to call in order to do that. It's like someone neutered the NSKeyedArchiver class, and took more than they should have.


it's looking more and more like a bug.


if I try to encode a String, it encodes.

if I try to encode an Array of anything, it fails to encode (that is... NSKeyedArchiver fails to hold onto the encoded data)

if I try to encode a custom class of any kind, it fails to encode.


documentation for NSKeyedArchiver dates back to Obj-c, and excludes some major changes that have occurred since then. The resulting document indicates that what I am trying to do "should" work. But I cannot be certain, because it's probably 15 years old, and I know there are ommisions and additions since the writing of that document.


I know we don't generally work directly with NSKeyed(Un)Archiver as a rule. Tell that to the people who wrote the NSPasteboard/DragNDrop nightmare. I don't have a choice here. And it just isn't doing the simplest of things, and there is no other resource on the entire internet that can address this issue other than THIS forum. (do a google search. every post is about encoding root objects, which is the incorrect way to do this) I'm at a complete loss here.

I just checked the documentation from Apple.

https://developer.apple.com/documentation/foundation/nskeyedunarchiver/2962883-init

You set the requiresSecureCoding to false for NSKeyedArchiver.

So you also should set the requiresSecureCoding to false after you initialize NSKeyunarchiver.