Search code examples
swiftswiftuiasync-awaitswiftui-tabview

TabView selection restores to previous one after tapping on a different tab


I'm encountering a strange behavior with TabView. In particular, I've created a UI with 4 different tabs. Every tab performs something different. The application starts with the first one selected. Tapping on the third tab FavoritesView is created.

struct FavoritesView: View {
    @StateObject private var viewModel = FavoritesViewModel()
    
    var body: some View {
        switch viewModel.viewState {
        case .idle:
            Color.red
                .onAppear {
                    Task {
                        try await viewModel.fetchBookmarks()
                    }
                }
        case .loading:
            ProgressView()
        case .failed(let error):
            Text(error.localizedDescription)
        case .finished(let newsLetters):
            if newsLetters.isEmpty {
                EmptyResultsView()
            } else {
                List(newsLetters) { newsLetter in
                    Text(newsLetter.title)
                }
            }
        }
    }
}

This View has a view model associated to it.

enum ViewState<Value> {
    case idle
    case loading
    case finished(Value)
    case failed(Error)
}

@MainActor
final class FavoritesViewModel: ObservableObject {
    @Published private(set) var viewState = ViewState<[NewsLetter]>.idle
    
    @Stored(in: .newsLetterStore) var newsLetters
    
    func fetchBookmarks() async throws {
        do {
            try await $newsLetters.itemsHaveLoaded()
            viewState = .finished($newsLetters.items)
        } catch {
            viewState = .failed(error)
        }
    }
}

Th fact is that, when viewState = .finished($newsLetters.items) is executed, the screen becomes red (due to Color.red) for just one second but the selection goes back to the first tab. In order to open the third tab effectively I must tap on it a second time. Commenting out viewState = the behavior is the one I expected. The screen remains red.

Any hint on that? I'm wrapping my head around but I cannot find a solution.

UPDATE

Here you can find a sample repo where is possible to replicate the same behavior I've described.

https://github.com/flexaddicted/sample-bug


Solution

  • Looking at the repo, I can see that you don't have .tag defined on tabItem.

    This generally results in selection switching to the first tab after changes in view model.

    Whether that is a feature or a bug in SwiftUI is hard to tell, but you can certainly resolve it by adding that.