Search code examples
swiftgenericsswiftuiswift-protocolsopaque-types

Cannot assign value of type 'some View' to type 'some View' - why not?


I am trying to create a structure that's holding the dynamic configuration of a popup view. That includes an array of actionButtons as well as a body property that will make up the later popup. The body can be seen as the "child views" of the popup, such as text for example. I want to be able to specify child views at will, using the @ViewBuilder in my initializer.

However, I am getting the following error:

Cannot assign value of type 'some View' to type 'some View' (type of 'PopupConfiguration.body')

public struct PopupConfiguration {
    let headline: String
    private(set) var body: some View = EmptyView()
    let actionButtons: [ActionButtonDefinition]

    init(headline: String, @ViewBuilder bodyContent: () -> some View, actionButtons: [ActionButtonDefinition]) {
        self.headline = headline
        self.body = bodyContent() // Cannot assign value of type 'some View' to type 'some View' (type of 'PopupConfiguration.body')
        self.actionButtons = actionButtons
    }
}

I am confused now, since the compiler should be able to assign some View to some View, right? It's literally the same type, or not?

Can anyone please explain why this generates above shown error?


Solution

  • You need to make use of generics in this case:

    public struct PopupConfiguration<Content: View> {
        let headline: String
        private(set) var body: Content
        let actionButtons: [ActionButtonDefinition]
    
        init(headline: String, @ViewBuilder bodyContent: () -> Content, actionButtons: [ActionButtonDefinition]) {
            self.headline = headline
            self.body = bodyContent()
            self.actionButtons = actionButtons
        }
    }
    

    I recommend you read the opaque types documentation, especially the section "Differences Between Opaque Types and Protocol Types" :)

    The main takeaway is that an opaque type refers to one specific type and retains said type informations. In your case, there is no guarantee that var body: some View and content: () -> some View will have the same type.