David
David

Reputation: 804

SwiftUI generics used from second initialiser

I am looking to structure a SwiftUI component in a similar way as some of Apple's implementations (E.g. Button). I am looking to have a label associated to this component (which is just another View) or have the ability to construct using just a string (and default to using a Text view).

This is what I have:

struct ExampleComponent<Label> : View where Label : View {
    let label: () -> Label
    let action: () -> ()
    
    init(_ title: String, action: @escaping () -> ()) {
        self.init(label: {
            Text(title)
        }, action: action)
    }
    
    init(@ViewBuilder label: @escaping () -> Label, action: @escaping () -> ()) {
        self.label = label
        self.action = action
    }
    
    var body: some View {
        label()
    }
}

However, this cannot compile due to the error:

Cannot convert value of type 'Text' to closure result type 'Label'.

What's going wrong?

Upvotes: 0

Views: 222

Answers (1)

Rob Napier
Rob Napier

Reputation: 299565

If you want this to be a default, then you need to make sure it only applies in cases where Label == Text. In other situations, you can't use it. I generally do this with a constrained extension:

extension ExampleComponent where Label == Text {
    init(_ title: String, action: @escaping () -> ()) {
        self.init(label: {
            Text(title)
        }, action: action)
    }
}

But you can also add the constraint on the init directly:

init(_ title: String, action: @escaping () -> ()) where Label == Text {
    self.init(label: {
        Text(title)
    }, action: action)
}

Upvotes: 1

Related Questions