Swift HStack View not conforming to protocol 'View'

I have built this code

struct StarDifficultyView: View {

var numberOfStarsToShow: Int
var numberOfTotalStarsToShow: Int = 5

var body: some View {
    HStack{
        var numberLeftToShow = numberOfStarsToShow
        ForEach(1..<numberOfTotalStarsToShow+1){_ in
            if(numberLeftToShow > 0){
                Image(systemName: "star.fill")
                    .foregroundColor(Color.yellow)
                numberLeftToShow -= 1
            }else{
                Image(systemName: "star.fille")
                    .foregroundColor(Color.yellow)
            }
        }
    }
}

}

It gives me an error on the line if(numberLeftToShow > 0){ saying "Type '()' cannot conform to 'View'"

Can anyone tell me what I'm doing wrong


Solution 1:

Explaining the issue:

You should not add expressions inside the view builder. So numberLeftToShow -= 1 will throw and error because it returns a void ('aka' type()) and this does not conform to View! that is the exact reason for the compiler!

Note 1

Don't use SwiftUI like the UIKit! SwiftUI views may execute over time on any state change and should not be used for calculating anything in this way

Note 2

You can convert 1..<numberOfTotalStarsToShow+1 to a closed range like 1...numberOfTotalStarsToShow (Although you don't need it at all for this question)

Note 3

Try not to use branch and convert your if/else code to something like:

Image(systemName: numberLeftToShow > 0 ? "star.fill" : "star.fille")
    .foregroundColor(Color.yellow)

Note 4:

The lower bound of a range can not be less than the upper range, but you can iterate over a reversed range like:

(1...numberOfTotalStarsToShow).reversed()

Note 5:

try using a single source of truth like the forEach parameter itself!

Note 6:

Swift can infer the type and you don't need to pass it again: so change Color.yellow to .yellow

Final Result:

Here is the code reviewed answer (based on the answer you have provided yourself):

    var body: some View {
        HStack {
            ForEach(1...numberOfTotalStarsToShow, id:\.self) { i in
                Image(systemName: "star.fill")
                    .foregroundColor(i > numberOfStarsToShow ? .gray : .yellow)
            }
        }
    }

Solution 2:

Don't throw away the closure parameter for the ForEach!

var body: some View {
    HStack{
        ForEach(0..<numberOfTotalStarsToShow){ i in // don't ignore the "i" here by writing "_"

            // "i" will be different in each "iteration"
            // use that to figure out which image to show
            if(i < numberOfStarsToShow){
                Image(systemName: "star.fill")
                    .foregroundColor(Color.yellow)
            } else {
                Image(systemName: "star")
                    .foregroundColor(Color.yellow)
            }
        }
    }
}