0

When using @EnvironmentObject, can you have an @EnvironmentObject inside of another class?

I have a setup (MeetingStats) class and then a processing class(Meeting). The processing class and the setup classes are both displayed via different tabs in my app. So if you can change the settings in setup, you should see in real time the changes to the processing class (that is mostly timer driven).

I have tried to switch from using the AppDelegate to passing things between views, to using @EnvironmentObject. I have changed my ScceneDelegate to create the initial objects, and am passing them .envrionmentObjects to the ContentView. However, when I run the app, the first time I try to access the setup class in the processing class, I crash with the message No ObservableObject of type MeetingStats found. A View.environmentObject(_:) for MeetingStats may be missing as an ancestor of this view.

SceneDelegate

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var meetingStats = MeetingStats()
    var meeting = Meeting()
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
    {
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            // passing EnvironmentObjects 
            window.rootViewController = UIHostingController(rootView:
                                                                ContentView()
                                                                .environmentObject(meeting)
                                                                .environmentObject(meetingStats)
                                                             )
            self.window = window
            window.makeKeyAndVisible()
        }
    }
}

Content View is a set of tabs, one for meeting, one for setup and one for statistics

meeting.swift - grossly simplified to show the problem area

//
//  meeting.swift
import Foundation
import SwiftUI
import Combine
import Intents
import os
class Meeting: ObservableObject {
    @EnvironmentObject var meetingStats: MeetingStats
    @objc func calcQuorumEvents() {
   // LOTS of calcualtions     
        self.meetingStats.totalWasteAmount = totalWasteAmount. // HERE's the Crash
        self.meetingStats.totalWasteAmountPersonal = totalWasteAmountPersonal
        self.meetingStats.totalLifetimeWasteAmount = totalLifetimeWasteAmount
    }
  }

Is the issue I am not passing explicitly passing the values to the tabs from the contentView? I have tried explicitly passing the Environment object to the meetingView but still receive the same error:

iOSMeetingView()
                .environmentObject(meeting)
                .environmentObject(meetingStats)
                .tabItem {
                    Image("presentation")
                    Text("Meeting")
            }.tag(1)

I didn't think that was required as Swift was to look in the object hierarchy to find the object.

Michael Rowe
  • 870
  • 2
  • 11
  • 27

1 Answers1

0

Did you add the environment objects in view iOSMeetingView?

@EnvironmentObject var meetingStats: MeetingStats

Edited: I am not sure about your requirement. Here is my thought. Pass EnvironmentObject meetingStats only to view and you will get the value changes of meetingStats while updating in Meeting.

class Meeting {
   func calcQuorumEvents(meetingStats: MeetingStats) {
      meetingStats.totalWasteAmount = 12 // HERE's the Crash
      meetingStats.totalWasteAmountPersonal = 60
      meetingStats.totalLifetimeWasteAmount = 8
  }
}

class MeetingStats: ObservableObject  {
    @Published var totalWasteAmount: Int
    @Published var totalWasteAmountPersonal: Int
    @Published var totalLifetimeWasteAmount: Int
    
    init() {
        totalWasteAmount = 0
        totalWasteAmountPersonal = 0
        totalLifetimeWasteAmount = 0
    }
}
achu
  • 329
  • 3
  • 7
  • Yes, I passed it and defined it in the iOSMeetingView, still crashes in the meeting object. – Michael Rowe Aug 12 '20 at 01:44
  • AFAIK EnvironmentObject can be passed only to SwiftUI View. Here you have passed it to the ObservableObject class. in Meeting class EnvironmentObject var meetingStats: MeetingStats is not passing from any where and that leads to the crash. – achu Aug 12 '20 at 04:20
  • Thanks @achu, so what is the best approach to passing to the Meeting Class? I am really struggling wrapping my head around this change – Michael Rowe Aug 12 '20 at 16:49
  • OK, I think I have resolved this.. Thanks for pointing out that this is only for UI. I will revert back to the delegate method to pass between Classes, and then utilize EnvironmentObject in the UI. Maybe this will be enhanced in the future for Class objects too. – Michael Rowe Aug 13 '20 at 12:45