Creating tables of subclasses in GRDB

I am new to SwiftUI and GRDB (using it for a school project), I have been struggling to store subclasses in my database.

This is my code:

class Item: Identifiable, Codable, Hashable, MutablePersistableRecord, FetchableRecord {
    
    // Defining properties
    var id = UUID().uuidString
    var name: String = ""
    var description: String = ""
    var dueDate: Date?
    var isCompleted: Bool = false
    var dateType: DateType = .due
    var color: PriorityColor = .none
    var tagID: String?
    
    // Initialisation of Item
    init(id: String = UUID().uuidString, name: String, description: String, dueDate: Date? = nil, isCompleted: Bool, dateType: DateType, color: PriorityColor, tag: Tag? = nil) {
        self.id = id
        self.name = name
        self.description = description
        self.dueDate = dueDate
        self.isCompleted = isCompleted
        self.dateType = dateType
        self.color = color
        self.tagID = tag?.id
    }
    
    // Conforming to Hashable and Equatable protocols
    func hash(into hasher: inout Hasher) {
        hasher.combine(self.id)
    }
    static func ==(lhs: Item, rhs: Item) -> Bool {
        return lhs.id == rhs.id
    }
    
}

// MARK: Enums for Item
extension Item {
    enum DateType: String, Codable, CaseIterable, Identifiable, DatabaseValueConvertible {
        case due = "Due" // The item is due on DueDate
        case completeBy = "Complete by" // The item should be marked as completed by DueDate
        case noDate = "No Date"
        var id: Self { self }
    }
    
    enum PriorityColor: String, Codable, CaseIterable, Identifiable, DatabaseValueConvertible {
        case red
        case yellow
        case green
        case none = "No Colour"
        var id: Self { self }
        var name: String { rawValue.localizedCapitalized }
        var color: Color {
            switch self {
            case .red:
                return .red
            case .yellow:
                return .yellow
            case .green:
                return .green
            default:
                return .gray
            }
        }
        var icon: String {
            switch self {
            case .red:
                return "exclamationmark.3"
            case .yellow:
                return "exclamationmark.2"
            case .green:
                return "exclamationmark"
            default:
                return "nosign"
            }
        }
    }
}

extension Item: TableRecord {
    static let tagRelation = belongsTo(Tag.self)
}
class TaskItem: Item {
    // Initialisation of TaskItem
    override init(
        id: String = UUID().uuidString,
        name: String,
        description: String,
        dueDate: Date? = nil,
        isCompleted: Bool = false,
        dateType: DateType = .due,
        color: PriorityColor = .none,
        tag: Tag? = nil
    ) {
        super.init(id: id, name: name, description: description, dueDate: dueDate, isCompleted: isCompleted, dateType: dateType, color: color, tag: tag)
    }
}

I am getting a 'required' initializer 'init(from:)' must be provided by subclass of 'Item' error in the TaskItem definition, and I'm not sure how best to fix it. It looks like I need to handle the encoding and decoding myself but it doesn't seem to work correctly. How do I do it correctly?

Thank you!

Hello @bradleythedeveloper,

The compiler error you see comes from Codable. Here is a way to reproduce it:

class Item: Codable {
    var id: Int
    
    init(id: Int) {
        self.id = id
    }
}

class TaskItem: Item {
    override init(id: Int) {
        super.init(id: id)
    }
}

// 'required' initializer 'init(from:)' must be provided by subclass of 'Item'
// note: 'required' initializer is declared in superclass here
//     internal init(from decoder: any Decoder) throws
//              ^

I'm not quite expert in the interaction between Codable and class hierarchies, but search engines provide some results such as All About Codable in Swift (Part 2) | by Ali Akhtar | Medium


On a more general note, I'm not sure defining a class hierarchy for dealing with a single database table is a good idea with GRDB (it looks like this is what you aim at).

I know some database libraries can deal with single table inheritance with the help of an extra discriminant column (e.g. ActiveRecord::Inheritance). But GRDB has no such feature: all convenience methods provided by the library decode the rows of this table in a single type:

// An array of Item (no TaskItem instance is created)
try Item.fetchAll(db)

// An array of TaskItem (no Item instance is created)
try TaskItem.fetchAll(db)

I can imagine that one would like to decode some specific rows as a specific type - this can make sense:

try TaskItem.filter(Column("kind") == "task").fetchAll(db)

Not knowing your specific goal, I can't really say more. I'll link to Record Timestamps and Transaction Date, though. This guide describes how multiple tables can share a common behavior through a protocol instead of a common super class. Maybe this can help.

Hi @gwendal.roue, thank you for your help! My original plan in this app (a task management app that can also save things like links, photos, etc. for later, sorry for not explaining it properly, new to this haha) was to store subclasses of Item such as TaskItem, and later PhotoItem, LinkItem, etc. inside of their own tables, not in the same table. I just wanted to use subclasses as a way to reduce repeating attributes between the different items, which would be produced if I had to make separate classes for each one. TaskItem, the example I gave, didn't have any extra attributes but ones like the others will (PhotoItem would have an Image variable for example). The project requires object-orientated programming to score higher marks, so I wanted to use this approach in order to integrate OOP, but it looks like from what you said, this might not be feasible. Thank you again!