Skip to content

Fix _flushViewController crash by using viewController index map#301

Merged
msaps merged 1 commit into
uias:mainfrom
sanghun0724:fix/flush-view-controller-crash
Apr 12, 2026
Merged

Fix _flushViewController crash by using viewController index map#301
msaps merged 1 commit into
uias:mainfrom
sanghun0724:fix/flush-view-controller-crash

Conversation

@sanghun0724
Copy link
Copy Markdown
Contributor

@sanghun0724 sanghun0724 commented Apr 11, 2026

Summary

Fixes a crash in UIPageViewController's internal _flushViewController method caused by stale currentIndex values in the UIPageViewControllerDataSource methods.

@msaps

  • Root cause: pageViewController(_:viewControllerBefore:) and pageViewController(_:viewControllerAfter:) use currentIndex to determine which adjacent page to return. After a cancelled or interrupted page transition, currentIndex can become stale (not yet updated to reflect the actual displayed page). This causes the data source to return an incorrect view controller, leading to an internal state inconsistency in UIPageViewController that triggers the _flushViewController crash.

  • Fix: Use viewControllerIndexMap.index(for: viewController) to look up the actual index of the viewController parameter passed by UIPageViewController, falling back to currentIndex only when the view controller is not found in the map. This ensures the correct adjacent page is always returned regardless of currentIndex state.

  • Safety: This change is backward-compatible. viewControllerIndexMap already tracks all managed view controllers and their indices. The fallback to currentIndex preserves existing behavior for any edge case where the view controller is not in the map.

          Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x114c70 __exceptionPreprocess
1  libobjc.A.dylib                0x31224 objc_exception_throw
2  Foundation                     0x9b3f50 _userInfoForFileAndLine
3  UIKitCore                      0xecf1b4 -[UIPageViewController _flushViewController:animated:]
4  UIKitCore                      0xecf240 -[UIPageViewController queuingScrollView:didFlushView:animated:]
5  UIKitCore                      0xed8344 -[_UIQueuingScrollView _flushView:animated:]
6  UIKitCore                      0xed847c -[_UIQueuingScrollView _replaceViews:updatingContents:adjustContentInsets:animated:]
7  UIKitCore                      0xedad34 -[_UIQueuingScrollView _didScrollWithAnimation:force:]
8  UIKitCore                      0xed7b58 -[_UIQueuingScrollView _scrollViewAnimationEnded:finished:]
9  UIKitCore                      0x70d4f8 -[UIScrollView animator:stopAnimation:fraction:]
10 UIKitCore                      0x70d314 -[UIAnimator stopAnimation:]
11 UIKitCore                      0x634f4c -[UIAnimator _advanceAnimationsOnScreenWithIdentifier:withTimestamp:]
12 UIKitCore                      0xfb330 _UIUpdateSequenceRunNext
13 UIKitCore                      0xf88bc schedulerStepScheduledMainSectionContinue
14 UpdateCycle                    0x156c UC::DriverCore::continueProcessing()
15 CoreFoundation                 0x21e5c __CFMachPortPerform
16 CoreFoundation                 0x64384 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
17 CoreFoundation                 0x642ac __CFRunLoopDoSource1
18 CoreFoundation                 0x2f368 __CFRunLoopRun
19 CoreFoundation                 0x2e1d0 _CFRunLoopRunSpecificWithOptions
20 GraphicsServices               0x1498 GSEventRunModal
21 UIKitCore                      0x1212cc -[UIApplication _run]
22 UIKitCore                      0x8c158 UIApplicationMain
23 Karrot                         0x4188 main + 11 (main.swift:11)
24 ???                            0x18920dc1c (Missing)

Related Issues

Test Plan

  • swift build passes successfully
  • Verified fix resolves the crash in a production app using Tabman with rapid tab switching and interrupted transitions

When UIPageViewController asks for the view controller before/after a
given view controller, the current implementation uses `currentIndex`
to determine which page to return. However, `currentIndex` can become
stale after a cancelled or interrupted transition, causing the data
source to return an incorrect view controller. This leads to an internal
state inconsistency in UIPageViewController, which eventually triggers
a `_flushViewController` crash.

This fix resolves the issue by looking up the actual index of the passed
`viewController` from `viewControllerIndexMap` first, falling back to
`currentIndex` only when the view controller is not found in the map.
This ensures the correct adjacent page is always returned, regardless
of whether `currentIndex` has been updated yet.

Refs: uias#286
Refs: uias/Tabman#556
@msaps msaps merged commit 978ac7e into uias:main Apr 12, 2026
@msaps
Copy link
Copy Markdown
Member

msaps commented Apr 12, 2026

Thanks @sanghun0724!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

NSInternalInconsistencyException in PatchedPageViewController

2 participants