Although SwiftUI did become more and more mature over the years: When it comes to error handling, SwiftUI still sometimes feels like a pain. You probably have seen the following error more than once: It reads “The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions” and looks like this:
I decided to write about this, as I get this error from time to time and it might make sense to collect anything that might have lead to this error here. See section “How can we fix it?” for any things you can try to fix it.
Why is it that painful?
Basically, this error just does not tell you anything. This sounds pretty dumb and it is. You can often just dig around what might have gone wrong. It just means what it says: The compiler gave up, try to fix it for yourself. I have no idea why this error still does appear for a couple of rather simple typo-like errors.
When it appears, it often does feel like it came out of nothing. Of course it always caused by something you did wrong. But the point of this live compiler in my opinion should be to mitigate these simple but difficult to spot issues. For some reason it does not work as nicely as it does in a pure Swift environment.
How can we fix it or what is causing it?
This list will surely never be complete as the reasons for the error are countless. However, this is an attempt to collect any reasons for it I came across.
Will be updated as soon as this beloved “The compiler is unable to type-check this expression in reasonable time” pops up for me again.
General approaches
- CMD+Z and see if it starts working again at some point. Then you should spot the issue.
- Comment out views inside of the
body
and see when it starts building again. - Split up your
View
even more - Have a look in the list of probably reasons below
- If nothing helps, just recreate your
View
using smaller child Views. This is probably the most time consuming approach but you can easily spend way more time finding the error than rebuilding yourView
properly.
My (incomplete) list of reasons causing the error
- Check if you mixed up bindings and “normal” properties in your arguments. The error above was caused by simply missing a
$
in front of my argument indicating that you want to pass a binding.
For the example above it was like this:SomeView(someArgument: argument1, someBinding: argument2, someOtherBinding: $argument3)
and it had to be like this:SomeView(someArgument: argument1, someBinding: $argument2, someOtherBinding: $argument3)
- Too many
onChange
modifiers chained together (might also be the case for chaining too modifiers of any type actually). As soon as you comment some of them the compiler might help you again. - You refactored your massive
View
that did not have any view model yet and forgot to update the existing call for your new initializer. - Accessing properties that you think were
static
but actually are not. For example I had this in anonChange
leading to the error:self.myProperty = Int(newValue / SomeClass.someNonStaticProperty)
when changing to the following it worked again:self.myProperty = Int(newValue / 16)
- Using
Button
initializers wrongly, e.g. having this:Button("Cancel", role: .cancel)
instead of thisButton("Cancel", role: .cancel) { }
(handler is not optional for some reason). Also using theaction
parameter inline for calling some method on button tap also lead to the error for me. Try using the trailing closure syntax with explicitly usingself
for your method call. This worked in my case.
How can we prevent this error?
There is no bullet proof strategy for eliminating the error but you can follow some practices to make it less likely to appear or at least easier to fix.
There are certain strategies to mitigate the error:
- Start creating small views as early as possible, even when quickly prototyping something. Trust me and do it. Don’t start with huge views for quickly prototyping stuff.
As soon as this error appears in such a huge view, you are screwed. Even more so if you did not constantly try building after each small step.
It’s way easier to spot any issues in them. As a side effect, this approach often has performance advantages and it’s easier to maintain. - Consider naming your arguments like:
xyzBinding
in order so see that you have to pass a binding. This sounds dumb, but as long as the compiler does not see this all the time, at least you can see it now. - It’s rather a security measure than a prevention: Do small git commits whenever possible, in case you want to do a major refactoring (e.g. change your
View
to use a view model) make sure that you can easily roll it back (e.g. from the stash or your remote repository).
Also see my other post regarding “micro commits”: https://mic.st/blog/how-to-remind-myself-of-where-i-stopped-coding-yesterday-micro-commits/
Conclusion
As much as I started loving SwiftUI, whenever “The compiler is unable to type-check this expression in reasonable time” is popping into my face I am really demotivated. It does not feel like bug fixing, it feels more like helping the compiler. I am aware that building compilers is not easy, still I think the compiler should be able to be more helpful here.
I hope this article somehow helped you fixing or preventing this issue,
Happy coding!