Home > front end >  How to bridge from any Type to Generic without switching through all possible Objects conforming to
How to bridge from any Type to Generic without switching through all possible Objects conforming to

Time:11-28

Context

I have a generic SwiftUI view called ComponentRow and would like to use it in different places inside my app. However, my model only returns the Component as (any Component)?, which is why I used a Switch to bridge between any and the generic ComponentRow view (see variant A in code example).

I came with an idea to simplify the code (see variant B in code example), however, I get the following Compiler Error:

Adjacent operators are in non-associative precedence group 'ComparisonPrecedence'


Code

protocol Component {
    static var name: String { get }
}

struct ContentView: View {
    var body: some View {
        // Variant A: Current Solution
        switch component {
        case let componentA as ComponentA: ComponentRow<ComponentA>()
        case let componentB as ComponentB: ComponentRow<ComponentB>()
        case let componentC as ComponentC: ComponentRow<ComponentC>()
        default: EmptyView()
        }

        // Variant B: My Idea, does not work
        if let safeComponent = component {
            EventRow<type(of: safeComponent)>(for: profile, with: event)
        }
    }

    var component: (any Component)? {
        // Some Logic...
    }
}

struct ComponentRow<C: Component>: View {
    var body: some View {
        Text(C.name)
    }
}

Question

  • Is there a way to avoid switching through all possible objects conforming to Component to initiate the appropriate ComponentRow?

CodePudding user response:

In order to use generics properly you need an instance of an object that conforms to the protocol Component.

Here's a quick example of what I think you're trying to accomplish:

protocol Component {
    var name: String { get set } // <~ Not static
}

struct FirstComponent: Component {
    var name: String
}

struct SecondComponent: Component {
    var name: String
}

struct ComponentRow<C: Component>: View {
    let component: C
    var body: some View {
        Text(component.name)
    }
}

struct ContentView: View {
    let first = FirstComponent(name: "Foo") // <~ instance
    let second = SecondComponent(name: "Bar") // <~ instance
    var body: some View {
        List {
            ComponentRow(component: first)
            ComponentRow(component: second)
        }
    }
}

Hope this helps!

CodePudding user response:

May be something like :

protocol Component { var name: String { get } }

struct ComponentA: Component {
    var name: String = "A"
}
struct ComponentB: Component {
    var name: String {
        get {
            return "B"
        }
    }
}

struct ContentView: View {
    var body: some View {
        // Variant B: My Idea, does not work
        if let safeComponent = components {
            ComponentRow(component: safeComponent)
        } else {
            EmptyView()
        }
    }
    
    var components: (any Component)? {
        // Some Logic...
        return (ComponentB())
    }
}

struct ComponentRow: View {
    // You do not need to declare a generic type
    var component: any Component
    var body: some View {
        Text(component.name)
    }
}
  • Related