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! :)
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 :
class UserSettings: ObservableObject {
    @Published var info: String = ""
}
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.
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.
let contentView = MyView().environmentObject(UserSettings())
You should see the text updating as time goes by.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With