WWDC24 SALE: Save 50% on all my Swift books and bundles! >>

SOLVED: WordScramble 'new game' help please

Forums > 100 Days of SwiftUI

Ok so after several days of wracking my brain, I will admit to myself that I need help. My app seems to have some strange behaviour I can't quite figure out.

The code seems to work well until I call the 'restart' function using the button, to generate a new rootWord. The restart function clears the usedWords array, and clears most user scores (except 'high score'), then calls the 'startGame' function.

All looks well until you play the game. If you have entered say 3 or 4 words into the game (which populate the usedWords array), and then restart the game with a button press to generate the new rootWord, it seems that any new words submitted thereafter do not show in the list view...until you have entered a suffciient quantity of words which exceeds the previous array size. This is weird, as I have (I think) cleared the contents of the usedWords array, and new words are appended at 0. Further weirdness is that I can submit the same valid word repeatedly in the restarted games only, which seems to go through the 'guard' statements and correctly get added to the userScore, but they do NOT appear in my list view until, again, the word is submitted more times than the previous array's last size. Finally, I notice that repeatedly submittng the same word over and over (in a restarted game only) will increment the score correctly, but does not reject the repeated word which has already been submitted, until it starts to appear in my list on screen. It's like the words somehow aren't getting to the usedWords array, until I have submitted several words, but ONLY when playing a restarted game! I feel like I'm going mad.

I'm sure I am just being an idiot, but I cant figure out where this has gone wrong. Like I say, the game functions perfectly until I call restart to generate a new rootWord...so strange. Any help would be appreciated, but I'm also keen to understand how I can go through a debugging process to track my data and see what is 'inside' the array.

//  ContentView.swift
//  WordScramble
//  Created by David  on 19/4/2023.

import SwiftUI

struct ContentView: View {

    let backgroundGradient = LinearGradient(
        colors: [Color.red, Color.blue],
        startPoint: .top, endPoint: .bottom)

    @State private var usedWords = [String]()
    @State private var rootWord = ""
    @State private var newWord = ""

    @State private var errorTitle = ""
    @State private var errorMessage = ""
    @State private var showingError = false
    @State private var totalScore = 0
    @State private var wordCount = 0
    @State private var wordLengthScore = 0
    @State private var highScore = 0

    var body: some View {
        ZStack {
            NavigationView {
                List {
                    Section {
                        TextField("Enter your word", text: $newWord)
                    Section {
                        ForEach(usedWords, id: \.self) { word in
                            HStack {
                                Image(systemName: "\(word.count).circle.fill")
                .background( .pink)
                .toolbar {
                    Button("New Word", action: restart)
                .onAppear(perform: startGame)
                .alert(errorTitle, isPresented: $showingError) {
                    Button("OK", role: .cancel) { }
                } message: {

            VStack {
                        Text("Word score: \(wordCount)")
                        Text("Length Score: \(wordLengthScore)")
                Text("Total Score: \(totalScore)")
                Text("HIGH SCORE: \(highScore)")



    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")

        guard isPossible(word: answer) else {
            wordError(title: "Word not possible", message: "You can't spell that word from '\(rootWord)'!")

        guard isReal(word: answer) else {
            wordError(title: "Word not recognized", message: "You can't just make them up, you know!")

        guard isNotRoot(word: answer) else {
            wordError(title: "Nice try", message: "That's the original word!")

        guard longEnough(word: answer) else {
            wordError(title: "Too short", message: "A word needs to be 3 or more letters!")
        wordCount += 1
        wordLengthScore += answer.count
        totalScore += wordLengthScore + wordCount
        if totalScore > highScore { highScore = totalScore } else { return }

        withAnimation {
            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"

        fatalError("Fatal Error: Could not load start.txt from bundle")
    func isOriginal(word: String) -> Bool {
        return !usedWords.contains(newWord)

    func isPossible(word: String) -> Bool {
        var tempWord = rootWord
        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 misspelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en")

        return misspelledRange.location == NSNotFound

    func isNotRoot(word: String) -> Bool {
         return !(word == rootWord)

    func longEnough(word: String) -> Bool {
        return word.count >= 3

    func restart() {
        usedWords = []
        totalScore = 0
        wordCount = 0
        wordLengthScore = 0
        newWord = ""

    func wordError(title: String, message: String) {
        errorTitle = title
        errorMessage = message
        showingError = true

    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {


Hi, The problem is in the func addNewWord() you have an else return after the totalScore > highScore

if totalScore > highScore { highScore = totalScore } else { return }

it should be

if totalScore > highScore { highScore = totalScore }


Wow!!! Amazing thank you so much Hector!

I was so focused on the functioning of the array, thinking i must be deleting the contents incorrectly or otherwise not handling the array properly I didn't spot that (obviously). You have no idea how grateful I am, thank you for saving my sanity!

edit: now I've seen it I can't believe I missed it...


Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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.