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

Please help with Milestone: Projects 4-6

Forums > 100 Days of SwiftUI

Hello there, and apologies for posting the same query on the wrong section of the forum

I have completed somehow the Milestone: Projects 4-6, which seems to work ok apart from a logic bug that I have obviously inadvertently created, which caused a double refresh of the question, once after providing the answer and once again after tapping the continue button on the allert window. I would be extremely helpful if you could help me understanding what I did wrong on this specific issue.

Many thanks in advance for your help.

Ammo.


import SwiftUI

struct ContentView: View {

    @State private var multiplier: Int = 1
    var numberOfQuestions = [5, 10, 12]
    @State private var chosenNumberOfQuestions = 0
    @State private var tableScale = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].shuffled()
    @State private var counter = 1
    @State private var score = 0
    @State private var scoreTitle = ""
    @State private var showingScore = false
    @State private var finalMessage = ""
    @State private var showingFinalMessage = false

    private var tableScaleRandom = [Int]()

    var body: some View {

        VStack {
            Text("❎").font(.largeTitle)
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Spacer()
            Text("Multiplication Challenge  ")
                .font(.largeTitle)

            Spacer()
            Text("Choose the multiplication table to test")

            Stepper("Multiplication table .. \(multiplier)", value: $multiplier, in: 1...12){_ in
                tableScale.shuffle()
            }

            Text("Number of questions").font(.title)
            Picker("Number of questions", selection: $chosenNumberOfQuestions) {
                ForEach(numberOfQuestions, id: \.self) {

                    Text("\($0)")
                       .font(.title)

                }
            }.pickerStyle(.segmented)

            Text("\(multiplier) X \(tableScale[0]) =").opacity(chosenNumberOfQuestions != 0 ? 1 : 0)
            let result = multiplication(a: multiplier, b: tableScale[0])
            let arrayOfNumbers = generateTwoRandomNumbers(correctAnswer: result)
            ForEach(arrayOfNumbers.shuffled(), id: \.self) { number in

                Button {
                    numberTapped(number: number, result: result)

                } label: {
                 Text("\(number)").opacity(chosenNumberOfQuestions != 0 ? 1 : 0)

                }

                Spacer()
            }

        }.alert(scoreTitle, isPresented: $showingScore) {
            Button("Continue", action: askAnotherQuestion)
        } message: {
            Text("Your score is: \(score)")
        }.alert(finalMessage, isPresented: $showingFinalMessage) {
            Button("Start again", action: resetGame)
        } message: {
            Text("Your score is: \(score)")
        }
        .padding()

    }

    func askAnotherQuestion() {
        tableScale.shuffle()
    }

    func multiplication(a: Int, b: Int) -> Int {
        let result = a * b
        return result
    }

    func generateTwoRandomNumbers(correctAnswer: Int) -> [Int] {

        let a = Int.random(in: 0...144)
        let b = Int.random(in: 0...144)

        let arrayOfRandomNumbers = [correctAnswer, a, b]

        return arrayOfRandomNumbers
    }

    func numberTapped(number: Int, result: Int){

        if counter < chosenNumberOfQuestions {

            if number == result {

                scoreTitle = "\(number) is correct! This is your try no. \(counter)"
                counter += 1
                score += 1

                print("Hello")
            } else {
                counter += 1
                score -= 1

                scoreTitle = "Wrong. The right result is \(result)"
            }
            showingScore = true
        } else if counter == chosenNumberOfQuestions {

            if number == result {

                scoreTitle = "\(number) is correct! This is your try no. \(counter)"
                counter += 1
                score += 1

                print("Hello")
            } else {
                counter += 1
                score -= 1

                scoreTitle = "Wrong. The right result is \(result)"
            }
            showingFinalMessage = true
            if score == chosenNumberOfQuestions {
                finalMessage = "Great!"
            } else if score > (score / 2) {
                finalMessage = "Well done!"
            } else {
                finalMessage = "Too bad!!!"
            }
        }
    }
    func resetGame() {
        counter = 1
        score = 0
        multiplier = 1
        chosenNumberOfQuestions = 0
        tableScale.shuffle()

    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

2      

Ammo asks for clarity:

[My code] causes a double refresh of the question. Once after providing the answer and again after tapping the continue button on the alert window. [Please] help me understanding what I did wrong...

First, welcome to Hacking With Swift! We can help answer your questions. But you have to help us! Please edit your message and place ALL YOUR CODE inside the three back tick marks.

```
// put ALL your code code inside
// some of your code is OUTSIDE and isn't formatted correctly.
```

Second, please don't cross post in different forums! You can visit your other post and DELETE IT. Thanks.

Third, you're on Project 4-6 ?? You did nothing wrong! You kicked your toe on a bump in the road. You are learning and asking the right questions.

Keep this in mind...

Think of a SwiftUI view struct like a small box. It contains a few variables, and some instructions on how SwiftUI should assemble the screen. This is a very small box. SwiftUI will DESTROY your box and recreate it hundreds, if not thousands of times when you run your application. Don't worry about it being created and DESTROYED and recreated.

SwiftUI is very efficient at creating and destroying your View structs.

Remember

SwiftUI will destroy your View struct and recreate it ANYTIME you change one of your @State variables. When you change an @State variable, you are telling SwiftUI that you have new data in your small box, and SwiftUI needs to destroy the old box and create a NEW box using the values in your @State variables.

So, when do you change your @State variables??

In your code, you allow your player to tap on one of three possible answers.

    Button {
        numberTapped(number: number, result: result)  // <-- What happens in here?
    } label: {
        Text("\(number)")
    }

When a player taps the button, you execute code in your numberTapped() function. Do you change any @State variables inside this function? (Hint: YES!)

    [snip....]
    // here!  you are changing THREE @State variables. 
    // These changes will tell SwiftUI to REDRAW your view.
    scoreTitle = "\(number) is correct! This is your try no. \(counter)"
    counter += 1
    score += 1

This is why your questions are redrawn when you tap the correct (or incorrect) answer.

Next

Next you allow the player to tap the Continue button. Do you change any @State variables when the player taps this button? (Hint: YES!)

    // Button smashing code...
    Button("Continue", action: askAnotherQuestion)

    // askAnotherQuestion code
    func askAnotherQuestion() {
        tableScale.shuffle()  // <-- Right here! Here you're changing an @State variable
    }

Again, you're telling SwiftUI to DESTROY the old view and recreate it using the updated variables. This is why SwiftUI updates the questions after your player taps continue.

Clear as mud?

Finally:

Keep Coding!

2      

Obelix, thanks a million for your full explaination. I think I need to digest it a bit, my mind does not seems to be particurarly flexible this days but I hope that with time and testing I will manage to get to fully implement your directions. Thanks again and have a great weekend.

2      

To address the logic bug causing a double refresh in Milestone: Projects 4-6, review your code for unintended loops or duplicate function calls. Check event handling and ensure proper execution flow. Debugging and testing are crucial for identifying and resolving the issue. For further assistance, provide specific details related to Budgeting Enterprise or the project code.

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

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.