Working code snippet:
import SwiftUI
import SwiftData
@main
struct MainApp: App {
var body: some Scene {
WindowGroup {
SomeView()
}
.modelContainer(appContainer)
}
}
struct SomeView: View {
@Query private var items: [AItem]
var body: some View {
ParentView(items: items)
}
}
struct ParentView: View {
private var groupedItems: [GroupedAItems] = []
init(items: [AItem]) {
Dictionary(grouping: items) { $0.categoryName }
.forEach {
let groupedItems = GroupedAItems(categoryName: $0.key, items: $0.value)
self.groupedItems.append(groupedItems)
}
}
var body: some View {
ScrollView {
VStack(spacing: 15) {
ForEach(groupedItems, id: \.self.categoryName) { groupedItems in
ChildView(groupedItems)
}
}
}
}
}
struct ChildView: View {
public var groupedItems: GroupedAItems
@State private var showItems: Bool
init(_ groupedItems: GroupedAItems) {
self.groupedItems = groupedItems
self._showItems = State(initialValue: !groupedItems.completed)
print("init, group \(groupedItems.categoryName) - items not completed \(!groupedItems.completed) - showItems \(showItems)")
}
var body: some View {
print("body, group \(groupedItems.categoryName) - items not completed \(!groupedItems.completed) - showItems \(showItems)")
if showItems {
return AnyView(ItemsSampleView(items: groupedItems.items, onClick: { showItems = false }))
} else {
return AnyView(GroupsView(groupedItems: groupedItems, onClick: { showItems = true }))
}
}
}
struct ItemsSampleView: View {
public var items: [AItem]
public var onClick: () -> Void
private let gridColumns = [GridItem(.adaptive(minimum: CGFloat(70)))]
var body: some View {
VStack {
Button {
onClick()
} label: {
Image(systemName: "chevron.down")
}
Spacer()
LazyVGrid(columns: gridColumns) {
ForEach(items.sorted(by: {$0.name < $1.name})) { item in
Button {
item.completed.toggle()
} label: {
Text(item.name)
}
}
}
}
}
}
struct GroupsView: View {
public var groupedItems: GroupedAItems
public var onClick: () -> Void
var body: some View {
VStack {
Button {
onClick()
} label: {
Image(systemName: "chevron.down")
}
Spacer()
Text(groupedItems.categoryName)
}
}
}
@Model
final class AItem: Identifiable {
@Attribute(.unique) public var id: String
public var name: String
public var categoryName: String
public var completed = false
internal init(name: String, categoryName: String) {
self.id = UUID().uuidString
self.name = name
self.categoryName = categoryName
}
}
struct GroupedAItems {
var categoryName: String
var items: [AItem]
var completed: Bool {
items.filter { !$0.completed }.isEmpty
}
}
@MainActor
let appContainer: ModelContainer = {
do {
let container = try ModelContainer(for: AItem.self)
// Make sure the persistent store is empty. If it's not, return the non-empty container.
var itemFetchDescriptor = FetchDescriptor<AItem>()
itemFetchDescriptor.fetchLimit = 1
guard try container.mainContext.fetch(itemFetchDescriptor).count == 0 else { return container }
container.mainContext.insert(AItem(name: "Apple", categoryName: "Fruits"))
return container
} catch {
fatalError("Failed to create container")
}
}()
Problem:
When clicking on Apple item is (un-)completed. When it's completed then ChildView should show GroupsView. However, that's not the case.
The logs are like this:
init, group Fruits - items not completed false - showItems false
body, group Fruits - items not completed false - showItems true
init, group Fruits - items not completed false - showItems false
init, group Fruits - items not completed false - showItems false
body log is not called again.