Just for completeness I’d like to show in this post how to make structs serializable in the same manner as enums (see this post). Again, we transform the struct to a dictionary of NSCoding compliant types and give it to an NSCoder (e.g. NSKeyedArchiver) for coding. The other way round we get the decoded dictionary and initialize the desired struct with it. Sounds simple, doesn’t it. Let’s start.
As for enums we have to implement the protocol „PropertyListReadable“ for our struct. As a reminder here is the protocol definition:
1 2 3 4 5 6 |
typealias PropertyList = [String : Any] protocol PropertyListReadable { // this protocol can be applied to any struct or enum func propertyListRepresentation() -> PropertyList init?(propertyListRepresentation:PropertyList?) } |
As an example let’s create a struct „Person“ that contains a name, an age and the mood of a person. The mood should be represented by the enum „Happiness2“ from my previous posts. So you can see that this mechanism can be nested as well.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
struct Person: PropertyListReadable { let name: String var age: Int var mood: Happiness2 init(withName name: String, andAge age: Int, andMood mood: Happiness2) { self.name = name self.age = age self.mood = mood } init?(propertyListRepresentation: PropertyList?) { guard let values = propertyListRepresentation else {return nil} // did we really get a dictionary? if let name = values["name"] as? String { // decode all properties self.name = name } else {return nil} if let age = values["age"] as? Int { self.age = age } else {return nil} if let pMood = values["mood"] as? PropertyList, // the decoded enum in its dictionary form let mood = Happiness2(propertyListRepresentation: pMood) { // transform it to the enum self.mood = mood } else {return nil} } func propertyListRepresentation() -> PropertyList { var representation: PropertyList = [:] // this will be the resulting dictionary representation["name"] = name // map all properties to dictionary entries representation["age"] = age // the enum is coded in its dictionary form representation["mood"] = mood.propertyListRepresentation() return representation } } |
Let’s look at the encoding part first (lines 26 – 32). Here we have to transform the struct to a dictionary of NSCoding compliant types. Every property will be an entry in the dictionary with an identifying key (String) and its value. For the name (String) and the age (Int) that’s simple. The mood is an enum and its value is not NSCoding compliant by itself. But since we have made „Happiness2“ PropertyListReadable we can put its perpertyListRepresentation into the dictionary.
For the init part (lines 12 – 24) we make it the other way round. First we have to check that we’ve really got a dictionary for initializing the struct. Then we pick one by one the properties from the dictionary and fill the struct. For the property mood we do not get the enum itself from the dictionary but its propertyListRepresentation. From that we can initialize the enum mood. That’s it.
Let’s make a check and create an instance of our struct, encode it and decode it finally.
1 2 3 4 5 6 7 |
let me = Person(withName: "Frank-Peter", andAge: 60, andMood: Happiness2.Happy(ofGrade: 5)) let data = NSKeyedArchiver.archivedData(withRootObject: me.propertyListRepresentation()) if let person = Person(propertyListRepresentation: NSKeyedUnarchiver.unarchiveObject(with: data) as? PropertyList) { Swift.print("person: \(person)") } |
Everything works fine. The decoded object is equal to the original one. Here is the playground of this example: