Home > Back-end >  Swift Codable struct recursively containing itself as property
Swift Codable struct recursively containing itself as property

Time:08-11

I have a rather large struct that conforms to Codable, and one of its properties needs to be of the same type as itself. A shortened sample of what I'm trying to do is shown below:

struct Message: Codable {
    let content: String
    // ...other values
    let reference: Message // <-- Error: Value type 'Message' cannot have a stored property that recursively contains it
}

Swift doesn't seem to allow a struct to recursively contain itself as one of its values. Is there any way to get this working besides creating a complete duplicate Message struct (which turns this into a chicken and egg problem where the duplicate struct cannot contain itself etc). Not creating a duplicate struct also allows me to reuse SwiftUI code that takes in and renders a Message struct.

CodePudding user response:

A simple way is to just change the struct into a class:

class Message: Codable {
    let content: String
    // ...other values
    let reference: Message? // optional - the recursion has to end somewhere right?
}

But this could break other parts of your code, since structs and classes have vastly different semantics.

An alternative would be to make a reference type Box:

class Box<T: Codable>: Codable {
    let wrappedValue: T
    required init(from decoder: Decoder) throws {
        wrappedValue = try T(from: decoder)
    }
    
    func encode(to encoder: Encoder) throws {
        try wrappedValue.encode(to: encoder)
    }
}

Then,

struct Message: Codable {
    let content: String
    // ...other values
    
    let boxedReference: Box<Message>?
    
    // you can still refer to 'reference' as 'reference' in your swift code
    var reference: Message? { boxedReference?.wrappedValue }
    
    enum CodingKeys: String, CodingKey {
        case content, boxedReference = "reference"
    }
}

CodePudding user response:

I'm answering my own question after a tip-off by @Sweeper.

By converting the Message struct into a class and changing several extensions, Message recursively containing itself as a property is possible. This is possible since class is a reference type, which are allowed to recursively contain themselves. So, the code below will compile:

class Message: Codable { // <-- Message is now a class
    let content: String
    // ...other values
    let reference: Message
}
  • Related