1

I am inserting a new item into the modelContext (while on the single Deck) and the view is supposed to update automatically, but it does not. It refreshes only when I navigate back from a single Deck view to the list of Decks view. I am not sure what I am doing wrong here.

*It also does not work when I delete a card from the deck

It just seems like I am passing a value instead of a reference somewhere. But I cannot spot where.

My Views structure looks like this:

  • Decks View (list of decks)
    • Deck View (list of cards in a deck - I am adding a new card from this view)
      • Card View (single card)
private func addCard() {
  let newCard = Card(
    front: front,
    deck: cardModel.deck!
  )
            
  withAnimation {
    modelContext.insert(newCard)
  }
}

CardModel stores the deck (I make sure the Deck is not nil before I try to insert a new Card into context):

@Observable
final class CardModel {
    var deck: Deck? = nil

    public func showAddSheet(deck: Deck) -> Void {
        self.deck = deck
    }
}

Deck model:

@Model
final class Deck {
    @Relationship(.cascade)
    var cards: [Card]? = [Card]()
    var name: String = ""
    
    init(name: String) {
        self.cards = [Card]()
        self.name = name
    }
}

Card model:

@Model
final class Card {
    @Relationship(inverse: \Deck.cards)
    var deck: Deck? = nil
    var front: String = ""
    
    init(front: String, deck: Deck) {
        self.deck = deck
        self.front = front
    }
}

Router class:

enum Path: Hashable {
    case decks
    case deck(Deck)
}

@Observable
final class RouterModel {
    var path: [Path] = [Path]()
}

Decks view:

struct DecksView: View {
    @Query() var decks: [Deck]

    var body: some View {
        List {
            NavigationStack(path: $routerModel.path) {
                ForEach(decks, id: \.self) { deck in
                    NavigationLink(value: Path.deck(deck)) {
                        Text(deck.name)
                    }
                }
            }
        }
        .navigationDestination(for: Path.self, destination: { path in
                switch path {
                    case .decks:
                        DecksView()
                    case let .deck(deck):
                        DeckView(deck: deck)
                    case let .card(card):
                        CardView(card: card)
                }
            })
    }
}

Deck view:

struct DeckView: View {
    var deck: Deck
    
    var body: some View {
        List {
            ForEach(cards) { card in
                VStack {
                    Text(card.front)
                }
            }
        }
    }
}
1
  • Seems your Path enum is missing card(Card) Commented Jun 26, 2023 at 19:19

2 Answers 2

4

Views in SwiftUI are kind of "static" and are constructed, destroyed and reconstructed by the system multiple times in the view rendering cycle.

To be really really fast in this process, all SwiftUI views are a struct, and by definition, structs in Swift always have their values passed by copy. Keep that in mind when designing a SwiftUI app.

In your case the DeckView doesn't have any clue that it's part of a bigger architecture and that the deck property might change. Only the DecksView has this context. Once it navigates to a DeckView child the view doesn't receive updates and DecksView, along with its properties will probably be destroyed as soon as it leaves the foreground.

A good alternative would be DeckView to have its own @Query and you only pass an id to its constructor and it queries itself. This way, any changes trigerred on the data DeckView is using will trigger a new render with updated data.

Sign up to request clarification or add additional context in comments.

Comments

1

You're missing a Query for Cards but SwiftData doesn't currently support querying for relationships, i.e. the cards in a deck, so instead you need to use Core Data @FetchRequest with a predicate for the relationship instead. It's quite complicated to run Core Data and Swift Data side by side so you might be better just using Core Data because there will likely be other features you need that are missing.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.