I have a 2 view setup.
In my ContentView I have a filter button that has 2 options, urgent and regular.
In DetailView I am loading the data based on the filter from SwiftData. I passed the filter to the init and fetched the objects from SwiftData. When I load data for the urgent, for example, I have 2 items and switch to the regular filter, where the total number of items changes to something greater than it currently is, my chart works fine. But my chart crashes if I switch from the regular filter back to the urgent when the number of items decreases. The error is:
Thread 1: Fatal error: Double value cannot be converted to Int because it is either infinite or NaN
This is simple code to demonstrate the error. My app is more complex. The reason I'm using the viewModel is that I need to change the data I'm getting from SwiftData to show it in the chart. I mimic the functionality in this code.
import Foundation
import SwiftData
@Model
final class Item {
var isUrgent: Bool
var name: String
var amount: Double
init(isUrgent: Bool, name: String, amount: Double) {
self.isUrgent = isUrgent
self.name = name
self.amount = amount
}
}
enum Filter: String, CaseIterable {
case urgent, regular
}
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@State var selectedFilter = Filter.regular
var body: some View {
NavigationView {
VStack {
Picker("", selection: $selectedFilter) {
ForEach(Filter.allCases, id: \.self) { filter in
Text(filter.rawValue)
.tag(filter)
}
}
.pickerStyle(.segmented)
DetailView(isUrgent: selectedFilter == .urgent)
}
.padding(.horizontal, 16)
.animation(.default, value: selectedFilter)
.toolbar {
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
}
}
private func addItem() {
withAnimation {
let newItem = Item(
isUrgent: Bool.random(),
name: "Some Item",
amount: Double.random(in: 1.0...50.0)
)
modelContext.insert(newItem)
}
}
}
import SwiftUI
import SwiftData
import Charts
struct DetailView: View {
@State var viewModel = DetailViewModel()
var isUrgent: Bool
@Query private var items: [Item]
init(isUrgent: Bool) {
self.isUrgent = isUrgent
self._items = Query(filter: #Predicate { $0.isUrgent == isUrgent } )
}
var body: some View {
VStack {
Chart(viewModel.chartItems, id: \.self) { item in
SectorMark(angle: .value("amount", item), innerRadius: .ratio(0.65), angularInset: 3.0)
}
}
.task(id: isUrgent) {
viewModel.setupData(items: items)
}
}
}
@Observable
final class DetailViewModel {
var chartItems: [Double] = []
func setupData(items: [Item]) {
chartItems = items.map { $0.amount }
}
}