ForEach doesn't update on items with the same ID

Note: the ForEach I talk about is the one in GameView, not KeyboardView

My app is a form of the game hangman, I have getLetters( ) get a random word from an array and split it into letters. I then have those letters displayed as lines using a ForEach until the user selects the corresponding letter on the keyboard view. This is done by adding the letter pressed into an array and the comparing to see if the letter in the ForEach is the same and if so, it is changed to the letter.

What I’m having trouble with is that sometimes when the correct letter button has been pressed, the ForEach does not update. I believe that has something to do with the ForEach using ‘id: .self’ as the issue only happens in words that have 2 or more of the same letter, though for some reason it almost always happens to the last letter of the word, even if not picked last.

If there is more information I can include either ask me and ill add it or check out the project files I’ve added.

class GameLetters: ObservableObject {
	@Published var usedLetters = [String]()
	@Published var gameLetters:Array = getLetters()
	@Published var correctLetters = [Character]()
	@Published var incorrectLetters = [Character]()
	static func getLetters() -> Array<Character> {
		let allWords = WordList.wordList
		let randomWord : String! = allWords.randomElement()
		let letters = Array(randomWord)
		print(type(of: letters))
		return letters

struct GameView: View {
	@ObservedObject var vm: GameLetters //@StateObject used in above view
	var body: some View {
		ForEach(vm.gameLetters, id: \.self) { letter in		
			if vm.correctLetters.contains(letter) {
					font(.system(size: 35, weight: .medium))
					} else {
						RoundedRectangle(cornerRadius: 10)
							.frame(width: 25, height: 4)
							.offset(y: 10)

struct KeyboardView: View {
	@ObservedObject var vm: GameLetters
        @State private var letters = KeyboardLetters()
	var body: some View {
				ForEach(letters.allLetters, id: \.self) { letter in
					Button {
						print("\(letter) button was pressed")
						if vm.gameLetters.contains(Character(letter)) {
							if checkLetters() == true {
								print("game won")
						} else {
					} label: {
    func checkLetters() -> Bool {
		if vm.gameLetters == vm.correctLetters {
			return true
		} else {
			return false

struct KeyboardLetters {
	let allLetters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
Terms of Service

Privacy Policy

Cookie Policy