UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Why is my app crashing? Project 2: Guess the Flag. 100 days of SwiftUI

Forums > 100 Days of SwiftUI

Hello,

I'm completely new to programming and have never done this before.

After doing project 2 of 100 days of SwiftUI, I thought I would try to add some of my own code before moving on to the next project. I managed to add a player name, but the app crashes after question 9 of the game when I play it again & I am not sure why.

I see a "Thread 1: fatal error: index out of range" error on line 53: Image(countries[number]).

Why does this error occur during a new game?

Here is my code:

static let allCountries = ["Estonia", "France", "Germany", "Ireland", "Italy", "Monaco", "Nigeria", "Poland", "Spain", "UK", "Ukraine", "US"]
@State private var countries = allCountries.shuffled()
@State private var correctAnswer = Int.random(in: 0...2)
@State private var nameGiven = true
@State private var playerName = ""

@State private var showingResults = false
@State private var showingScore = false
@State private var scoreTitle = ""
@State private var score = 0
@State private var QuestionCounter = 1
var body: some View {
    ZStack {
        RadialGradient(stops: [
            .init(color: Color(red: 0.1, green: 0.2, blue: 0.45), location: 0.3),
            .init(color: Color(red: 0.76, green: 0.15, blue: 0.26), location: 0.3)
        ], center: .top, startRadius: 200, endRadius: 700)

        .ignoresSafeArea()

        VStack {
            Spacer()

            Text("Guess the flag")
                .font(.largeTitle.weight(.bold))
                .foregroundStyle(.white)
            VStack (spacing: 15) {

                VStack {
                    Text("Tap the flag of")
                        .foregroundStyle(.secondary)
                        .font(.subheadline.weight(.heavy))

                    Text(countries[correctAnswer])
                        .font(.largeTitle.weight(.semibold))

                }
                ForEach(0..<3){ number in
                    Button {
                        flagTapped(number)
                    } label: {
                        Image(countries[number])
                            .clipShape(.capsule)
                            .shadow(radius: 5)
                    }
                }
            }
            .frame(maxWidth: .infinity)
            .padding(.vertical, 20)
            .background(.regularMaterial)
            .clipShape(.rect(cornerRadius: 20))

            Spacer()
            Spacer()

            Text ("\(playerName)'s Score: \(score) / 10")
                .foregroundStyle(.white)
                .font(.title.bold())
        }
    }
    .alert(scoreTitle, isPresented: $showingScore) {
        Button("Continue", action: askQuestion)
    } message: {
        Text("Your score is \(score) / 10")
    }
    .alert("Game over!", isPresented: $showingResults) {
        Button("Finish", action: restartGame)
    } message: {
        Text("Your final score was \(score) / 10")
    }

    .alert("Enter your name", isPresented: $nameGiven) {
        TextField("Name", text: $playerName)
        Text("Play")
    }
}

func flagTapped(_ number: Int) {
    if number == correctAnswer {
        scoreTitle = "Correct, \(playerName)!"
        score += 1
    } else {
        let needsThe = ["UK", "US"]
        let theirAnswer = countries[number]

        if needsThe.contains(theirAnswer) {
            scoreTitle = "Wrong, \(playerName)! that's the flag of the \(theirAnswer)"
        } else {
            scoreTitle = "Wrong, \(playerName)! That's the flag of \(theirAnswer)"
        }
    }
    if QuestionCounter == 10 {
        showingResults = true
    } else {
        showingScore = true
    }
}

func askQuestion() {
    countries.remove(at: correctAnswer)
    countries.shuffle()
    correctAnswer = Int.random(in: 0...2)
    QuestionCounter += 1
}
func restartGame() {
    QuestionCounter = 0
    score = 0
    countries = Self.allCountries
    askQuestion()
    askForPlayerName()
}
func askForPlayerName() {
    if score == 0 {
        nameGiven = true
        playerName = ""
    } else {
        nameGiven = false
    }
}
}

Thank you

3      

Because when you restartGame() the QuestionCounter is set to zero when it should be set to one(1). You ask more questions in the restart games and as you removing the correct answer from the array you cannot add three countries for the last question.

PS you should use camelCase for properties eg questionCounter

3      

Thank you @NigelGee!

Now when the game restarts, only 9 questions are asked and the final score message is always 9 / 10 for any new games played after the first one. I have tried many combinations to fix this, but I am lost and do not understand why only 9 questions are asked for new games after it restarts.

Thank you

3      

Change your restartGame() method to be this

    func restartGame() {
        QuestionCounter = 1
        score = 0
        countries = Self.allCountries
//        askQuestion() <- Remove this
        countries.shuffle() // <- add this
        correctAnswer = Int.random(in: 0...2)  // <- add this
        askForPlayerName()
    }

3      

Thank you so much for the help!

3      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.