Why "Generic parameter 'Content' could not be inferred"? It's just one expression, very simple

struct VerySimple: View {
    var body: some View {
        GeometryReader { _ in   // Compile error: Generic parameter 'Content' could not be inferred
            let x: CGFloat = 0
            return Color.blue.frame(width: x)
        }
    }
}

So I can make the compiler happy by:

struct VerySimple: View {
    var body: some View {
        GeometryReader { (proxy: GeometryProxy) -> AnyView /* ModifiedContent<SwiftUI.Color, SwiftUI._FrameLayout> */ in   // Compile error: Generic parameter 'Content' could not be inferred
            let x: CGFloat = 0
            return AnyView(Color.blue.frame(width: x))
        }
    }
}

But I want to avoid AnyView, so I find out Color.blue.frame(width: x) type is ModifiedContent<SwiftUI.Color, SwiftUI._FrameLayout> , so I tried:

struct VerySimple: View {
    var body: some View {
        GeometryReader { (proxy: GeometryProxy) -> ModifiedContent<SwiftUI.Color, SwiftUI._FrameLayout> in   // Compile error: Generic parameter 'Content' could not be inferred
            let x: CGFloat = 10
                                              // must force cast as!, if not, compile error
            return Color.blue.frame(width: x) as! ModifiedContent<Color, _FrameLayout>  // Compile error: Cannot convert return expression of type 'some View' to return type 'ModifiedContent<Color, _FrameLayout>'
        }
    }
}

Why must as!? Since return Color.blue ..., Is it still using @​ViewBuilder? Is this why it cannot infer Content?

Firstly, the compiler won't use closure's content to infer its type if it's not a single-statement closure. It's the current limitation of the type inference. It can still know the type if that information come from the outside.

Something like this:

func foo(a: (Int) -> Int) {
}

// Multi-statement, but fine, we know the type from `foo` signature
foo {
  let a = 0
  return a + $0
}

The same can't me said about GeometryReader, it constrains the argument to be GeometryProxy but doesn't fully-specify the return type.


Secondly, frame returns an opaque some View, so the type system can't really say that it is ModifiedContent<Color, _FrameLayout>* even if it is. That's why you need force casting as there's no restriction whatsoever. If the type ends up being OtherHiddenView that is not castable to ModifiedContent<...>, the compiler won't complain, but the program will instead crash at runtime. So I'd advise against this.

* How do you know that it is ModifiedContent<Color, _FrameLayout>?.


If you want to do multi-line computations inside closure, and the compiler struggles to infer types, as much as you do (regarding the return type). I'd suggest that you put it in another function, and refer to it.

var body: some View {
  GeometryReader(content: self.internalView(proxy:))
}
    
private func internalView(proxy: GeometryProxy) -> some View {
  let x: CGFloat = 0
  return Color.blue.frame(width: x)
}

Or if you're not using proxy, use computed property instead, and make the closure single-statement:

var body: some View {
  GeometryReader { _ in self.internalView }
}
    
private var internalView: some View {
  let x: CGFloat = 0
  return Color.blue.frame(width: x)
}
1 Like

I didn't know the reason, but I did went with a function to get pass the problem:

    var body: some View {
        GeometryReader { proxy in
                self.horizontalBody(
                    //
                    // a whole lot of parameters computed from proxy
                    // would have been a bunch of let's
                   // now become parameters to this func
                   //
                )
            } 
}

Now I understand what happening here. Thanks!
:star_struck::pray:

Design-wise, I'd suggest that you try suppling only proxy into horizontalBody as well. There, you can easily have a bunch of let so it may improve readability. Plus you don't need to deal with capture list shenanigan (keep in mind you're already capturing self, though that shouldn't matter too much).

1 Like

Right you are! I forgot it just function, was still thinking closure...

:+1: