1

In the following code, the Xcode Preview crashes when the array is more than 9 elements but does not crash with 9 or less elements.

CompileDylibError: Failed to build ContentView.swift Compiling failed: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

The code doesn't access the test function, nor the vars within and is a very simple test project:

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
        }
        .padding()
    }
    
    func test() {
        let stringArray1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] //preview crashes
        
        //let stringArray2 = ["a", "b", "c", "d", "e", "f", "g", "h", "i"] //preview does not crash
    }
}

#Preview {
    ContentView()
}

This is a fresh project with no other code, clean build folder.

macOS 15.6, macOS App

Xcode Version 16.4 (16F6)

To duplicate, copy and paste the code into a new project. If the stringArray1 line is commented out and stringArray2 line is uncommented, Preview works. As is, Preview crashes.

I attempted creating a class to hold the function with the same result

class Test {
    func test() {
        let stringArray1 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] //preview crashes
    }
}

struct ContentView: View {
    let test = Test()
    ...

Any idea why? It seems to be duplicatable across devices.

1
  • 2
    Previews are strange creatures. Adding a type declaration, [String], to the constant will fix the issue. Commented Aug 9 at 16:49

1 Answer 1

4

When compiling for Preview, Xcode actually wraps the string literals in your code with a __designTimeString function. If you click on the stethoscope button, you can see something like this

ContentView.swift:33:13: error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
    37 | //        ] //preview crashes
    38 |         
    39 |         let stringArray1 = [__designTimeString("#1663_3", fallback: "a"), __designTimeString("#1663_4", fallback: "b"), __designTimeString("#1663_5", fallback: "c"), __designTimeString("#1663_6", fallback: "d"), __designTimeString("#1663_7", fallback: "e"), __designTimeString("#1663_8", fallback: "f"), __designTimeString("#1663_9", fallback: "g"), __designTimeString("#1663_10", fallback: "h"), __designTimeString("#1663_11", fallback: "i"), __designTimeString("#1663_12", fallback: "j"), __designTimeString("#1663_13", fallback: "k")] //preview does not crash
       |             `- error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
    40 |     }
    41 | }

From what I understand from this article, these __designTimeXXX wrappers are basically what allows Xcode to update the preview so quickly when you change something in your code, without recompiling everything.

This obviously makes the whole expression more complicated, and type-checking becomes more consuming. Even so, a __designTimeString function on its own is not enough for the compiler to emit an error. You can try declaring your own __designTimeString function to see this:

// this still compiles for non-Previews
struct ContentView: View {
    // ...

    func modifiedTest() {
        
        let stringArray1 = [
            __designTimeString("a", fallback: "a"),
            __designTimeString("b", fallback: "b"),
            __designTimeString("c", fallback: "c"),
            __designTimeString("d", fallback: "d"),
            __designTimeString("e", fallback: "e"),
            __designTimeString("f", fallback: "f"),
            __designTimeString("g", fallback: "g"),
            __designTimeString("h", fallback: "h"),
            __designTimeString("i", fallback: "i"),
            __designTimeString("j", fallback: "j"),
            __designTimeString("k", fallback: "k"),
        ]
    }
}
    
public func __designTimeString<T>(_ key: Swift.String, fallback: T) -> T where T : Swift.ExpressibleByStringLiteral {
    fallback
}

In reality, there are three overloads of __designTimeString. Resolving these overloads makes type-checking even more time-consuming, and is what ultimately led to the compiler giving up. Here I have copied them from the .swiftinterface files.

import os

@available(iOS 14.0, macOS 11, tvOS 14.0, watchOS 7.0, *)
@_semantics("constant_evaluable") @_transparent public func __designTimeString(_ key: Swift.String, fallback: os.OSLogMessage) -> os.OSLogMessage {
    fallback
}
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public func __designTimeString<T>(_ key: Swift.String, fallback: T) -> T where T : Swift.ExpressibleByStringLiteral {
    fallback
}
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public func __designTimeString<T>(_ key: Swift.String, fallback: T) -> T where T : Swift.ExpressibleByExtendedGraphemeClusterLiteral {
    fallback
}

If you add these declarations into your code, the modifiedTest function fails to compile, even when you are not compiling for Previews.


A simple way to fix this is to specify the type of the variable explicitly. This makes the compiler do a lot less type inference, and therefore can type-check the expression within the time limit.

let stringArray1: [String] = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]

Another way is to declare your own __designTimeString function.

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public func __designTimeString<T>(_ key: Swift.String, fallback: T) -> T where T : Swift.ExpressibleByStringLiteral {
    // call the original function by qualifying it with "SwiftUI."
    SwiftUI.__designTimeString(key, fallback: fallback)
}

Since the generated __designTimeString calls are all unqualified, you can actually have them resolve to your own implementation.

This reduces the amount of work the compiler has to do (and therefore fixes the error), because the compiler prioritises declarations in the current module. It will not try to find the 3 declarations of __designTimeString in SwiftUI, when the __designTimeString you declared in the current module is already applicable.

Sign up to request clarification or add additional context in comments.

2 Comments

An excellent and clear answer. Thank you. In your opinion, is implicitly specifying the variable type best practice? let myString: [String] or let myInt: [Int| or let myClass: MyClass ?
@Jay yes, I recommend explicitly specifying the variable type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.