Do I have to downcast or am I missing something?

Viewed 84

So I have a Type that I would like to make dynamic at the call site:

    struct DynamicView<Content: View>: View {
    let content: Content
    var body: some View {
              content
           }
    }

using some type of function that would convert it into something concrete

    static func contentCreator<T: View>(id: Int) -> T {
    switch id {
    case 0:
        return FirstView() //<-- Xcode is demanding that I state as! T here
    }
}

where FirstView

struct FirstView: View {
    var body: some View {
        Circle()
    }
}

Am I approaching this wrong or what? As long as they all conform to a common protocol, they should be interchangeable right?

2 Answers

That's because the type is not inferred by checking return.

If you try:

let v = contentCreator(id: 0)

You will get a Generic parameter T could not be inferred error, because v type is unknown.

If type is set, it will compile :

let v: View = contentCreator(id: 0)

So to get back to you interrogation, since View is a protocol, when Xcode builds the source, it has no way to know the class 'T', and no way to force you to use the right type when you use it.

struct FirstView: View {
    var body: some View {
        Circle()
    }
}

struct SecondView: View {
    var body: some View {
        Square()
    }
}

// This will work, because once executed, we know v1 class. 
// The cast will work as long as v1 is declared as conforming to 'View' protocol. 
// Type is not inferred in this case, but known only when it returns.

let v1: View = contentCreator(0)

/// This won't work, because the inferred type is 'SecondView'. 
/// The cast 'return FirstView() as! T' will crash

let v2: SecondView = contentCreator(0)

I assume you just looking for something like this

@ViewBuilder
func contentCreator(id: Int) -> some View {
    switch id {
        case 0:
            FirstView()
        case 1:
            SecondView()
        // ...
        default:
            EmptyView()
    }
}
Related