Search code examples
swiftgenericsinitializer

override initializer from generic class


open class CheckItem<T, U: Equatable & CustomStringConvertible>: DataTableItem<T,U,Bool> {
    public override init(_ data: T, getter: @escaping (T) -> U) {
        super.init(data, getter: getter)
    }
}

open class DataTableItem<T, U: Equatable & CustomStringConvertible, V: CustomStringConvertible>: TableItem{
    let data: T
    let getter: (T) -> U

    public init(_ data: T, getter: @escaping (T) -> U) {
        self.data = data
        self.getter = getter
    }
}

open class TableItem: NSObject {
    public var title: String?
}

It is weird that, can't override the init in subclass CheckItem.

Compiler complains that Initializer does not override a designated initializer from its superclass. It complains that Overriding declaration requires an 'override' keyword if I remove the override keyword.

It drives me crazy anyone helps? Thanks in advance.

The more weird part is that it works in LabelItem

open class LabelItem<T, U: Equatable & CustomStringConvertible, V: CustomStringConvertible>: DataTableItem<T,U,V>{

public override init(_ data: T, getter: @escaping (T) -> U) {
    super.init(data, getter: getter)
}

The full code is available here https://github.com/magerate/TableMaker

Edit

let checkItem = CheckItem<People, Bool>(people, getter: {(p: People) -> Bool in
        p.isGirl
    })

It compiles if don't try to create any instance of CheckItem. But complains

Cannot convert value of type 'People' to expected argument type 'Bool'

when try to create a new instance of CheckItem.

It seems that type inference is not correctly here.

Edit

It works when I deploy the code to swift framework. WTF


Solution

  • I am not sure what exactly goes wrong for you. When I run your code everything seems fine. Are you sure you're instantiating your instances correctly?

    let checkItem = CheckItem("Check item data") { (string: String) -> String in
        return string + "checkItem"
    }
    
    
    let labelItem = LabelItem<String, String, Int>("Label item") { (string: String) -> String in
        return string + "labelItem"
    }
    
    
    let dataTableItem = DataTableItem<String, String, Int>("Data table item") { (string: String) -> String in
        return string + "dataTableItem"
    }
    

    In LabelItem and DataTableItem you have a generic V which is not used anywhere and is not a parameter, so you need to be explicit with your types upon instantiation since you're not passing the V type in the init and the compiler can't infer the type. Thus <String, String, Int> or any other types that meet the constraints.

    EDIT:

    After looking into your project code (the project didn't run on my Xcode, I only copied relevant code into my project) I still see no problem - both initializers of CheckItem compile:

    open class TableItem: NSObject {
        public var title: String?
    }
    
    open class DataTableItem<T, U: Equatable & CustomStringConvertible, V: CustomStringConvertible>: TableItem{
        let data: T
        let getter: (T) -> U
    
        public weak var host: TableItemHost?
    
        public init(_ data: T, getter: @escaping (T) -> U) {
            self.data = data
            self.getter = getter
        }
    }
    
    public protocol TableItemHost: class {}
    
    open class CheckItem<T, U: Equatable & CustomStringConvertible>: DataTableItem<T,U,Bool> {
        public init(_ data: T, host: TableItemHost, getter: @escaping (T) -> U) {
            super.init(data, getter: getter)
            self.host = host
        }
    
        public override init(_ data: T, getter: @escaping (T) -> U) {
            super.init(data, getter: getter)
        }
    }
    

    Creating instances:

    let checkItem1 = CheckItem("Check item 1 ") { (string: String) -> String in
        return string
    }
    
    class Host: TableItemHost {}
    let host = Host()
    let checkItem2 = CheckItem("Check item 2 ", host: host) { (string: String) -> String in
        return string
    }
    
    print(checkItem1.data)
    print(checkItem2.data)
    

    Copy paste my code into a playground and see for yourself. Perhaps there is something other than the initializer causing the error.

    For a test you can also try commenting out both initializers of CheckItem and instantiate it with the inherited initializer. That should work, because CheckItem will not have its own designated initializer anymore (https://stackoverflow.com/a/31459131/1433612)