Codable and structs

the documentation abruptly ends where my questions begin.

I have a mac os X document app. please refer to this if your experience is in IOS, it most likely will not be of very much help.


Background:

I am currently trying to adopt Codable, very much against my better instincts, because a number of subsystems in the Mac OS have been fairly recently uncerimoniously kicked down a flight of stairs when NSCoding was damaged. I backed into the knowlege that NSCoding no longer worked for custom classes. The system returns nil, on decoding these classes. Then I got neck deep in a mess trying to implement NSSecureCoding, which went precisely nowhere.

Anyway, If I want to continue working on any aspect of my app, I need to get archiving working. NSCoding, and NSSecureCoding don't work. The only other option is Codable. This is not the time to take my fledgling app and cut it's head off and try to graft a new on on it... But I have no choice.


the issue:

public struct BKColorComponents: Codable{
    public var red : CGFloat = 1.0
    public var green : CGFloat = 1.0
    public var blue : CGFloat = 1.0
    public var alpha : CGFloat = 1.0
}


by all accounts of online tutorials and the byzantine documentation from Apple HQ, this struct is now Codable compliant.

except that it crashes the file save. Here's that code in context:


open var components : BKColorComponents = BKColorComponents()


enum CodingKeys: String, CodingKey {
        case components
    }



public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(components, forKey: .components)
    }



my document class saving method:

override func data(ofType typeName: String) throws -> Data {
    return try PropertyListEncoder().encode(docData)
}


there's not much documentation about how to do that at all. I'm not fond of the PropertyListEncoder, but whatever. I'm following along with someone's tutorial, because Apple has dropped the ball in a major way on the subject of archives and serialization. Believe me, I'd rather look up the answer than ask in the forums, I tried. The most important thing about this is that it serializes, and it runs through a host of other objects before it hits this structure, and they all encode without complaint. But for some reason, as soon as this struct comes up, the app crashes.


there's no errors, no red flags of any kinds.

I just get this error during a save (which seems to take about 10 times longer than it should):

Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeef3fff50)


thoughts appreciated.

A few fragmens of code would not be helpful to find what's happening.


What is `docData`? In which type, `open var components...` ... `func encode(to:)` exist?

don't worry about that. I discovered the hard way that Codable isn't as robust as NSCoding.

circular references such as adding a parent property to a child class, just create unending loops. NSCoding was smart enough to make a single record for the parent, and just put an identifier in other references to the same object. Not a win for Codable.


But I have a new issue. JSONEncoder is uninteresting to me, I am not working within the assumed framework of archiving, so I have to build my own encoder, and add keyed data. documentation is nonexistent. examples (from apple) are non existent. tutorials do not cover anything I am trying to do, and every single one of them relates to JSONEncoder.


I need to figure out:

1. besides PropertyListEncoder and JSONEncoder, what are the other options for encoding data? (unsuprisingly... undocumented, and there seems to only be references to those two) Encoder shows all the signs of not being a concrete class.

2. what are the steps to create an encoder, encode keyed data, and wind up with a Data object at the end.


where is my problem?

public func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any?

like I said: I am not working within the assumed framework of archiving. normal tutorials do not cover the requirements.

1. besides PropertyListEncoder and JSONEncoder, what are the other options for encoding data?

None in the Swift Standard Library. Find some third party library, or make your own.


2. what are the steps to create an encoder, encode keyed data, and wind up with a Data object at the end.

Swift Standard Library is open-sourced, checking the source code of two known encoders would be some help.

github.com/apple/swift-corelibs-foundation/blob/master/Foundation/JSONEncoder.swift

github.com/apple/swift-corelibs-foundation/blob/master/Foundation/PropertyListEncoder.swift

In my opinion, I would not try to create my own encoders unless I have enough time just for exploring how encoders work inside.


NSCoding was smart enough to make a single record for the parent, and just put an identifier in other references to the same object.


Then NSCoding seems to be your only option.

NSCoding has taken a bullet to the head.

Apple has disabled it. Which is why I am doing this in the first place

Why do you think it's disabled?

because it no longer decodes custom class types. it returns nil instead.

is it a bug? is it deliberate? I don't care.

I just spent 4 days trying to figure it out. adding NSSecureCoding support fixed it, with little or no Other code changes.


NSSecureCoding has it's own mindBlowing limitations. So halfway through refactoring my project, just before hitting the steep end of the hockeystick in that graph, I looked into Codable. No point sticking with teh past if it doesn't work.

because it no longer decodes custom class types. it returns nil instead.


It may be a bug, or you may be doing something wrong. You are not showing enough info about it.

ha!


go here:

https://forums.developer.apple.com/thread/116282


scroll down about 1/3rd of the way. There's a playground that illustrates the issue.

right beneath it, there's a proof of concept NSSecureCoding solution.

I have read it already.

I can't explain it. I just have to accept it as a boundry.

The thread is just explaing you were doing something wrong in a experimental code.

Why don't you try to find the right way to archive/unarchive your actual data with NSCoding?

relatively back on topic...

I'm trying to connect the design pattern :


public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)


with my pasteboard code.

I have to instantiate an encoder, and a container.

then I have to create the Data object.


i "think" the encoder they use is the propertyListEncoder, from what you've said, but they do not take a container.


so there's a lot of space between what is known, what is documented, and what the examples say.

as I stated,

changing the code to NSSecureCoding, solved the issue.


meanwhile, I am faced with 3 options : NSCoding, NSSecureCoding, and Codable. None of them have adequate documentation. the Docs for NSCoding all state very plainly: that it should not be used. I tried SecureCoding, and the workload to just make it work, was extreme. I'm trying to learn new, future-proof, technologies. This is a good thing.


I don't get any benefit sticking with NSCoding. I get a huge liability.

i "think" the encoder they use is the propertyListEncoder


Seems your assumption is wrong. If you are to create a return value for `pasteboardPropertyList(forType:)`, the value may be any type of plist-compatible types, NSString, NSNumber, NSArray, NSDictionary, NSDate and NSData.


When you return `Data`, it is bridged to NSData as you know. And the data is not necessarily archived with PropertyListEncoder.


None of them have adequate documentation.


I do strongly agree on this point, as Claude31 in the previous thread.

Have you started this new thread just to complain about the documentation quality of Apple?

Or find someting other?


If the latter, someone (including me) may be able to help if you provide enough info about what you want to find.

I want to look IN the docs, and find answers. I don't want to be bothering 3rd party developers with questions that should not be difficult to answer. Am I complaining? ok. I apologise for that.


I shouldn't have to guess. I've been guessing for a week. I didn't even start out with file saving. I'm working on drag n drop. file saving was an attempt to shake out the issues taht I couldn't get a handle on in the drag n drop.


My app saves its data now. I've figured it out, some hours ago.

I've since moved onto trying to cobble together support for NSPasteboard. I've done that wrong in the past, and I'm trying to take the full swing this time around. I could easily fake it. do what I did in the document :


return try PropertyListEncoder().encode(docData)


but that's problematic because pasteboard behaves slightly different from everything else. it wants a Data object that has keys for the properties in your object, not the object itself, which is more like the code in an encode method:


public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)
        try container.encode(hint, forKey: .hint)
        try container.encode(modes, forKey: .modes)
        try container.encode(baseMode, forKey: .baseMode)
        try container.encode(selectedMode, forKey: .selectedMode)
        try container.encode(subCons, forKey: .subCons)
    }


I did just find out that PasteboardEncoder is not the same thing as Encoder. But Encoder has no accessible initializers. That seems to indicate it's not a concrete class.

Codable and structs
 
 
Q