UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Environment Object - Crash / Question

Forums > Swift

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 SceneDelegate 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.

I am not sure if I have the right understanding of how to use this.

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()
        }
    }
}

ContentView

struct ContentView: View {
    @EnvironmentObject var meeting: Meeting
    @EnvironmentObject var meetingStats : MeetingStats    
    @State private var selection = 1
    var body: some View {
        TabView(selection: $selection) {
            iOSMeetingView()
                .tabItem {
                    Image("presentation")
                    Text("Meeting")
            }.tag(1)
            iOSSetupView()
                .tabItem{
                    Image("gear")
                    Text("Setup")
            }.tag(2)
            iOSTotalsView()
                .tabItem{
                    Image("dashboard")
                    Text("Totals")
            }.tag(3)
            iOSHelpView()
                .tabItem{
                    Image("info")
                    Text("Help")
            }.tag(4)
        }
        .font(.headline)
        .onReceive(NotificationCenter.default.publisher(for: Notification.Name("switchTabs"))) { notification in
                    if let tabTag = notification.object as? Int {
                        self.selection = tabTag
                    }
                }
    }
}

Meeting Class (this is the crash)

//
//  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 explicitly passing the values to the tabs from the contentView?

I have tried changing ContentView to look like this
            iOSMeetingView()
                .environmentObject(meeting)
                .environmentObject(meetingStats)
                .tabItem {
                    Image("presentation")
                    Text("Meeting")
            }.tag(1)

Explicitly passing the environment objects but that doesn't seem to make a difference, and I didn't think that was required as Swift was to look in the object hierarchy to find the object.

2      

This is the one thing that really bugs me about SwiftUI, alright it is one of the many many things. I was hoping this would be "fixed" in SwiftUI 2.0.

You cannot use EnvironmentObjects in ObservedObjects, however as they are just other ObservedObjects themselves, you can pass them in at creation in your top level file. Obviously you will need to create an initialiser for Meeting that takes a MeetingStat.

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    let meetingStats = MeetingStats()
    let meeting = Meeting(stats: meetingStats)

They can both still be passed as EnvironmentObjects for use in Views.

2      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.