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.