Compiler can't figure out the type

I have this bit if SwiftUI code:

struct TimePosition: View {
    var scale: Double
    var body: some View {
        GeometryReader { geometry in
            VStack {
                ForEach(0..<Int(geometry.size.height)/Int(1/self.scale), id:\.self) { y in
                    Text(String(Double(y)/4.0))
                        .position(x: geometry.size.width/2, y: CGFloat(y))
                        .font(.system(size: 8.0))
                        .foregroundColor(.gray)
                 }
            }
        }
    }
}

If I try to compile it, it complains:
"The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions"

However, if I change the line Text(String(Double(y)/4.0)) to Text("\(Double(y)/4.0)"),
The compiler is happy.

Why is that?

This is from Apple Swift version 5.3 (swiftlang-1200.0.16.9 clang-1200.0.22.5)
Target: x86_64-apple-darwin19.5.0

Text’s unlabelled initializer takes a LocalizedStringKey, not a String, so the first variant is not valid Swift. Buried in that big complex statement, the compiler was simply unable to locate and diagnose the real problem.

The second variant works because LocalizedStringKey conforms to ExpressibleByStringInterpolation.

You could also use Text(verbatim:) to initialize with a String instead.

That's not quite right. If the parameter is a non-literal String, it's not interpreted as a localized key, just as a string:

You only need verbatim: to make a string literal be a String rather than a LocalizedStringKey.

This init ambiguity is just plain terrible, but there it is.

1 Like

So, you raise another question I have. These structs can arbitrarily large and all, but I don't understand why the compiler has a hard time with them. Lots of layers, yes, but it seems like there isn't a lot of magical type changing. Naively, it seems like it ought to be a matter of building the tree, and resolving or complaining about type matches on leaf nodes. I don't see where the structure would change anything about expected types inside of these declarations.

Any insights?

I don’t know. The other initializer @QuinceyMorris pointed at should have made it valid.

In general, because so many types are expressible as literals, combining lots of them into a single expression can cause the compiler to a have lot of options to rule out. Often breaking it up as the error suggests can lead to a clearer message, then when you know what the real problem is you can sometimes put it back:

let a = Text(String(Double(y)/4.0))
let b = a.position(x: geometry.size.width/2, y: CGFloat(y))
let c = b.font(.system(size: 8.0))
let d = c.foregroundColor(.gray)
return d
1 Like

Nice, not sure why that sort of break out never occurred to me.