Search code examples
swiftmacosgenericsswiftuitablecolumn

How to add a TableColumn initialiser that takes a format parameter, allowing me to format the column's contents?


public init<F>(_ titleKey: LocalizedStringKey, value: KeyPath<RowValue, String>, format: F) where F : FormatStyle, F.FormatInput : Equatable, F.FormatOutput == String {

    self.init(titleKey) { item in
        Text(item[keyPath: value], format: format)
    }
}

So I'd like to do something like the above, but the compiler is complaining and not being very helpful at all. I'm not great with generics and I'm hoping someone could explain to me the problem here so that I can throw that info in the old toolbox upstairs. Looking at the documentation Apple TableColumn Docs it says available when return type Label is Text, but the compiler says "Referencing initializer init(_:content:) on TableColumn requires the types 'Label' and 'Text' be equivalent"?!?

Additionally is says "Referencing initializer init(_:content:) on TableColumn requires the types Sort and Never be equivalent"

I'm not sure what it's trying to tell me here. I've tried a few different ways, but I can't seem to compile.

Even the following fails.

public init(_ titleKey: LocalizedStringKey, keyPath: KeyPath<RowValue,String>) {
    self.init(titleKey, value: keyPath)
}

But it just says no exact calls to init.

Also

public init(_ titleKey: LocalizedStringKey, keyPath: KeyPath<RowValue,String>) {
    self.init(titleKey, value: keyPath) { item in
        Text("")
    }
}
    Compiler says: "Cannot infer type of closure parameter 'item' without a type annotation"
               "Initializer `'init(_:value:format:)'` requires that `'((_) -> Text).FormatInput'` conform to 'Equatable'"
               "Initializer `'init(_:value:format:)'` requires the types `'((_) -> Text).FormatOutput'` and 'String' be equivalent"
               "Type `'(_) -> Text'` cannot conform to `'FormatStyle'`"

Solution

  • it says available when return type Label is Text, but the compiler says "Referencing initializer init(_:content:) on TableColumn requires the types 'Label' and 'Text' be equivalent"?!?

    This is because your initialiser is declared to be always available regardless of the types of Label and Sort, so it obviously cannot call an initialiser that requires these type parameters to be specific types.

    In fact, TableColumn has 4 type parameters, and your initialiser requires 3 of them (everything except RowValue) to be constrained, but you have not written any of those constraints in the declaration.

    Your initialiser requires Label == Text and Sort == Never as the error messages say. It also requires Content == Text, since you are returning a Text(item[keyPath: value], format: format) in the view builder.

    Adding all those requirements, you get:

    extension TableColumn {
        public init<F>(
            _ titleKey: LocalizedStringKey,
            value: KeyPath<RowValue, F.FormatInput>, format: F
        ) where F : FormatStyle, F.FormatOutput == String, F.FormatInput: Equatable,
                Label == Text, Sort == Never, Content == Text {
    
            self.init(titleKey) { item in
                Text(item[keyPath: value], format: format)
            }
        }
    }
    

    (Note that value should be a key path to F.FormatInput, not String)