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

SOLVED: DAY 31. Challenge.

Forums > 100 Days of SwiftUI

I wanted to extend the game to save the log of previos words alongside with a title of root word in header/first row and score for the game. It's about working with a List

I thought to record in wordsMemory all the words(root in first place and composed words following), update it each time when user presses button "new game", and then somehow to pass that data into a List, but I don't know how to coordinate between wordsMemory and usedWords. And how to pass it into list, as I have 2 arrays? The current words user adds, and the words from memory?

Also, is it possible to dynamically build rows in a List from different sources? How?

This code doesn't work ('The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions')

....
@State private var usedWords = [String]()
@State private var rootWord = ""
@State private var newWord = ""
@State private var playerScore = 0
@State private var wordsMemory = [[String]]()

....

        NavigationView {
            Vstack{
            ....
                List(usedWords, id: \.self) {
                    Image(systemName: "\($0.count).circle")
                    Text($0)

                    if !wordsMemory.isEmpty {
                        ForEach(0..<wordsMemory.count) {
                            Text(wordsMemory[$0][0])

                            ForEach(1..<wordsMemory[$0].count) { row in
                                Text(wordsMemory[row])
                            }
                        }
                    }
                }
            ...
            }
            .navigationBarTitle(rootWord)
            .navigationBarItems(leading: Button(action: startGame, label: {
                Text("new word")
        }

        func addNewWord() {

              // lowercase and trim the word, to make sure we don't add duplicate words with case differences
              let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)

              // exit if the remaining string is empty
              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 notTheSameAs(word: answer) else {
                  // Also, nice titles - title: "Your word is equal to mine", message: "use your imagination"
                  wordError(title: "Word can't be used", message: "Its the initial word")
                  return
              }

              playerScore += giveScore(to: answer)

              // update the hishest score if true
              if playerScore > highestScore {
                  highestScore = playerScore
              }

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

       }

      func startGame() {
          // saving root word, and all the words we came up with into memory
          var memorySection = [String]()
          memorySection.append(contentsOf: usedWords)
          memorySection.insert(rootWord, at: 0)
          wordsMemory.insert(memorySection, at: 0)

          // remove all used words as game restarts
          usedWords.removeAll()
          // update the score
          playerScore = 0

          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 recordInMemory(rootWord: String) {
        rootWordsMemory.insert(rootWord, at: 0)
        }

3      

First off, I admire the ambition. Good on you mate. Now, over the coming days / chapters you will learn about passing data between views, and how to model the data.

For now though, here's some info, which you can choose to research deeper if you want:

1- You want to start with a decision of where to display this "stored" data. Most probably a new view? If yes, do you want it to be a NavigationView? or a TabView? Either way, that new view will need to be passed your saved data. A list needs one source of truth. That source can have multiple ways it gets the info, but the list needs to know something to work with.

What dynamic means is that it only displays as many items as you give it, and will grow or shrink according to the data it gets. So to build it from different sources, as far as I know, is not possible or even wanted. It creates problems. What you can do is make the different sources dump their info into one source for the list to use. (but why do you want that anyway?)

2- When you say store, it usually means persisting the data. So the when the user terminates the app and relaunches the information is there. However, this is too early on your journey. I would recommend you stick the course until you learn how.

For now though, what you want to do is figure out what that data should look like. Think almost as if it were a JSON file. So for example:

struct Game: Identifiable {
    let id = UUID()
    let rootWord: String
    let usedWords: [String]
}

You then have an array of [Game] which will store each game. How? Well, when the user taps new game, the first thing that should happen is a call to the function that saves. That function, will basically create an instance of Game, with rootWord and usedWords, then insert it into the array.

func recordInMemory() {
    let game = Game(rootWord: rootWord, usedWords: usedWords)
    gamesArray.insert(game, at: 0)
}

The reason I added the conformance to Identifiable is to make sure it can be used with List

4      

@MarcusKay thanks a lot! very helphuf!

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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.