Hi all,
I am having a problem when using a function to change data in an EnvironmentObject, this does not immediately update the view code, however the change does appear to have been made successfully as when the view is reloaded the change appears as expected.
After playing around I have noticed that when the ‘@Published’ modifier is on the var in the Model rather than on the new instance of the Class in the ViewModel the view updates immediately as expected. However, I am unable to put the ‘@Published’ modifier on the var’s in the Model as these need to conform the ‘Decodable’ protocol.
The view is a detail page at the bottom of a sequence of a navigation link lists. This view then also has other navigation links to be able to edit some of the details (change the variety and quantity of components etc). All of the functions are called from the ViewModel apart from the ‘Delete’ function which I have lazily left in the view for now, but the behaviour is the same (i.e needing to reload the view in order to see the effect of the function).
I hope that makes sense, code below. Please let me know if any more detail is required.
Thank you!
Model
import Foundation
class FoodandDrink: Identifiable, Decodable {
var id:UUID?
var catagory:String
var items:[Item]
}
class Item: Identifiable, Decodable {
var id:UUID?
var name: String
var totalCalories: Int
var isFavourite: Bool
var addedToday: Int
var components:[Component]
}
class Component: Identifiable, Decodable {
var id:UUID?
var name: String = “name”
var varietyName: String = “varirtyName”
var varietyCalories: Int = 0
var totalCalories: Int = 0
var quantity: Int = 0
var unit: String = “unit”
}
class Variety: Identifiable, Decodable {
var id:UUID?
var itemName: String
var varieties:[Varieties]
}
class Varieties: Identifiable, Decodable {
var id:UUID?
var name: String
var calories: Int
}
class DailyTotal: Identifiable, Decodable {
var id:UUID?
var todayTotal:Int = 0
var todayDate:Date?
var componentsAdded:[ComponentsAdded]
}
class ComponentsAdded: Identifiable, Decodable {
var id:UUID?
var componentCategory:String = “catagory”
var componentName:String = “component name”
var componentCalories:Int = 0
}
ViewModel
import Foundation
class DailyTotalModel: ObservableObject {
@Published var dailyTotal = [DailyTotal]() @Published var componentsAdded = [ComponentsAdded]() init() { self.dailyTotal = DataService.getLocalData3() }
}
class VarietyModel: ObservableObject {
@Published var variety = [Variety]() @Published var varieties = [Varieties]() init() { self.variety = DataService.getLocalData2() }
}
class FoodandDrinkModel: ObservableObject {@Published var foodandDrink = [FoodandDrink]() @Published var item = [Item]() @Published var component = [Component]() init() { self.foodandDrink = DataService.getLocalData() } static func calculatedCalories(component:Component, computedQuantity:Int) -> Int { var computedTotal:Int = 0
// var computedQuantity:Int = component.quantity ?? 0
var computedCalories:Int = component.varietyCaloriescomputedTotal = ((computedQuantity+1)*10) * computedCalories component.totalCalories = computedTotal return component.totalCalories } static func totalCal (item:Item) -> Int { var tryTotalCal: Int tryTotalCal = item.components.reduce(0) { cals, currentItem in cals + currentItem.totalCalories } item.totalCalories = tryTotalCal return item.totalCalories } static func getVarities (component:Component, varietyModel:VarietyModel) -> [Varieties] { var variety = varietyModel.variety var myVariety:[Varieties] = [] for variety in variety { if variety.itemName == component.name { myVariety = variety.varieties } } print (myVariety) return myVariety } static func newComponent (item:Item, newVariety:Variety) -> [Component] { // get item.component var componentList = item.components var newComponent:Component = Component() // get selected component newComponent.id = UUID() newComponent.name = newVariety.itemName newComponent.varietyName = newVariety.varieties[0].name newComponent.varietyCalories = newVariety.varieties[0].calories newComponent.totalCalories = 0 newComponent.quantity = 0 newComponent.unit = "g" // appened selected component to item.component componentList.append(newComponent)
//
item.components = componentList
// go to new instance of ComponentListViewprint (componentList) return componentList } static func deleteComponent(at offsets: IndexSet, item:Item) { item.components.remove(atOffsets: offsets) } func addToTotal (foodanddrink:FoodandDrink, item:Item, dailyTotal:DailyTotal) -> Int { //create addedComponent array var newComponentsAdded:ComponentsAdded = ComponentsAdded() //Assighn selcted component to newComponentAdded newComponentsAdded.id = UUID() newComponentsAdded.componentName = item.name newComponentsAdded.componentCategory = foodanddrink.catagory newComponentsAdded.componentCalories = item.totalCalories //append to today.addedcomponents var newComponentsAddedList = dailyTotal.componentsAdded newComponentsAddedList.append(newComponentsAdded) dailyTotal.componentsAdded = newComponentsAddedList print (dailyTotal.componentsAdded) //calculate daily total and assign date var calculatedDailyTotal = dailyTotal calculatedDailyTotal.todayTotal = dailyTotal.componentsAdded.reduce(0) {cals, currentItem in cals + currentItem.componentCalories} calculatedDailyTotal.todayDate = Date() dailyTotal.todayDate = calculatedDailyTotal.todayDate dailyTotal.todayTotal = calculatedDailyTotal.todayTotal return dailyTotal.todayTotal } static func minusFromTotal (item:Item, dailyTotal:DailyTotal) -> [ComponentsAdded] { var myItemsAdded = dailyTotal.componentsAdded if let i = myItemsAdded.firstIndex(where: { $0.componentName == item.name}) {myItemsAdded.remove(at: i)} dailyTotal.componentsAdded = myItemsAdded print (dailyTotal.componentsAdded) //calculate daily total and assign date var calculatedDailyTotal = dailyTotal calculatedDailyTotal.todayTotal = dailyTotal.componentsAdded.reduce(0) {cals, currentItem in cals + currentItem.componentCalories} calculatedDailyTotal.todayDate = Date() dailyTotal.todayDate = calculatedDailyTotal.todayDate dailyTotal.todayTotal = calculatedDailyTotal.todayTotal return dailyTotal.componentsAdded } static func countAdded (item:Item, dailyTotal:DailyTotal) -> Int { let myItemsAdded = dailyTotal.componentsAdded let myCount = myItemsAdded.filter { $0.componentName == item.name }.count print (myCount) return myCount } }
View
import SwiftUI
struct ComponentListView: View {
@EnvironmentObject var model:FoodandDrinkModel
@EnvironmentObject var model2:VarietyModel
@EnvironmentObject var model3:DailyTotalModel@State var item:Item @State var foodandDrink:FoodandDrink var body: some View { Spacer() VStack(alignment: .leading){ HStack { Spacer() Text("Calories Today") ForEach (model3.dailyTotal) { r in Text(String(r.todayTotal))} Spacer() } HStack { VStack(alignment: .leading) { Text("Name") Text(item.name) } Spacer() VStack(alignment: .leading) { Text("Calories") Text(String(FoodandDrinkModel.totalCal(item: item))) } Spacer() VStack(alignment: .leading) { Text("Added Today") HStack { ForEach (model3.dailyTotal) { r in Button(action: {model.addToTotal(foodanddrink: foodandDrink, item: item, dailyTotal: r)}) { Image(systemName: "plus.circle") } Text(String(FoodandDrinkModel.countAdded(item: item, dailyTotal: r))) Button(action: {FoodandDrinkModel.minusFromTotal(item: item, dailyTotal: r)}) { Image(systemName: "minus.circle") } } } } } } VStack(alignment: .leading){ HStack { Text("Components:") } .padding(.vertical) Form { ForEach (item.components) { r in HStack{ Text(r.name) Text(String(r.totalCalories)) Spacer() NavigationLink ( destination: VarietyListView(component:r), label: {Text("Edit")}) } } .onDelete(perform: delete) } VStack { HStack{ ForEach (model3.dailyTotal) { r in Button ("Add", action: {model.addToTotal(foodanddrink: foodandDrink, item: item, dailyTotal: r) } ) } } Spacer() HStack (alignment: .top){ Spacer() NavigationLink ( destination: NewComponentView(item: item), label: {Text("New Component")}) Spacer() } Spacer() } } } func delete(at offsets: IndexSet) { item.components.remove(atOffsets: offsets) }
}