Description
In the new SwiftUI TabView-based iOS implementation (ios/PagerView.swift, 8.0.x), the tag index is derived with props.children.firstIndex(of:) while iterating that same array:
TabView(selection: $props.currentPage) {
ForEach(props.children) { child in
if let index = props.children.firstIndex(of: child) {
RepresentableView(view: child.view)
.tag(index)
}
}
}
firstIndex(of:) returns the index of the first element equal under Equatable, so when two entries in props.children compare equal, both resolve to the same index and multiple pages render with an identical .tag. With TabView(selection:), duplicate tags make the selection bind to the wrong page (page snaps back / won't switch) and, on iOS 17 and below, throw NSInternalInconsistencyException (hard crash). The lookup is also O(n²).
Note: The crash reproduces only on iOS 17 and below (verified on both a device and a simulator). On newer OS versions the duplicate tag does not crash.
Library version
react-native-pager-view 8.0.0 – 8.0.2 (current master)
React Native version
0.84.1 — New Architecture / Fabric, Hermes. (The faulty code path does not depend on the RN version.)
Platform
iOS only (new SwiftUI implementation). Crash occurs on iOS 17 and below.
Steps to reproduce
- Render a
<PagerView> with multiple children whose backing items compare equal under the element's Equatable conformance.
- Swipe or programmatically change pages.
- On iOS 17 or below, this crashes with
NSInternalInconsistencyException.
Expected
Each page gets a unique tag matching its position; switching works and never crashes.
Actual
Duplicate .tag values → wrong/sticky page selection, and on iOS 17 and below an NSInternalInconsistencyException crash.
Proposed fix
Use the positional index from enumerated() (always unique) and keep stable identity via the element id:
- ForEach(props.children) { child in
- if let index = props.children.firstIndex(of: child) {
- RepresentableView(view: child.view)
- .tag(index)
- }
- }
+ ForEach(Array(props.children.enumerated()), id: \.element.id) { index, child in
+ RepresentableView(view: child.view)
+ .tag(index)
+ }
Description
In the new SwiftUI
TabView-based iOS implementation (ios/PagerView.swift, 8.0.x), the tag index is derived withprops.children.firstIndex(of:)while iterating that same array:firstIndex(of:)returns the index of the first element equal underEquatable, so when two entries inprops.childrencompare equal, both resolve to the same index and multiple pages render with an identical.tag. WithTabView(selection:), duplicate tags make the selection bind to the wrong page (page snaps back / won't switch) and, on iOS 17 and below, throwNSInternalInconsistencyException(hard crash). The lookup is also O(n²).Library version
react-native-pager-view 8.0.0 – 8.0.2 (current master)
React Native version
0.84.1 — New Architecture / Fabric, Hermes. (The faulty code path does not depend on the RN version.)
Platform
iOS only (new SwiftUI implementation). Crash occurs on iOS 17 and below.
Steps to reproduce
<PagerView>with multiple children whose backing items compare equal under the element'sEquatableconformance.NSInternalInconsistencyException.Expected
Each page gets a unique tag matching its position; switching works and never crashes.
Actual
Duplicate
.tagvalues → wrong/sticky page selection, and on iOS 17 and below anNSInternalInconsistencyExceptioncrash.Proposed fix
Use the positional index from
enumerated()(always unique) and keep stable identity via the elementid: