Search code examples
swiftparametersswiftuiclosuresreturn-type

How to create a struct parameter that is a closure with input, returning `some View` instead of `AnyView` in SwiftUI?


Current situation:

I'm trying to create a reusable view in SwiftUI that returns another view passed into it. At the moment I'm using a closure that returns AnyView, as the parameter. When I initialize the reusable view I pass on a custom view wrapped inside AnyView().

Goal:

Ideally I'd like a way to omit the AnyView() wrapper and just pass on my CustomViewA or CustomViewB as is.

My attempts and issues:

I tried changing the closure parameter to return some View but I'm getting the following error:

'some' types are only implemented for the declared type of properties and subscripts and the return type of functions

I've seen a few attempts on S.O. to work around that by somehow using a generic parameter but I'm not sure how to implement this in my case or if it's even possible. Also important to note, the closure needs to take a String as its own input parameter.

Example code:

import SwiftUI

struct ReusableView: View {
    let outputView: (String) -> AnyView
    
    var body: some View {
        outputView("Hello World")
    }
}

struct CustomViewA: View {
    let label: String
    
    init(_ label: String) {
        self.label = label
    }
    var body: some View {
        return Text(label)
    }
}

struct CustomViewB: View {
    let label: String
    
    init(_ label: String) {
        self.label = label
    }
    var body: some View {
        return HStack{
            Text(label)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ReusableView() {
            AnyView(CustomViewA($0))
        }
    }
}

Solution

  • You just need to make ReusableView generic and declare the return type of your closure as the generic type.

    struct ReusableView<Output: View>: View {
        let outputView: (String) -> Output
        
        var body: some View {
            outputView("Hello World")
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ReusableView() {
                CustomViewA($0)
            }
        }
    }