Skip to content

Bugfix: Preserve ForEach TabView State - #533

Merged
marcprux merged 1 commit into
skiptools:mainfrom
fhasse95:TabView-Identity-Bugfix
Oct 1, 2026
Merged

marcprux merged 1 commit into
skiptools:mainfrom
fhasse95:TabView-Identity-Bugfix

Conversation

@fhasse95

@fhasse95 fhasse95 commented Oct 1, 2026

Copy link
Copy Markdown
Member

This PR fixes a TabView state reset on Android when tab content is produced by an unrolled ForEach.

This appears to be a regression after #500. ForEachIdentityModifier keyed the rendered subtree with both the remembered ForEach namespace and the element identity. In TabView, content can leave and re-enter the composition, recreating that namespace and causing stable tab content to remount.

This change keeps the namespace for parent container child-key calculation, but uses only the stable element identity for the modifier's inner render key.

To reproduce:

import SwiftUI

struct RootTabView: View {
    var body: some View {
        TabView {
            ForEach(0..<3, id: \.self) { tab in
                TabContentView(tab: tab)
                    .tag(tab)
                    .tabItem {
                        Text("Tab \(tab)")
                    }
            }
        }
    }
}

private struct TabContentView: View {
    let tab: Int
    @State private var instanceID = String(UUID().uuidString.prefix(4))
    @State private var tapCount = 0

    var body: some View {
        VStack(spacing: 16) {
            Text("Tab \(self.tab)")
                .font(.title)
            Text("Instance: \(self.instanceID)")
                .font(.headline)
        }
    }
}

Before:
Before

After:
After

=> As you can see, before this change the instanceID state changes every time the tab is revisited, because the tab content is remounted. After this change, the instanceID remains stable when switching away from a tab and back.


Thank you for contributing to the Skip project! Please review the contribution guide at https://skip.dev/docs/contributing/ for advice and guidance on making high-quality PRs.

Use this space to describe your change and add any labels (bug, enhancement, documentation, etc.) to help categorize your contribution.

Skip Pull Request Checklist:

  • REQUIRED: I have signed the Contributor Agreement
  • REQUIRED: I have tested my change locally with swift test
  • OPTIONAL: I have tested my change on an iOS simulator or device
  • OPTIONAL: I have tested my change on an Android emulator or device
  • REQUIRED: I have checked whether this change requires a corresponding update in the Skip Fuse UI repository (link related PR if applicable)
  • OPTIONAL: I have added an example of any UI changes to the Showcase sample app

  • AI was used to generate or assist with generating this PR. Please specify below how you used AI to help you, and what steps you have taken to manually verify the changes.

@cla-bot cla-bot Bot added the cla-signed label Oct 1, 2026
@fhasse95

fhasse95 commented Oct 1, 2026

Copy link
Copy Markdown
Member Author

Hi @marcprux, thanks for merging my other PRs.

However, after merging the latest main into my temporary branch, I noticed this small Android regression. It seems related to #500 and affects TabView content produced by an unrolled ForEach, where local tab states can reset when switching tabs.

The fix is tiny and the PR includes a small repro plus before/after screenshots. If possible, it would be great to get this into the next skip-ui release as well. Thanks! 😊

@fhasse95 fhasse95 changed the title Bugfix: Stable TabView Content Bugfix: Preserve ForEach TabView State on Android Oct 1, 2026
@fhasse95 fhasse95 changed the title Bugfix: Preserve ForEach TabView State on Android Bugfix: Preserve ForEach TabView State Oct 1, 2026
@marcprux
marcprux merged commit c18d6a4 into skiptools:main Oct 1, 2026
2 checks passed
@fhasse95
fhasse95 deleted the TabView-Identity-Bugfix branch October 1, 2026 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants