How to automatically update SwiftUI View depending on an ObservableObject hidden as an Associated Value of an Enum Property?

Viewed 51

Context

I have an Enum where one of its Cases has an ObservableObject as an Associated Value. I also have a SwiftUI View with a Property of this Enum. This View displays Data of the associated ObservableObject, if applicable. However, since I can't define the Enum itself as an ObservedObject, the UI does not change when the associated ObservableObject changes.


Code

// This is actually a CoreData NSManagedObject.
class CustomComponent: ObservableObject { ... }

enum Component {
    case componentA
    case componentB
    case custom(_ customComponent: CustomComponent)

    static func getComponents(with customComponents: FetchedResults<CustomComponent>) -> [Component] {
        var components = [.componentA, .componentB]
        for customComponent in customComponents {
            components.append(.custom(customComponent))
        }
        return components
    }

    var name: String {
        switch self {
            case .componentA: return "Component A"
            case .componentB: return "Component B"
            case .custom(let customComponent): return customComponent.name
        }
    }
}

struct ComponentsView: View {
    @FetchRequest(sortDescriptors: [SortDescriptor(\.name)]) private var customComponents: FetchedResults<CustomComponent>

    var body: some View {
        ForEach(Component.getComponents(with: customComponents)) { component in
            ComponentView(with: component)
        }
    }
}

struct ComponentView: View {
    // I can't define this as ObservedObject, however, this View should update when the associated ObservableObject updates.
    let component: Component

    var body: some View {
        Text(component.name)
    }
}

Question

How can I achieve my goal, that, even though the ObservableObject is hidden as an Associated Value of an Enum, the ComponentView updates when the Associated Value changes?

1 Answers

I found a solution to the problem. Even though it involves some duplicate Code, it is the best I was able to come up with. If you know a better one, feel free to share it!


Code

struct ComponentView: View {
    let component: Component

    var body: some View {
        if case .custom(let customComponent) = component {
            CustomComponentView(customComponent: customComponent)
        } else {
            Text(component.name)
        }
    }
}

fileprivate struct CustomComponentView: View {
    @ObservedObject var customComponent: CustomComponent

    var body: some View {
        Text(customComponent.component.name)
    }
}
Related