Edit with minimal reproducible example:
@main
struct SwiftDataTestApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Team.self, Player.self
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query(sort: \Team.name) var teams: [Team]
@State private var viewModel = ViewModel()
var body: some View {
VStack {
List {
ForEach(teams) { team in
Section(header: Text(team.name)) {
ForEach(team.players) { player in
Text("\(player.name) #\(player.number)")
}
}
}
}
Button(action: {
let player1 = Player(name: "John", number: 1)
let player2 = Player(name: "Joe", number: 2)
let team = Team(name: "Team1", players: [player1, player2])
modelContext.insert(team)
try? modelContext.save()
}, label: {
Text("initialize")
})
Button(action: testChangeNumber) {
Label("Change player number", systemImage: "plus")
}
}
}
private func testChangeNumber() {
if let team = teams.first, let player = team.players.first {
viewModel.updatePlayer(playerID: player.id, teamID: team.id)
}
}
}
@MainActor
@Observable class ViewModel {
private let modelContext: ModelContext = SwiftDataService.shared.getContext()
func updatePlayer(playerID: UUID, teamID: UUID) {
let descriptor = FetchDescriptor<Team>(predicate: #Predicate { $0.id == teamID })
guard let team = try? modelContext.fetch(descriptor).first else {
return
}
if let playerIndex = team.players.firstIndex(where: { $0.id == playerID }) {
let player = team.players[playerIndex]
player.number = 7
try? modelContext.save()
}
}
}
class SwiftDataService {
private let modelContainer: ModelContainer
private let modelContext: ModelContext
@MainActor
static let shared = SwiftDataService()
@MainActor
private init() {
self.modelContainer = try! ModelContainer(
for: Team.self, Player.self,
configurations: ModelConfiguration(isStoredInMemoryOnly: false)
)
self.modelContext = modelContainer.mainContext
}
func getContext() -> ModelContext {
modelContext
}
}
@Model
class Player {
@Attribute(.unique) var id: UUID
var name: String
var number: Int
@Relationship(inverse: \Team.players) var team: Team?
init(id: UUID = UUID(), name: String, number: Int) {
self.id = id
self.name = name
self.number = number
}
}
@Model
class Team {
@Attribute(.unique) var id: UUID
var name: String
@Relationship var players: [Player]
init(id: UUID = UUID(), name: String, players: [Player] = []) {
self.id = id
self.name = name
self.players = players
for player in players {
player.team = self
}
}
}
First tap "Initialize". This will create a team with two players, John #1 and Joe #2. Then tap the "Change player number" button. The view will not update. However if you restart the app, the first player will now show with the correct #7.
The question is how to get the view to update when a change to an individual player information occurs.