skip to Main Content

The background color and text in ReadyDashboardView below don’t update when isConnected is updated. Obviously I want to update the view when the connection is made. I was expecting that publishing everything in the chain to that variable would make it update instantly in swiftui. Instead, it’s always rendering using the value provided when the variable is instantiated. Here’s a very simplified look at the situation:

Is there another swiftui feature i should be using or am I going to have to make some sweeping changes to my codebase?

import SwiftUI

@main
struct TestApp: App {
    @StateObject private var env = PrinterEnv()
    
    var body: some Scene {
        WindowGroup {
            ReadyDashboardView()
                .environmentObject(env)
        }
    }
}

struct ReadyDashboardView: View {
    @EnvironmentObject var env: PrinterEnv

    var body: some View {
        VStack {
            HStack {
                Spacer()
                VStack {
                    Text(env.selectedPrinter?.isConnected ?? false ? "Printer Ready" : "Not Connected")
                        .padding(.bottom)
                }
                Spacer()
            }
            .background(env.selectedPrinter?.isConnected ?? false ? .green : .red)
           
        Button("Connect") { env.selectedPrinter?.isConnected = true }
        Button("Disconnect") { env.selectedPrinter?.isConnected = false }
            
        }
    }
}

class PrinterEnv: ObservableObject {
    @Published var configuredPrinters: [Printer] = []
    @Published var selectedPrinter: Printer?
    
    init() {
        configuredPrinters.append(contentsOf: [Printer()])
        selectedPrinter = configuredPrinters.first
    }
}

class Printer: ObservableObject {
    @Published var isConnected = false
}

2

Answers


  1. I suggest you do not nest ObservableObject, it does not work very well.

    Try a Printer struct for example, such as:

    struct Printer: Identifiable {
        let id = UUID()
        var isConnected = false
    }
    
    Login or Signup to reply.
  2. I do not think the problem is specifically related to nested observable objects. Nesting them is working fine and is even recommended by community members as the best way to manage app wide states and ensure performance is acceptable.

    See this: https://www.fivestars.blog/articles/app-state/

    That said, I believe it’s probably related to the wrapping of the observable object with @Published property wrapper.

    Try this in a playground:

    import SwiftUI
    
    class AppState: ObservableObject {
        let fooState = FooState()
        let barState = BarState()
    }
    
    class FooState: ObservableObject {
        @Published var foo: Int = 42
    }
    
    class BarState: ObservableObject {
        @Published var bar: String = Date().debugDescription
    }
    
    struct FooView: View {
    
        @EnvironmentObject var fooState: FooState
    
        var body: some View {
            Text("foo: (fooState.foo)")
        }
    }
    
    struct BarView: View {
    
        @EnvironmentObject var barState: BarState
    
        var body: some View {
            Text("bar: (barState.bar)")
        }
    }
    
    struct ContentView: View {
    
        @EnvironmentObject var appState: AppState
    
        var body: some View {
            VStack {
                Spacer()
    
                FooView()
                    .environmentObject(appState.fooState)
    
                Button("update foo") {
                    appState.fooState.foo = Int.random(in: 1...100)
                }
    
                Spacer()
    
                BarView()
                    .environmentObject(appState.barState)
    
                Button("update bar") {
                    appState.barState.bar = Date().debugDescription
                }
    
                Spacer()
            }
        }
    }
    
    import PlaygroundSupport
    
    PlaygroundPage.current.liveView = UIHostingController(
        rootView: ContentView()
            .frame(width: 320, height: 414)
            .environmentObject(AppState())
    )
    

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search