swiftrandomswiftuiunwrap

Unwrapped optional keeps refreshing / Working with optionals, arrays and randomElement()


I was wondering how you would approach making the following application work:

Trying to make it work I stumbled upon the following problems:

Here's my code:

struct Calculate: View {
    
    @State var answerVar = ""
    @State var myNum = Int.random(in: 0...12)
    let numArray = [2,3,4,5] // this array will be dynamically filled in real implementation
    
    var body: some View {
        VStack {
            List {
                Text("Calculate")
                    .font(.largeTitle)
                
                HStack(alignment: .center) {
                    if let myNum2 = numArray.randomElement() {
                        Text("\(myNum) * \(myNum2) = ") // how can i make it so it doesn't reload every time i type an answer?
                    }
                    
                    TextField("Answer", text: $answerVar)
                        .multilineTextAlignment(.trailing)
                }
                
                HStack {
                    if Int(answerVar) == myNum * myNum2 { //how can i reuse the randomly picked element from array without unwrapping again?
                        Text("Correct")
                    } else {
                        Text("Wrong")
                    }
                    Spacer()
                    Button(action: {
                        answerVar = ""
                        myNum = Int.random(in: 0...12)
                    }, label: {
                        Text("Submit")
                            .foregroundColor(.white)
                            .shadow(radius: 1)
                            .frame(width: 70, height: 40)
                            .background(.blue)
                            .cornerRadius(15)
                            .bold()
                    })
                }
            }
        }
    }
}

Solution

  • Move your logic to the button action and to a function to setupNewProblem(). Have the logic code change @State vars to represent the state of your problem. Use .onAppear() to set up the first problem. Have the button change between Submit and Next to control the submittal of an answer and to start a new problem.

    struct Calculate: View {
        @State var myNum = 0
        @State var myNum2 = 0
        @State var answerStr = ""
        @State var answer = 0
        @State var displayingProblem = false
        @State var result = ""
        let numArray = [2,3,4,5] // this array will be dynamically filled in real implementation
        
        var body: some View {
            VStack {
                List {
                    Text("Calculate")
                        .font(.largeTitle)
                    
                    HStack(alignment: .center) {
                        Text("\(myNum) * \(myNum2) = ")
                        
                        TextField("Answer", text: $answerStr)
                            .multilineTextAlignment(.trailing)
                    }
                    
                    HStack {
                        Text(result)
                        
                        Spacer()
                        
                        Button(action: {
                            if displayingProblem {
                                if let answer = Int(answerStr) {
                                    if answer == myNum * myNum2 {
                                        result = "Correct"
                                    } else {
                                        result = "Wrong"
                                    }
                                    displayingProblem.toggle()
                                }
                                else {
                                    result = "Please input an integer"
                                }
                            }
                            else {
                                setupNewProblem()
                            }
                                
                        }, label: {
                            Text(displayingProblem ? "Submit" : "Next")
                                .foregroundColor(.white)
                                .shadow(radius: 1)
                                .frame(width: 70, height: 40)
                                .background(displayingProblem ? .green : .blue)
                                .cornerRadius(15)
                                .bold()
                        })
                    }
                }
            }
            .onAppear {
                setupNewProblem()
            }
        }
        
        func setupNewProblem() {
            myNum = Int.random(in: 0...12)
            myNum2 = numArray.randomElement()!
            result = ""
            answerStr = ""
            displayingProblem = true
        }
    }