Jcipa
Jcipa

Reputation: 1

SwiftUI Compiler unable to type-check in reasonable time

I have a playerView view that currently displays two things: the player's hand and the total value of their cards. It is defined like so:

struct playerView: View{
        @ObservedObject var player:Player
        var isDealt:Bool
        
        var body: some View{
            VStack{
                ZStack{
                    ForEach(player.viewCards.indices, id: \.self){ cardNumber in
                        //The first card has no offset so need conditional
                        if cardNumber == 0{
                            if player.viewCards[cardNumber]{
                                Image(player.hand[cardNumber].suit.rawValue + String(player.hand[cardNumber].rank) ).resizable().frame(width:120, height:160)
                            }
                        }   else{
                            if player.viewCards[cardNumber]{
                                Image(player.hand[cardNumber].suit.rawValue + String(player.hand[cardNumber].rank) ).resizable().frame(width:120, height:160).offset(x: 40, y: -40)
                            }
                        }
                    }
                }.padding(.top, 140)
                
                //Display player card Score
                if isDealt{
                    if player.hasAce{
                        Text("Card Score\n" + String(player.cardScore) + " / " + String(player.cardScore + 10)).font(.body).bold().foregroundColor(.white).lineLimit(2).multilineTextAlignment(.center)
                    }   else{
                        Text("Card Score\n" + String(player.cardScore)).font(.body).bold().foregroundColor(.white).lineLimit(2).multilineTextAlignment(.center)
                    }
                }
            }
        }
    }

And I keep getting the error "The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions" whenever I try to add almost anything to the code. For example, if I try to change the offset of the Image in the else part of the ForEach loop to be (x: 40 * cardNumber, y: -40 * cardNumber), I will get the error. I think that to fix this I will need to simplify my code further by perhaps creating another view to be used within this view. But I am not entirely sure how/why because this view doesn't seem very complex to me. Could someone give me an example of what I should do to simplify it further?

My understanding of what I should do is to take the blocks of code and turn them into views. So take the ZStack and turn it into a new view and call it within this view. But to do that I will need to pass the player into the new view, which is already passed into this view. Is this the right way to simplify views & get rid of this error?

Upvotes: 0

Views: 126

Answers (2)

rob mayoff
rob mayoff

Reputation: 385600

Here are some things we can do to help the compiler.

For the card score text, you're using string concatenation where interpolation would be more idiomatic. Try this instead:

Text("""
    Card Score
    \(player.cardScore) / \(player.cardScore + 10)
    """
)

We can also combine your two card score branches into one:

let aceAlt = player.hasAce ? " / \(player.cardScore + 10)" : ""
Text("""
        Card Score
        \(player.cardScore)\(aceAlt)
        """
)
    .font(.body)
    .bold()
    .foregroundColor(.white)
    .lineLimit(2)
    .multilineTextAlignment(.center)

It would reasonable to extract the image name computation into a separate property:

extension Card {
    fileprivate var imageName: String { "\(suit.rawValue)\(rank)" }
}

Then we can create the Image like this:

Image(player.hand[cardNumber].imageName)

Since the only difference between the cardNumber == 0 case and the else case is the offset, we can just use offsets of zero in the cardNumber case:

Image(player.hand[cardNumber].imageName)
    .resizable()
    .frame(width:120, height:160)
    .offset(x: cardNumber == 0 ? 0 : 40, y: cardNumber == 0 ? 0 : -40)

If you're still having trouble, extract the card image and the card score to helper functions:

extension Card {
    fileprivate var imageName: String { "\(suit.rawValue)\(rank)" }
}

struct PlayerView: View {
    @ObservedObject var player: Player
    var isDealt: Bool

    var body: some View {
        VStack {
            ZStack {
                ForEach(player.viewCards.indices, id: \.self) { cardNumber in
                    if player.viewCards[cardNumber] {
                        cardImage(for: player.hand[cardNumber], isFirst: cardNumber == 0)
                    }
                }
            }
            .padding(.top, 140)

            if isDealt {
                cardScoreView
            }
        }
    }

    private func cardImage(for card: Card, isFirst: Bool) -> some View {
        return Image(card.imageName)
            .resizable()
            .frame(width: 120, height: 160)
            .offset(
                x: isFirst ? 0 : 40,
                y: isFirst ? 0 : -40)

    }

    private var cardScoreView: some View {
        let aceAlt = player.hasAce ? " / \(player.cardScore + 10)" : ""
        return Text("""
            Card Score
            \(player.cardScore)\(aceAlt)
            """
        )
            .font(.body)
            .bold()
            .foregroundColor(.white)
            .lineLimit(2)
            .multilineTextAlignment(.center)
    }
}

Upvotes: 1

assuming "player.viewCards[cardNumber]" returns a Bool, you could try re-structuring your if, like this:

        if player.viewCards[cardNumber] {
            if cardNumber == 0 {
                Image(player.hand[cardNumber].suit + String(player.hand[cardNumber].rank) ).resizable().frame(width:120, height:160)
            } else {
                Image(player.hand[cardNumber].suit + String(player.hand[cardNumber].rank) ).resizable().frame(width:120, height:160).offset(x: 40, y: -40)
            }
        } else {
            EmptyView()
        }

Upvotes: 0

Related Questions