Repository navigation
[iOS] Clear DevLoadingView when its host is invalidated - #59165
Open
fairysword wants to merge 1 commit into
Open
fairysword wants to merge 1 commit into
fairysword wants to merge 1 commit into
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
In an iOS app that creates a React Native host for an individual screen, closing that screen and releasing its host while the bundle is still loading can leave the “Loading from Metro…” banner visible over the native screen. If the host is released before the initial banner delay expires, the banner can appear after the screen has already closed.
RCTDevLoadingViewretains_initialMessageBlock, whose callback captures the module. Normally, bundle progress or completion clears this block. When the host is released before the first bundle progress update, the module can remain retained by this cycle. Cleanup indealloccannot break the cycle, and the module currently has noinvalidateimplementation to clear its pending work and window during host teardown.Cleanup also needs to reject queued display work. An initial-message or download-progress callback can enqueue a display block before invalidation, and that block can run after invalidation has cleared the window. The invalidation check runs inside the queued block so it cannot recreate the banner after teardown.
This change:
invalidateto cancel the pending initial message and hide and release the loading window and its views on the main queue.Changelog:
[IOS] [FIXED] - Clear the development loading banner when its React Native host is invalidated, including pending messages that could appear after teardown.
Test Plan:
Reproduced on main baseline
27e77216ef2557ccbf31a01067a44dc1dd2ec846and verified with the fix applied, using RNTester in Debug, Xcode 27.0, and an iPhone 18 Pro simulator running iOS 27.0.The reproduction branch contains the native RNTester controller, a six-second Metro delay proxy, and the Run configuration. Setup and run instructions are included with the demo.
The controller creates and releases a real
RCTReactNativeFactory/RCTHost; it does not call DevLoadingView's display, hide, or invalidation methods. Native controls sit above the loading window so the close button remains tappable while loading.Reproduction
Before the fix: the RN view is removed and the weak reference to its host is nil, but the loading banner remains over the native screen.
With the fix: the banner clears during invalidation and does not reappear. A new host still displays its loading banner normally and clears it when RNTester finishes loading.
Additional lifecycle checks
Queued display regression
Compared the same cleanup implementation with and without the
_invalidatedcheck, using repeated real host creation and release in the simulator. The controller did not call DevLoadingView methods directly or retain the module. Each variant ran 60 cycles per timing scenario:For both download races, the module deallocated about 3.9 seconds after recreating the window. This confirms that relying on eventual
deallocdoes not ensure the banner disappears when its host is released.Both baseline and patched Debug simulator builds succeeded. The build command used from
packages/rn-testerwas:xcodebuild \ -workspace RNTesterPods.xcworkspace \ -scheme RNTester \ -configuration Debug \ -sdk iphonesimulator \ -destination 'generic/platform=iOS Simulator' \ -derivedDataPath build/devloading-invalidation/DerivedData \ -jobs 6 \ ARCHS=arm64 ONLY_ACTIVE_ARCH=YES CODE_SIGNING_ALLOWED=NO \ buildAutomated regression tests
RCTDevLoadingViewTestsis included in theRNTesterUnitTeststarget and covers:showMessageandupdateProgresscallbacks from creating a window after invalidation.A test subclass overrides a private wrapper around
RCTRunningInTestEnvironment()so the tests exercise the real display path instead of returning early under XCTest. The production implementation still uses the same environment check.The targeted run used the repository's default AddressSanitizer and UndefinedBehaviorSanitizer test configuration: 3 tests passed, 0 failed, 0 skipped.
For negative controls, the pending-message cancellation, window cleanup, and invalidation guard were each removed independently. Each variant failed its corresponding test while the other two tests passed. After restoring the complete fix, all three tests passed again.
Run the regression tests from
packages/rn-tester, selecting an available iOS simulator: