Binding of an array element property inside a struct not working anymore

Hi,

I programmed an app to draw cards from a deck. After a card is drawn it is hidden from the deck. The drawn state is a property of a card struct in an array inside a deck struct. This worked well in the past since iOS 14. Since Xcode 16 this does not work as before anymore.

This is the Card struct with a Boolfor the drawn state:

struct Card: Codable, Identifiable, Equatable, Hashable {
    var isDrawn: Bool
    …
}

The cards are stored in an array that is inside a CardDeck struct among other properties:

struct CardDeck: Codable {
    var cards: [Card] = []
    var cardSpacing: CGFloat
    …
    mutating func hideCard(card: Card) {
        if let cardIndex = self.cards.firstIndex(of: card) {
            self.cards[cardIndex].isDrawn = true
        }
    }
}

The deck is a published property of an observable DeckStore class for permanent storage:

class CardDeckStore: ObservableObject {
    @Published var deck: CardDeck = CardDeck()
    …
}

The DeckStore class is @StateObject in the App struct:

@main
struct CardApp: App {
    @StateObject var store = OpaliaCardDeckStore()

    var body: some Scene {
        WindowGroup {
            ContentView(deck: $store.deck)
        }
    }
}

Here is the simplified DrawCardsView where I present and draw the cards:

struct DrawCardsView: View {
    @Binding var deck: CardDeck

    var body: some View {
        // Was a NavigationView before
        NavigationStack() {
            HStack(spacing: deck.cardSpacing) {
                ForEach($deck.cards) { $card in
                    if card.isDrawn {
                        CardBackView(card: card)
                            .hidden()
                    }
                    else {
                        NavigationLink(destination: DrawnCardView(card: card, deck: $deck)) {
                            CardBackView(card: card)
                        }
                    }
                }
            }
        }
    }
}

To hide a drawn card, hideCard() is called in the DrawnCardView:

struct DrawnCardView: View {
    var card: Card
    @Binding var deck: CardDeck
    @State var drawnCard: DrawnCard = .init()
    
    var body: some View {
        DrawnCardSimpleView(drawnCard: self.drawnCard)
            .onDisappear(perform: {
                deck.hideCard(card: self.card)
            })
    }
}

I am not a pro in programming and there are better solutions to program this, but this worked until I upgraded to Xcode 16. Now it seems the isDrawn state of a card does not update the DrawCardsView right away anymore. A drawn card is not hidden and still present when returning to DrawCardsView from DrawnCardView. After tapping the same card again or another update of the UI, the card will then be hidden.

I do not know the reason. It seems the binding of the isDrawn state inside an element of the card array in the observable object is not working anymore. Other properties of the observable object like cardSpacing do work as expected.

I can empty the cards array and fill it with new cards without problems in the DrawCardsView.

I eliminated the ForEach loop by addressing the array elements directly, but to no avail. I tried different solutions I found on the internet, but nothing worked.

I was under the impression that every change in an observable object would update the UI.

Any ideas for a explanation/solution?

Thanks,

Christian

You don't show enough code (eg, ContentView) to find out what is the problem.

Card is not ObservableObject. That may be the cause of the problem. Try to make it a class and ObservableObject.

2 more comments:

  • why don't you use environmentObject for deck, instead of all the bindings ?
  • in Xcode16, observable is much easier to use than ObservableObject. If needed, get details here: https://medium.com/@vladislavshkodich/remove-observableobject-from-your-swiftui-model-cb455e9572ef

Please share a link to your test project. That'll help us better understand what's going on. If you're not familiar with preparing a test project, take a look at Creating a test project.

Binding of an array element property inside a struct not working anymore
 
 
Q