Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass EnvironmentObject to an ObservableObject class [duplicate]

Tags:

ios

swift

swiftui

I have made a SwiftUI app that repeatedly fetches telemetry data to update custom views. The views use a variable stored in an EnvironmentObject.

struct updateEO{
    @EnvironmentObject var settings:UserSettings
    func pushSettingUpdate(telemetry: TelemetryData) {
        settings.info = telemetry
        print(settings.info)
    }
}

class DownloadTimer : ObservableObject {
    var timer : Timer!
    let didChange = PassthroughSubject<DownloadTimer,Never>()
    @Published var telemetry = TelemetryData()
    func start() {
        connectToClient()
        self.timer?.invalidate()
        self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) {
            _ in
            guard let url = URL(string: "http://127.0.0.1:25555/api/telemetry") else {
                print("Invalid URL")
                return
            }

            let request = URLRequest(url: url)

            URLSession.shared.dataTask(with: request) { data, response, error in
                if let data = data {
                    if let decodedResponse = try? JSONDecoder().decode(TelemetryData.self, from: data) {
                        DispatchQueue.main.async {
                            updateEO().pushSettingUpdate(telemetry: decodedResponse)
                        }
                        return
                    }
                }
            }.resume()
        }
    }
}

At runtime, when the telemetry is passed to the pushSettingUpdate(telemetry: decodedResponse), the app crashes with an error of 'Fatal error: No ObservableObject of type UserSettings found.'. I understand I may need to pass the struct the EnvironmentObject but I am not sure on how to do that. Any help would be much appreciated. Thanks! :)

like image 725
xp3dx Avatar asked Sep 15 '25 06:09

xp3dx


1 Answers

You should use @EnvironmentObject in your view and pass it down to your model if needed.

Here, struct updateEO is not a view.

I've created a simpler example to show you how to do this :

UserSettings

class UserSettings: ObservableObject {
    @Published var info: String = ""
}

DownloadTimer

class DownloadTimer: ObservableObject {
    var timer : Timer?
    
    func start(settings: UserSettings) {
        self.timer?.invalidate()
        self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { t in
            settings.info = t.fireDate.description
        }
    }
}

And you call start (with UserSettings as parameter) when the Text appears.

MyView

struct MyView: View {
    @StateObject private let downloadTimer = DownloadTimer()
    @EnvironmentObject var settings: UserSettings
    
    var body: some View {
        Text(settings.info)
            .onAppear {
                self.downloadTimer.start(settings: self.settings)
        }
    }
}

And don't forget to call .environmentObject function to inject your UserSettings in SceneDelegate.

SceneDelegate

let contentView = MyView().environmentObject(UserSettings())

You should see the text updating as time goes by.

like image 130
Nicolas Mandica Avatar answered Sep 17 '25 21:09

Nicolas Mandica