Home > database >  Type '()' cannot conform to View (except it definitely is a View, no shenanigans this time
Type '()' cannot conform to View (except it definitely is a View, no shenanigans this time

Time:08-06

I know it's a weird title but there are lots of posts with similar titles and completely different problems. Mostly people writing other stuff than View code inside their view, which I am not doing (as far as I can tell).

I'm trying to make Picker compatible with other BinaryInteger types since it doesn't work with anything but Int, and I'm having some trouble getting the Previews to work. Here's the code :

import SwiftUI

struct CompatibilityPicker<Label, SelectionValue, Content> : View where Label : StringProtocol, SelectionValue : BinaryInteger, Content : View {
    
    var content : () -> Content
    var label : Label
    
    @Binding private var _selection : SelectionValue
    
    private var selection: Binding<Int> { Binding<Int>(
        get: {
            Int(_selection)
        },
        set: {
            self._selection = SelectionValue($0)
        })
    }
    
    init(_ label : Label, selection : SelectionValue, content : @escaping () -> Content) {
        self.label = label
        self._selection = selection
        self.content = content
    }
    
    var body: some View {
        Picker(label, selection: selection, content: content)
    }
}

struct CompatibilityPicker_Previews: PreviewProvider {
    @State static var selection : UInt8 = 4
    
    static var previews: some View {
        CompatibilityPicker("Difficulty", selection: selection) { //error : Type'()' cannot conform to 'View'
            Text("Easy").tag(0)
            Text("Normal").tag(1)
            Text("Hard").tag(2)
        }
    }
}

What gives ? I have a normal Picker that uses the exact same syntax and that works, I don't know what I'm doing wrong.


Thanks to @RobMayoff's solution, I am one step further ahead, however seemingly nonsensical errors have shown up that don't clear with cmd shift k :

init(_ label : Label, selection : SelectionValue, @ViewBuilder content : @escaping () -> Content) {
    self.content = content
    self.label = label
    self._selection = selection //variable self._selection used before initialised
    // This stays on this line if I change the order,
} // Return from initializer without initialising all stored properties
// That is not true, as far as I can tell

CodePudding user response:

Shenaniganically, you are trying to use ViewBuilder syntax in the trailing closure, but you didn't adorn content with the @ViewBuilder annotation. So Swift infers that the trailing closure returns () (also called Void).

Change the init declaration to mention @ViewBuilder:

struct CompatibilityPicker<blah blah blah>: View where blah blah blah {

    init(
        _ label : Label,
        selection : SelectionValue,
        @ViewBuilder content : @escaping () -> Content
     // ^^^^^^^^^^^^
    ) {
        blah blah blah

UPDATE

    @Binding private var _selection : SelectionValue

    blah blah blah

    init(_ label : Label, selection : SelectionValue, content : @escaping () -> Content) {
        self.label = label
        self._selection = selection
        self.content = content
    }

The _selection variable is wrapped by the Binding wrapper, which means that it is really a computed property. The stored property is named __selection (note the two underscores) and has type Binding<SelectionValue>. Because _selection is a computed property, init cannot mention it until all stored properties are initialized. Probably you should change init to take a Binding<SelectionValue> argument instead of a SelectionValue argument:

    init(
        _ label : Label,
        selection : Binding<SelectionValue>,
        @ViewBuilder content : @escaping () -> Content
     // ^^^^^^^^^^^^
    ) {
        self.label = label
        self.content = content
        __selection = selection
    }

UPDATE 2

I looked at demo

struct CompatibilityPicker<Label, SelectionValue, Content>: View where Label : StringProtocol, SelectionValue : BinaryInteger, Content : View {

    var label : Label
    @Binding var selection : SelectionValue
    var content : () -> Content

    init(_ label : Label, selection : Binding<SelectionValue>, @ViewBuilder content : @escaping () -> Content) {
        self.label = label
        self._selection = selection
        self.content = content
    }

    private var value: Binding<Int> { Binding<Int>(
        get: {
            Int(selection)
        },
        set: {
            self.selection = SelectionValue($0)
        })
    }

    var body: some View {
        Picker(label, selection: value, content: content)
    }
}

struct CompatibilityPicker_Previews: PreviewProvider {

    struct TestView: View {
        @State var selection : UInt8 = 1
        var body: some View {
            CompatibilityPicker("Difficulty", selection: $selection) {
                Text("Easy").tag(0)
                Text("Normal").tag(1)
                Text("Hard").tag(2)
            }
        }
    }
    static var previews: some View {
        TestView()
    }
}
  • Related