NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

Project 5, Challenge #3

Forums > 100 Days of SwiftUI

On Project 5, WordScramble, and I'm trying to do Challenge #3. Here is the code for my textField TextField("Enter your word", text: $newWord, onCommit: addNewWord) .textFieldStyle(RoundedBorderTextFieldStyle()) .autocapitalization(.none) .padding() and my func: func increaseScore(){ if usedWords.count <= 5 { score += 1 } else { score += usedWords.count - 2 } I want to run addNewWord and increaseScore when onCommit is run.

1      

Call a function that itself calls addNewWord and increaseScore.

FYI, you can format your code using three backticks ``` on the line before and the line after your code block. That makes it easier to read and also to copy/paste into Xcode when trying to find a solution to a problem.

TextField("Enter your word", text: $newWord, onCommit: addNewWord)
    .textFieldStyle(RoundedBorderTextFieldStyle())
    .autocapitalization(.none)
    .padding()
func increaseScore(){ 
    if usedWords.count <= 5 { 
        score += 1
    } else {
        score += usedWords.count - 2
    }
}

See how much nicer and easier to read that is?

1      

Thanks, that does look better. Putting the 2 func in a single func worked. But now, my else statement isn't working. It increases score by one no matter what. Also, it is increasing score even if an error message comes up. How do I say increase score unless all of the error func's don't come up?

 func isOriginal(word: String) -> Bool {
        !usedWords.contains(word)
    }
    func isPossible(word:String) -> Bool {
        var tempWord = rootWord.lowercased()

        for letter in word {
            if let pos = tempWord.firstIndex(of: letter){
                tempWord.remove(at: pos)
            } else {
            return false
            }
        }
        return true
    }
    func isReal(word: String) -> Bool {
        let checker = UITextChecker()
        let range = NSRange(location: 0, length: word.utf16.count)
        let mispelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en")

        return mispelledRange.location == NSNotFound
    }
    func isSame(word: String) -> Bool{
        if word == rootWord {
        return false
        }
        return true
    }
    func isShort(word: String) -> Bool{
        let wordLength = word.count
        if wordLength < 3 {
        return false
        }
        return true
    }
    func wordError(title: String, message: String){
        errorTitle = title
        errorMessage = message
        showingError = true
    }
        func increaseScore(){
        if usedWords.count <= 5 {
            score += 1
        } else {
            score += usedWords.count - 2

1      

Hacking with Swift is sponsored by Play

SPONSORED Play is the first native iOS design tool created for designers and engineers. You can install Play for iOS and iPad today and sign up to check out the Beta of our macOS app with SwiftUI code export. We're also hiring engineers!

Click to learn more about Play!

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

Thanks, that does look better. Putting the 2 func in a single func worked. But now, my else statement isn't working. It increases score by one no matter what. Also, it is increasing score even if an error message comes up. How do I say increase score unless all of the error func's don't come up?

 func isOriginal(word: String) -> Bool {
        !usedWords.contains(word)
    }
    func isPossible(word:String) -> Bool {
        var tempWord = rootWord.lowercased()

        for letter in word {
            if let pos = tempWord.firstIndex(of: letter){
                tempWord.remove(at: pos)
            } else {
            return false
            }
        }
        return true
    }
    func isReal(word: String) -> Bool {
        let checker = UITextChecker()
        let range = NSRange(location: 0, length: word.utf16.count)
        let mispelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en")

        return mispelledRange.location == NSNotFound
    }
    func isSame(word: String) -> Bool{
        if word == rootWord {
        return false
        }
        return true
    }
    func isShort(word: String) -> Bool{
        let wordLength = word.count
        if wordLength < 3 {
        return false
        }
        return true
    }
    func wordError(title: String, message: String){
        errorTitle = title
        errorMessage = message
        showingError = true
    }
        func increaseScore(){
        if usedWords.count <= 5 {
            score += 1
        } else {
            score += usedWords.count - 2

1      

Your issue is not "where you declare" the function, just keep it at the same level as the other functions. The answer is "where do you use it".

Essentially you should call the function near the end of addNewWord() just after you update the usedWords array. So if we passed all the checks (isOriginal, isPossible etc...) then we can update the score.

Hope that helps.

1      

1: I put increaseScore at the end of all of the guard func, and the else statement is working, but it still increases even if there is an error.

2: How can I refresh the app when I hit the Restart button? right now it just gives a new word

import SwiftUI

struct ContentView: View {
    @State private var usedWords = [String]()
    @State private var rootWord = ""
    @State private var newWord = ""

    @State private var score = 0

    @State private var errorTitle = ""
    @State private var errorMessage = ""
    @State private var showingError = false

    var body: some View {
        NavigationView {
            Form{
            VStack {
                TextField("Enter your word", text: $newWord, onCommit: both)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .autocapitalization(.none)
                    .padding()

                List(usedWords, id: \.self) {
                    Image(systemName: "\($0.count).circle")
                    Text($0)
                }
                VStack{
                    Text("Score: \(score)")
                }
            }
            .navigationBarTitle(rootWord)
            .navigationBarItems(
                trailing: Button(action: {
                    startGame()
                }, label: { Text("Restart") })
            )
            .onAppear(perform: startGame)
                .alert(isPresented: $showingError){
                    Alert (title: Text(errorTitle), message: Text(errorMessage), dismissButton: .default(Text("Ok")))
                }
        }
        }
    }
    func addNewWord(){
        let answer = newWord.lowercased()
            .trimmingCharacters(in: .whitespacesAndNewlines)

        guard answer.count > 0 else{
            return
        }
        guard isOriginal(word: answer) else {
            wordError(title: "Word used already", message: "Be more original")
            return
        }
        guard isPossible(word: answer) else {
            wordError(title: "Word not recognized", message: "You can't just make them up you know!")
            return
        }
        guard isReal(word: answer) else {
            wordError(title: "Word not possible", message: "That isn't a real word")
            return
        }
        guard isShort(word: answer) else {
            wordError(title: "Word is too short", message:"Needs to be 3 or more letters")
            return
        }
        guard isSame(word: answer) else {
            wordError(title: "You can't use \(rootWord)", message: "")
            return
        }

        increaseScore()

        usedWords.insert(answer, at: 0)
        newWord = ""
    }

    func startGame(){
        if let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt"){
            if let startWords = try?
                String(contentsOf: startWordsURL){
                let allWords = startWords.components(separatedBy: "\n")
                rootWord = allWords.randomElement() ?? "silkworm"

                return
            }
        }

        fatalError("Could not load start.txt from bundle.")
    }

    func isOriginal(word: String) -> Bool {
        !usedWords.contains(word)
    }
    func isPossible(word:String) -> Bool {
        var tempWord = rootWord.lowercased()

        for letter in word {
            if let pos = tempWord.firstIndex(of: letter){
                tempWord.remove(at: pos)
            } else {
            return false
            }
        }
        return true
    }
    func isReal(word: String) -> Bool {
        let checker = UITextChecker()
        let range = NSRange(location: 0, length: word.utf16.count)
        let mispelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en")

        return mispelledRange.location == NSNotFound
    }
    func isSame(word: String) -> Bool{
        if word == rootWord {
        return false
        }
        return true
    }
    func isShort(word: String) -> Bool{

        let wordLength = word.count
        if wordLength < 3 {
        return false
        }
        return true
    }
    func wordError(title: String, message: String){
        errorTitle = title
        errorMessage = message
        showingError = true
    }
    func increaseScore(){
        if usedWords.count <= 5 {
            score += 1
        } else {
            score += usedWords.count - 2
        }
}
    func both(){
        increaseScore()
        addNewWord()
    }

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

1      

    func both(){
        increaseScore()
        addNewWord()
    }

Here you call increaseScore then addNewWord, so your score will always increase before addNewWord (and its many guard statements) is even called.

But also...

Inside the addNewWord function, you again call increaseScore after all the guard statements, meaning the score will be increased a second time if the guard statements pass.

1      

As per the previous comment, you don't need the both() function in the onCommit, but instead just the addNewWord function. Inside of it, you should also move the increaseScore() function after the usedWords.insert in order for your score to be updated properly. You calculate the score based on the array count so make sure it gets updated before you calculate.

As for the restart, it should give you a new word at the top and an empty list... all the button should technically do is reset the usedWords array, and startNewGame().

1      

How do I reset the usedWords array? I've tried usedWords[] and usedWords[]() in my Button (action:, but I keep getting an error

1      

Also, my else statement is still not working

if usedWords.count <= 5 {
            score += 1
        } else {
            score += usedWords.count - 2
        }

1      

Have your button call startGame and while that should reset usedWords, I like to make sure of it, so I also reset it inside the startGame function usedWords = [] just after the rootWord line.

1      

Ok. Thank you. That totally worked. How about the If ... else statement? It only increase by 1 no matter the word length.

1      

Word length is not being scored by you. You are simply adding the score based on the number of words. If the total words is less or equal to 5 you add 1, otherwise you add (Total number of words - 2)... Your else statement will only work starting with the 6th word you find.

2      

Ok. I changed usedWords to wordLength.

    func increaseScore(word: String){
        let wordLength = word.count
        if wordLength <= 5 {
            score += 1
        } else {
            score += wordLength - 2

but when I call increaseScore

usedWords.insert(answer, at: 0)
        newWord = ""

        increaseScore()

i get an error "Missing argument for parameter 'word' in call"

1      

Right, because your increaseScore function needs to know the length of the current word in order to increase the score properly: let wordLength = word.count

So you have to supply a word parameter when you call increaseScore. Presumably, that should be: increaseScore(word: answer)

2      

would that be in the func increaseScore()? or would that be when I call increaseScore()?

also, I don't have an "answer" variable. would that be usedWords?

1      

That would be when you call increaseStore. You already have it in the parameter to that function: func increaseScore(word: String)

I was basing what I wrote on the fact that you are currently (at least the last time you posted significant code) calling increaseScore from within the addNewWord function, where you do have an answer variable; you are using it in all those guard statements to check if the user got it right.

2      

You were right, I just totally looked over it . Adding (word: answer) totally worked, and now the app works exactly how I want. thank you so much for taking the time to help.

1      

Hacking with Swift is sponsored by Play

SPONSORED Play is the first native iOS design tool created for designers and engineers. You can install Play for iOS and iPad today and sign up to check out the Beta of our macOS app with SwiftUI code export. We're also hiring engineers!

Click to learn more about Play!

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.