skip to Main Content

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?

2

Answers


  1. 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!

    Login or Signup to reply.
  2. 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)
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search