Skip to content

Observing stops collecting when body becomes an EmptyView() #146

@valentinilk

Description

@valentinilk

Hey,

I'm observing the following issue. Whenever the only child of Observing in SwiftUI becomes an EmptyView(), SKIE's Observing view will stop collecting my flow.

Steps to reproduce:

// Some ViewModel in .kt
val viewState = flowOf(listOf("foo", "bar"))
    .map { ScreenState.Available(it) }
    .stateIn(viewModelScope, SharingStarted.Lazily, ScreenState.Initial)

// ScreenState
sealed interface ScreenState {
    data object Initial : ScreenState
    data class Available(val states: List<String>) : ScreenState
}
// Some View in .swift
private let viewModel = SomeViewModel()

var body: some View {
    VStack {
        // State independent UI
        Observing(viewModel.viewState) { viewState in
            switch onEnum(of: viewState) {
            case .initial: EmptyView()
            case .available(let state): Text("Available")
            }
        }
    }
    .navigationTitle("Some Title")
}

This will never display the text Available on my screen. Not even the logging statements in my flows get fired, as if the flows aren't being collected.

Funnily enough, I can fix it by simply replacing the EmptyView() with a simple Text.

// Some View in .swift
private let viewModel = SomeViewModel()

var body: some View {
    VStack {
        // State independent UI
        Observing(viewModel.viewState) { viewState in
            switch onEnum(of: viewState) {
            case .initial: Text("Loading") // <--------------- EmptyView() replaced with the text
            case .available(let state): Text("Available")
            }
        }
    }
    .navigationTitle("Some Title")
}

Same behavior can be observed with a ForEach, which might return an EmptyView() under the hood, if the list I'm handing in is empty?!

// Some ViewModel in .kt
val viewState = flowOf(listOf("foo", "bar"))
    .stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
// Some View in .swift
private let viewModel = SomeViewModel()

var body: some View {
    VStack {
        // State independent UI
        Observing(viewModel.viewState) { viewState in
            ForEach(viewState) { state in
                Text(state)
            }
        }
    }
    .navigationTitle("Some Title")
}

So the sample above won't display my list. But the following code works:

// Some View in .swift
private let viewModel = SomeViewModel()

var body: some View {
    VStack {
        // State independent UI
        Observing(viewModel.viewState) { viewState in
            Text("Hello") // <--------------- Added
            ForEach(viewState) { state in
                Text(state)
            }
        }
    }
    .navigationTitle("Some Title")
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions