Skip to content

Fix Switch crash when onChange fires after view recycle - #59216

Open
Mr-Perfectuz wants to merge 1 commit into
react:mainfrom
Mr-Perfectuz:fix/switch-null-event-emitter-59203
Open

Mr-Perfectuz wants to merge 1 commit into
react:mainfrom
Mr-Perfectuz:fix/switch-null-event-emitter-59203

Conversation

@Mr-Perfectuz

Copy link
Copy Markdown

Summary:

Fixes #59203.

-[RCTSwitchComponentView onChange:] dereferences _eventEmitter without checking it. RCTViewComponentView -prepareForRecycle resets _eventEmitter, and on iOS 26+ the redesigned UISwitch can deliver its ValueChanged action from a deferred _UIAfterCACommitBlock (e.g. after a drag/long-press). If the view gets recycled in between, onChange: runs on a recycled view and crashes with EXC_BAD_ACCESS inside EventEmitter::dispatchEvent.

This adds an early return when _eventEmitter is null, matching the existing guards in RCTScrollViewComponentView, RCTImageComponentView, RCTModalHostViewComponentView and RCTVirtualViewComponentView. Dropping the event is correct since the recycled view no longer represents that React node.

Changelog:

[IOS] [FIXED] - Fix crash in Switch when onChange fires after the view has been recycled

Test Plan:

  • The crash is intermittent and timing-dependent (reported from production), so it is not reproducible on demand.
  • The change is limited to an early-return null guard before the emitter dereference; normal toggling in RNTester's Switch examples is unaffected since _eventEmitter is set for mounted views.

🤖 Generated with Claude Code

Guard _eventEmitter in RCTSwitchComponentView onChange before
dispatch, matching ScrollView/Image/Modal. prepareForRecycle clears
the emitter while UISwitch can still deliver a deferred ValueChanged.

Fixes react#59203

Co-authored-by: Cursor <cursoragent@cursor.com>
@meta-cla

meta-cla Bot commented Oct 20, 2026

Copy link
Copy Markdown

Hi @Mr-Perfectuz!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@Mr-Perfectuz

Copy link
Copy Markdown
Author

done CLA

@hcbylmz

hcbylmz commented Oct 20, 2026

Copy link
Copy Markdown

Thanks for the fix. The issue says the crash can't be reproduced on demand, so I wrote a unit test that hits this code path. It may help this PR get merged.

It fails on main and passes with your change. Tested on the iPhone 17 simulator (iOS 27), Xcode 27:

  • Without the fix: the test crashes in -[RCTSwitchComponentView onChange:]. UBSan reports reference binding to null pointer of type 'const facebook::react::BaseViewEventEmitter'.
  • With the fix: ** TEST SUCCEEDED **

The test calls the action directly. Unit tests run without UIApplication, so sendActionsForControlEvents: does not dispatch anything there. With that call the test passes even without the fix.

packages/rn-tester/RNTesterUnitTests/RCTSwitchComponentViewTests.mm
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#import <XCTest/XCTest.h>

#import <React/RCTSwitchComponentView.h>

@interface RCTSwitchComponentViewTests : XCTestCase
@end

@implementation RCTSwitchComponentViewTests

// UISwitch can deliver its value-changed action after the view was recycled
// (prepareForRecycle resets the event emitter). This must not crash.
- (void)testValueChangedAfterRecycleDoesNotCrash
{
  RCTSwitchComponentView *view = [[RCTSwitchComponentView alloc] initWithFrame:CGRectMake(0, 0, 51, 31)];
  [view prepareForRecycle];

  UISwitch *switchView = (UISwitch *)view.contentView;
  XCTAssertTrue([switchView isKindOfClass:[UISwitch class]]);

  [switchView setOn:YES animated:NO];

  // Unit tests run without UIApplication, so UIControl does not dispatch
  // actions. Invoke the registered action directly instead.
  NSArray<NSString *> *actions = [switchView actionsForTarget:view forControlEvent:UIControlEventValueChanged];
  XCTAssertEqual(actions.count, 1u);
  for (NSString *action in actions) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [view performSelector:NSSelectorFromString(action) withObject:switchView];
#pragma clang diagnostic pop
  }
}

@end

The file also has to be added to the RNTesterUnitTests target in RNTesterPods.xcodeproj (4 lines in project.pbxproj). Run it with:

xcodebuild test -workspace RNTesterPods.xcworkspace -scheme RNTester \
  -destination 'platform=iOS Simulator,name=iPhone 17,OS=27.0' \
  -only-testing:RNTesterUnitTests/RCTSwitchComponentViewTests

Feel free to add it to this PR.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 20, 2026
@meta-cla

meta-cla Bot commented Oct 20, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Oct 20, 2026
@meta-codesync

meta-codesync Bot commented Oct 20, 2026

Copy link
Copy Markdown

@javache has imported this pull request. If you are a Meta employee, you can view this in D126404965.

This branch has not been deployed

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[iOS][Fabric] RCTSwitchComponentView onChange: dereferences _eventEmitter without a null check (EXC_BAD_ACCESS after view recycle)

2 participants