wrong value on the first buttonClick

When I run this code, and click on one of both 'currentZin' in the first screen that comes up with the view WordView, I see the content of the .preview value I used to initialize currentVerse (Verse= .preview) and not the values of the currentVerse that is in the Button action.

When I leave the WordView-sheet and click again the WordView shows the good result. I looks that on the first click the currentVerse = verse in the Button is not executed. If Ido not initialize it, it has a nil value. Can Anyone explain what happens and how to solve it.


struct HymnVerses: View {
    var hymn:Hymn
    @State private var currentZin: Int = 2
    @State private var isLatin: Bool = true
    @State private var isMasked: Bool = false
    @State private var isTranslation: Bool = true
    @State private var currentSentence: String = ""
    @State private var showWordView: Bool = false
    @State private var currentVerse: Verse = .preview
    
    // Deze calculated property wordt op voorhand berekend.
    // Hierdoor blijft de referentie naar het origineel bestaan
    // wanneer ik currentVerse bereken.  Daarvoor geraakte ik ze altijd kwijt.
    private var filteredVerses: [Verse] {
            hymn.verses.filter { $0.zin <= currentZin }
        }
    
    var body: some View {
        List {
            ForEach(filteredVerses) { verse in
                VStack(alignment: .leading) {
                    Button {
                        
                        currentVerse = verse
                        
                        showWordView = true
                    } label: {
                        Text("\(verse.zin). \(currentSentence(for: verse))")
                            .font(.headline)
                    }
                   
                }
            } .onAppear {
                currentVerse = filteredVerses.first!
            }

        }.sheet(isPresented: $showWordView, content: {
            WordView(vers: currentVerse, showWordView: $showWordView)
        })
       
        .toolbar {
            
            ToolbarItem(placement: .bottomBar) {
                Button(isLatin ? "Dutch" : "Latin") {
                    isLatin.toggle()
                }
            }
            
            ToolbarItem(placement: .bottomBar) {
                Button(isMasked ? "Unmask" : "Mask") {
                    isMasked.toggle()
                    
                }
            }
            
            ToolbarItem(placement: .bottomBar) {
                Button("Restart") {
                    currentZin = 1
                    
                }
            }
            
            ToolbarItem(placement: .bottomBar) {
                Button("Next") {
                    if currentZin < hymn.verses.count  {
                        currentZin += 1
                    }
                    
                }
            }
        }
        
    }
    
    func maskLetters(in sentence: String, with mask: Character = "*") -> String {
        return sentence.map { char in
            if char.isLetter {
                return String(mask)
            } else {
                return String(char)
            }
        }.joined()
    }
    
    private func currentSentence(for verse: Verse) -> String {
        var temp: String {
            return isLatin ? verse.latijn : verse.nederlands
        }
        if isMasked {
            return maskLetters(in: temp)
        }
        else {
            return temp
        }
        
        
    }
}

#Preview {
    /// the navigationStack is nodig omdat anders de toolbar niet zichtbaar is met #Preview
    NavigationStack {
        let allTexts = AllTexts()
        HymnVerses(hymn: .preview).environment(allTexts)
    }
}


@HermanVanCauwelaert What steps have you taken to debug your code?

Have you tried adding a print statement to print the values of verse when the button is clicked and adding a print statement an onChange(of:) Modifier to observe when changes to currentVerse are made ?

I did use print statements and de debugger before, but not the .onChange method.

When I add the .onChange to see what happens, the result is good, but when I put it in comment, then the result is wrong. I changed the program like this. I will add to screenshots one with .onChange in my code and one with the result when I put .onChange in comment.

This is my code now :

code-block

//
//  HymnVerses.swift
//  Tridenti
//
//  Created by Herman VAN CAUWELAERT on 08/01/2025.
//

import SwiftUI

struct HymnVerses: View {
    var hymn:Hymn
    @State private var currentZin: Int = 1
    @State private var isLatin: Bool = true
    @State private var isMasked: Bool = false
    @State private var isTranslation: Bool = true
    @State private var currentSentence: String = ""
    @State private var showWordView: Bool = false
    @State private var currentVerse: Verse = .preview
    
    // Deze calculated property wordt op voorhand berekend.
    // Hierdoor blijft de referentie naar het origineel bestaan
    // wanneer ik currentVerse bereken.  Daarvoor geraakte ik ze altijd kwijt.
    private var filteredVerses: [Verse] {
            hymn.verses.filter { $0.zin <= currentZin }
        }
    
    var body: some View {
        List {
            ForEach(filteredVerses) { verse in
                VStack(alignment: .leading) {
                    Button {
                        handleVerseSelection(verse)
                    } label: {
                        Text("\(verse.zin). \(currentSentence(for: verse))")
                            .font(.headline)
                    }
                   
                }
            } .onAppear {
                print ("Start .onAppear")
                currentVerse = filteredVerses.first!
                print ("End .onAppear")
            }

        }
        .onChange(of: currentVerse) { oldValue, newValue in
            print("currentVerse changed:")
            print("Van: \(oldValue.latijn)")
            print("Naar: \(newValue.latijn)")
        }
        .sheet(isPresented: $showWordView, content: {
            WordView(vers: currentVerse, showWordView: $showWordView)
        })
       
        .toolbar {
            
            ToolbarItem(placement: .bottomBar) {
                Button(isLatin ? "Dutch" : "Latin") {
                    isLatin.toggle()
                }
            }
            
            ToolbarItem(placement: .bottomBar) {
                Button(isMasked ? "Unmask" : "Mask") {
                    isMasked.toggle()
                    
                }
            }
            
            ToolbarItem(placement: .bottomBar) {
                Button("Restart") {
                    currentZin = 1
                    
                }
            }
            
            ToolbarItem(placement: .bottomBar) {
                Button("Next") {
                    if currentZin < hymn.verses.count  {
                        currentZin += 1
                    }
                    
                }
            }
        }
        
    }
    
    private func handleVerseSelection(_ verse: Verse) {
            currentVerse = verse
            print("\(currentVerse.latijn)")
            showWordView = true
        }

    
    func maskLetters(in sentence: String, with mask: Character = "*") -> String {
        return sentence.map { char in
            if char.isLetter {
                return String(mask)
            } else {
                return String(char)
            }
        }.joined()
    }
    
    private func currentSentence(for verse: Verse) -> String {
        var temp: String {
            return isLatin ? verse.latijn : verse.nederlands
        }
        if isMasked {
            return maskLetters(in: temp)
        }
        else {
            return temp
        }
        
        
    }
}

#Preview {
    /// the navigationStack is nodig omdat anders de toolbar niet zichtbaar is met #Preview
    NavigationStack {
        let allTexts = AllTexts()
        HymnVerses(hymn: .preview).environment(allTexts)
    }
}


hereby the screenshots

wrong value on the first buttonClick
 
 
Q