@ObservedObject and Dictionaries in SwiftUI

I'm having a lot of trouble developing a macOS App using SwiftUI.
(macOS version 11.4, Xcode version 12.5)

struct ContentView: View {

    @ObservedObject var viewModel: ViewModel

    var body: some View {

        VStack {
            Text("RX frequency: " + viewModel.status.receiverStatus.frequency)
            Text("TX frequency: " + viewModel.status.transmitterStatus.frequency)
            Text("Ambient °C  : " + viewModel.status.climateSensorStatus["Ambient"]!.degrees)

The 3rd Text line dies with "Fatal error: Unexpectedly found nil while unwrapping an Optional value".

This is not happening in the ViewModel, so I'm thinking that @ObservedObject is incapable of dealing with dictionaries. Is this a bug, or am I asking too much?

It is surely capable of dictionaries.

obs-obj-dict

Something else is going on. All that I know is that your dictionary does not contain the key "Ambient" at the time the body property is invoked.

Could it be that your temperature info is being downloaded asynchronously?

Try using the ["Ambient", default: "..."] subscript, or make the whole view display conditionally on the data being available.

1 Like

Thank you tem. I've added a "fetch" into the ViewModel and this fixes the problem.

        init() {
            requestStatus()
        }

As a noobie, it's all very confusing as to what order things get instantiated, so I get there by trial and error. Please could you explain how to write your suggested ["Ambient", default: "..."] subscript? I have so much to learn!

:ok_hand:

Yes, that aspect of SwiftUI is a bit nebulous.

I suggest watching Demystify SwiftUI from this year's WWDC for better insights.

Last year's video Data Essentials in SwiftUI is also pretty good.

The SwiftUI lifecycle and things like @ObservedObject are illuminated in these videos.

lifecycle-diagram

Furthermore, this article about @State is worth checking out:

And of course the official guides:

SwiftUI: State and Data Flow


The subscript is written just like the normal one. In your case it could be:

.climateSensorStatus["Ambient", default: Temperature(20)].degrees

Actually, you probably should use the nil-coalescing operator here:

.climateSensorStatus["Ambient"]?.degrees ?? "⏳"
2 Likes

The nil-coalescing way works great. The other way doesn't, probably because "degrees" is not the only variable type in there. In fact, I'm making extensive use of dictionaries for other devices.

I've got my SwiftNIO server running, but I'm having a hard time with the client end. It's for a local network, so I don't want HTTPS - just TCP without the overhead. For now, I'm simulating the server request/responses within the client without the hardware and I can evolve the API more easily.

I'm extremely grateful for your help. Thank you.