Skip to content

Creating a Record Structure with Generic Types #1749

Closed
@Artem-Viveritsa

Description

@Artem-Viveritsa

Let's say I have this structure:

struct Block: Syncable, Transferable {
            
    static var databaseTableName: String { "blocks" }
    
    var id:              UUID = UUID()

    ...
    ...
    ...
    
    var type:            BlockType // enum
    var data:            String = ""
    var parentId:        UUID?
    
    
    static var parent: BelongsToAssociation<Block, Block> { belongsTo(Block.self, key: "parent") }
    var parent: QueryInterfaceRequest<Block> { request(for: Block.parent) }
    
    static var children: HasManyAssociation<Block, Block> { hasMany(Block.self, key: "children") }
    var children: QueryInterfaceRequest<Block> { request(for: Block.children) }
    
    
    enum Columns {
        static var type: Column     { Column(CodingKeys.type) }
        static var data: JSONColumn { JSONColumn(CodingKeys.data) }
        static var parentId: Column { Column(CodingKeys.parentId) }
    }
    
    
    init(_ type: BlockType) { self.type = type }
    
    init(_ data: any BlockData) {
        self.type = Swift.type(of: data).type
        setProperties(data)
    }

    
    func getData<T: BlockData>(as type: T.Type) -> T? {
        try? JSONDecoder().decode(T.self, from: data.data(using: .utf8)!)
    }
    
    mutating func setData<T: BlockData>(_ value: T) {
        self.type = T.type
        if let data = try? value.toJSON() {
            self.data = data
        }
    }
    
    mutating func setParent(_ parent: Block?) { parentId = parent?.id }

}

Where the data field is currently represented by a json string. To get this data, you need to call the appropriate methods. But I would like it to have the correct type depending on its content:

struct Block<T: BlockData>: Syncable, Transferable {
            
    ...

    var data: T // Codable

And if the request for blocks of the same type works well:

struct BlockRequest<T: BlockData>: ValueObservationQueryable {
    static var defaultValue: Block<T> { Block<T>(.empty) }
    let blockId: UUID
    init(_ blockId: UUID) { self.blockId = blockId }
    func fetch(_ db: Database) throws -> Block<T> { try Block.find(db, id: blockId) }
}

Then what about those requests that should return blocks of different types?

struct BlocksRequest: ValueObservationQueryable {
    
    static var defaultValue: [Block<any BlockData>]  { [] }

Errors:
Type 'BlocksRequest' does not conform to protocol 'Queryable'
Type 'any BlockData' cannot conform to 'BlockData'
Generic parameter 'T' could not be inferred
...

Ideally, I want to make it so that the type of the T block is determined based on its type field, so that the maximum amount of work is done on the request side and ready-made blocks with the correct types come to the interface. Is this even possible?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions