From 59248a41543269ef308f1e74b247c26309b3a07a Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 4 Sep 2026 18:25:02 -0400 Subject: [PATCH 1/6] fix: initialize iOS dev settings before bundle load --- .../runtime/iostests/RCTInstanceTests.mm | 53 ++++++++++++++++++- .../platform/ios/ReactCommon/RCTInstance.mm | 5 +- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm index b1d1b16b45ee..0f0e45262982 100644 --- a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm +++ b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm @@ -86,6 +86,32 @@ - (instancetype)init @end +@interface FakeDevSettings : FakeEagerModule +- (void)setupHMRClientWithBundleURL:(NSURL *)bundleURL; +@end + +@implementation FakeDevSettings + +static NSString *const FakeDevSettingsInitialize = @"DevSettings.initialize"; + ++ (NSString *)moduleName +{ + return @"DevSettings"; +} + +- (void)initialize +{ + @synchronized([FakeDevSettings initCounts]) { + [[FakeDevSettings initCounts] addObject:FakeDevSettingsInitialize]; + } +} + +- (void)setupHMRClientWithBundleURL:(__unused NSURL *)bundleURL +{ +} + +@end + @interface RCTInstanceTests : XCTestCase @end @@ -108,8 +134,12 @@ - (void)setUp OCMStub([_mockTMMDelegate getModuleClassFromName:nullptr]).ignoringNonObjectArgs().andDo(^(NSInvocation *invocation) { const char *requestedName = nullptr; [invocation getArgument:&requestedName atIndex:2]; - Class result = - (requestedName != nullptr && strcmp(requestedName, "FakeEagerModule") == 0) ? [FakeEagerModule class] : Nil; + Class result = Nil; + if (requestedName != nullptr && strcmp(requestedName, "FakeEagerModule") == 0) { + result = [FakeEagerModule class]; + } else if (requestedName != nullptr && strcmp(requestedName, "DevSettings") == 0) { + result = [FakeDevSettings class]; + } [invocation setReturnValue:&result]; }); } @@ -172,6 +202,25 @@ - (void)testInstantiatesRequestedModuleOnMainQueue [instance invalidate]; } +- (void)testInitializesDevSettingsBeforeLoadingBundle +{ + OCMStub([_mockDelegate unstableModulesRequiringMainQueueSetup]).andReturn(@[]); + + XCTestExpectation *loadStarted = [self expectationWithDescription:@"bundle load started"]; + __block NSUInteger initializeCountAtBundleLoad = 0; + OCMStub([_mockDelegate loadBundleAtURL:[OCMArg any] onProgress:[OCMArg any] onComplete:[OCMArg any]]) + .andDo(^(NSInvocation *_) { + initializeCountAtBundleLoad = [FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize]; + [loadStarted fulfill]; + }); + + RCTInstance *instance = [self makeInstance]; + [self waitForExpectations:@[ loadStarted ] timeout:5.0]; + [instance invalidate]; + + XCTAssertEqual(initializeCountAtBundleLoad, 1u); +} + - (void)testBundleLoadAwaitsMainQueueModuleSetup { OCMStub([_mockDelegate unstableModulesRequiringMainQueueSetup]).andReturn(@[ FakeEagerModule.moduleName ]); diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index de522b07d035..a97b5764b193 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -543,6 +543,8 @@ - (void)_loadJSBundle:(NSURL *)sourceURL } #endif + RCTDevSettings *const devSettings = (RCTDevSettings *)[_turboModuleManager moduleForName:"DevSettings"]; + __weak __typeof(self) weakSelf = self; [_delegate loadBundleAtURL:sourceURL onProgress:^(RCTLoadingProgress *progressData) { @@ -567,9 +569,6 @@ - (void)_loadJSBundle:(NSURL *)sourceURL [strongSelf handleBundleLoadingError:error]; return; } - // DevSettings module is needed by _loadScriptFromSource's callback so prior initialization is required - RCTDevSettings *const devSettings = - (RCTDevSettings *)[strongSelf->_turboModuleManager moduleForName:"DevSettings"]; [strongSelf _loadScriptFromSource:source]; // Set up hot module reloading in Dev only. [strongSelf->_performanceLogger markStopForTag:RCTPLScriptDownload]; From 650caec0e4424e1d087626fd9fa3048ef359a0e7 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 4 Sep 2026 18:40:24 -0400 Subject: [PATCH 2/6] fix: avoid retaining dev settings during bundle load --- .../runtime/iostests/RCTInstanceTests.mm | 24 ++++++++++++++----- .../platform/ios/ReactCommon/RCTInstance.mm | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm index 0f0e45262982..b5e7b1197083 100644 --- a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm +++ b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm @@ -8,6 +8,7 @@ #import #import +#import #import #import #import @@ -202,23 +203,34 @@ - (void)testInstantiatesRequestedModuleOnMainQueue [instance invalidate]; } -- (void)testInitializesDevSettingsBeforeLoadingBundle +- (void)testInitializesDevSettingsAfterBundleLoadFailure { OCMStub([_mockDelegate unstableModulesRequiringMainQueueSetup]).andReturn(@[]); XCTestExpectation *loadStarted = [self expectationWithDescription:@"bundle load started"]; - __block NSUInteger initializeCountAtBundleLoad = 0; + __block RCTSourceLoadBlock loadComplete; OCMStub([_mockDelegate loadBundleAtURL:[OCMArg any] onProgress:[OCMArg any] onComplete:[OCMArg any]]) - .andDo(^(NSInvocation *_) { - initializeCountAtBundleLoad = [FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize]; + .andDo(^(NSInvocation *invocation) { + __unsafe_unretained RCTSourceLoadBlock completion; + [invocation getArgument:&completion atIndex:4]; + loadComplete = [completion copy]; [loadStarted fulfill]; }); RCTInstance *instance = [self makeInstance]; [self waitForExpectations:@[ loadStarted ] timeout:5.0]; - [instance invalidate]; - XCTAssertEqual(initializeCountAtBundleLoad, 1u); + XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 0u); + + RCTFatalHandler previousFatalHandler = RCTGetFatalHandler(); + RCTSetFatalHandler(^(__unused NSError *error){ + }); + loadComplete([NSError errorWithDomain:@"RCTInstanceTests" code:1 userInfo:nil], nil); + RCTSetFatalHandler(previousFatalHandler); + + XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 1u); + + [instance invalidate]; } - (void)testBundleLoadAwaitsMainQueueModuleSetup diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index a97b5764b193..dffdce549098 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -543,8 +543,6 @@ - (void)_loadJSBundle:(NSURL *)sourceURL } #endif - RCTDevSettings *const devSettings = (RCTDevSettings *)[_turboModuleManager moduleForName:"DevSettings"]; - __weak __typeof(self) weakSelf = self; [_delegate loadBundleAtURL:sourceURL onProgress:^(RCTLoadingProgress *progressData) { @@ -565,6 +563,8 @@ - (void)_loadJSBundle:(NSURL *)sourceURL return; } + RCTDevSettings *const devSettings = + (RCTDevSettings *)[strongSelf->_turboModuleManager moduleForName:"DevSettings"]; if (error) { [strongSelf handleBundleLoadingError:error]; return; From 27dcad6d8bf10b197798281a69ba0e8b7b874549 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 4 Sep 2026 18:44:42 -0400 Subject: [PATCH 3/6] test: isolate bundle failure handling --- .../react/runtime/iostests/RCTInstanceTests.mm | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm index b5e7b1197083..11c1ab2afd08 100644 --- a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm +++ b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm @@ -8,7 +8,6 @@ #import #import -#import #import #import #import @@ -19,6 +18,10 @@ using namespace facebook::react; +@interface RCTInstance (RCTInstanceTests) +- (void)handleBundleLoadingError:(NSError *)error; +@end + @interface FakeEagerModule : NSObject @property (class, readonly) NSCountedSet *initCounts; @property (class, nullable) void (^onInit)(void); @@ -222,14 +225,13 @@ - (void)testInitializesDevSettingsAfterBundleLoadFailure XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 0u); - RCTFatalHandler previousFatalHandler = RCTGetFatalHandler(); - RCTSetFatalHandler(^(__unused NSError *error){ - }); + id instanceMock = OCMPartialMock(instance); + OCMStub([instanceMock handleBundleLoadingError:[OCMArg any]]); loadComplete([NSError errorWithDomain:@"RCTInstanceTests" code:1 userInfo:nil], nil); - RCTSetFatalHandler(previousFatalHandler); XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 1u); + [instanceMock stopMocking]; [instance invalidate]; } From c5d4dfd7fb95c3b34d798f05da8daf0b044b2b4c Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 4 Sep 2026 18:54:00 -0400 Subject: [PATCH 4/6] fix: avoid reload setup after invalidation --- .../runtime/iostests/RCTInstanceTests.mm | 30 ++++++++++++++----- .../platform/ios/ReactCommon/RCTInstance.mm | 5 ++-- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm index 11c1ab2afd08..1f492701e7d8 100644 --- a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm +++ b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm @@ -18,10 +18,6 @@ using namespace facebook::react; -@interface RCTInstance (RCTInstanceTests) -- (void)handleBundleLoadingError:(NSError *)error; -@end - @interface FakeEagerModule : NSObject @property (class, readonly) NSCountedSet *initCounts; @property (class, nullable) void (^onInit)(void); @@ -225,16 +221,36 @@ - (void)testInitializesDevSettingsAfterBundleLoadFailure XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 0u); - id instanceMock = OCMPartialMock(instance); - OCMStub([instanceMock handleBundleLoadingError:[OCMArg any]]); loadComplete([NSError errorWithDomain:@"RCTInstanceTests" code:1 userInfo:nil], nil); XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 1u); - [instanceMock stopMocking]; [instance invalidate]; } +- (void)testDoesNotInitializeDevSettingsAfterInvalidation +{ + OCMStub([_mockDelegate unstableModulesRequiringMainQueueSetup]).andReturn(@[]); + + XCTestExpectation *loadStarted = [self expectationWithDescription:@"bundle load started"]; + __block RCTSourceLoadBlock loadComplete; + OCMStub([_mockDelegate loadBundleAtURL:[OCMArg any] onProgress:[OCMArg any] onComplete:[OCMArg any]]) + .andDo(^(NSInvocation *invocation) { + __unsafe_unretained RCTSourceLoadBlock completion; + [invocation getArgument:&completion atIndex:4]; + loadComplete = [completion copy]; + [loadStarted fulfill]; + }); + + RCTInstance *instance = [self makeInstance]; + [self waitForExpectations:@[ loadStarted ] timeout:5.0]; + [instance invalidate]; + + loadComplete([NSError errorWithDomain:@"RCTInstanceTests" code:1 userInfo:nil], nil); + + XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 0u); +} + - (void)testBundleLoadAwaitsMainQueueModuleSetup { OCMStub([_mockDelegate unstableModulesRequiringMainQueueSetup]).andReturn(@[ FakeEagerModule.moduleName ]); diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index dffdce549098..5b85b5e97255 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -521,6 +521,7 @@ - (void)handleBundleLoadingError:(NSError *)error return; } + [_turboModuleManager moduleForName:"DevSettings"]; RCTRedBox *redBox = [_turboModuleManager moduleForName:"RedBox"]; RCTExecuteOnMainQueue(^{ @@ -563,12 +564,12 @@ - (void)_loadJSBundle:(NSURL *)sourceURL return; } - RCTDevSettings *const devSettings = - (RCTDevSettings *)[strongSelf->_turboModuleManager moduleForName:"DevSettings"]; if (error) { [strongSelf handleBundleLoadingError:error]; return; } + RCTDevSettings *const devSettings = + (RCTDevSettings *)[strongSelf->_turboModuleManager moduleForName:"DevSettings"]; [strongSelf _loadScriptFromSource:source]; // Set up hot module reloading in Dev only. [strongSelf->_performanceLogger markStopForTag:RCTPLScriptDownload]; From 77957ee9553e552c064742eaebfbbbdcb3a619a6 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 4 Sep 2026 19:02:27 -0400 Subject: [PATCH 5/6] test: make invalidation coverage deterministic --- .../react/runtime/iostests/RCTInstanceTests.mm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm index 1f492701e7d8..e16dde5b0b6d 100644 --- a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm +++ b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm @@ -13,6 +13,7 @@ #import #import #import +#import #import #import @@ -244,11 +245,22 @@ - (void)testDoesNotInitializeDevSettingsAfterInvalidation RCTInstance *instance = [self makeInstance]; [self waitForExpectations:@[ loadStarted ] timeout:5.0]; + + dispatch_semaphore_t jsThreadBlocked = dispatch_semaphore_create(0); + dispatch_semaphore_t releaseJsThread = dispatch_semaphore_create(0); + RCTJSThreadManager *jsThreadManager = [instance valueForKey:@"jsThreadManager"]; + [jsThreadManager dispatchToJSThread:^{ + dispatch_semaphore_signal(jsThreadBlocked); + dispatch_semaphore_wait(releaseJsThread, DISPATCH_TIME_FOREVER); + }]; + XCTAssertEqual(dispatch_semaphore_wait(jsThreadBlocked, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)), 0); + [instance invalidate]; loadComplete([NSError errorWithDomain:@"RCTInstanceTests" code:1 userInfo:nil], nil); XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 0u); + dispatch_semaphore_signal(releaseJsThread); } - (void)testBundleLoadAwaitsMainQueueModuleSetup From 073e56e3c5732ccf100afb3a13df15bebeee6a7f Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Fri, 4 Sep 2026 20:27:13 -0400 Subject: [PATCH 6/6] refactor: initialize dev settings before bundle request --- .../runtime/iostests/RCTInstanceTests.mm | 95 +------------------ .../platform/ios/ReactCommon/RCTInstance.mm | 4 +- 2 files changed, 5 insertions(+), 94 deletions(-) diff --git a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm index e16dde5b0b6d..b1d1b16b45ee 100644 --- a/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm +++ b/packages/react-native/ReactCommon/react/runtime/iostests/RCTInstanceTests.mm @@ -13,7 +13,6 @@ #import #import #import -#import #import #import @@ -87,32 +86,6 @@ - (instancetype)init @end -@interface FakeDevSettings : FakeEagerModule -- (void)setupHMRClientWithBundleURL:(NSURL *)bundleURL; -@end - -@implementation FakeDevSettings - -static NSString *const FakeDevSettingsInitialize = @"DevSettings.initialize"; - -+ (NSString *)moduleName -{ - return @"DevSettings"; -} - -- (void)initialize -{ - @synchronized([FakeDevSettings initCounts]) { - [[FakeDevSettings initCounts] addObject:FakeDevSettingsInitialize]; - } -} - -- (void)setupHMRClientWithBundleURL:(__unused NSURL *)bundleURL -{ -} - -@end - @interface RCTInstanceTests : XCTestCase @end @@ -135,12 +108,8 @@ - (void)setUp OCMStub([_mockTMMDelegate getModuleClassFromName:nullptr]).ignoringNonObjectArgs().andDo(^(NSInvocation *invocation) { const char *requestedName = nullptr; [invocation getArgument:&requestedName atIndex:2]; - Class result = Nil; - if (requestedName != nullptr && strcmp(requestedName, "FakeEagerModule") == 0) { - result = [FakeEagerModule class]; - } else if (requestedName != nullptr && strcmp(requestedName, "DevSettings") == 0) { - result = [FakeDevSettings class]; - } + Class result = + (requestedName != nullptr && strcmp(requestedName, "FakeEagerModule") == 0) ? [FakeEagerModule class] : Nil; [invocation setReturnValue:&result]; }); } @@ -203,66 +172,6 @@ - (void)testInstantiatesRequestedModuleOnMainQueue [instance invalidate]; } -- (void)testInitializesDevSettingsAfterBundleLoadFailure -{ - OCMStub([_mockDelegate unstableModulesRequiringMainQueueSetup]).andReturn(@[]); - - XCTestExpectation *loadStarted = [self expectationWithDescription:@"bundle load started"]; - __block RCTSourceLoadBlock loadComplete; - OCMStub([_mockDelegate loadBundleAtURL:[OCMArg any] onProgress:[OCMArg any] onComplete:[OCMArg any]]) - .andDo(^(NSInvocation *invocation) { - __unsafe_unretained RCTSourceLoadBlock completion; - [invocation getArgument:&completion atIndex:4]; - loadComplete = [completion copy]; - [loadStarted fulfill]; - }); - - RCTInstance *instance = [self makeInstance]; - [self waitForExpectations:@[ loadStarted ] timeout:5.0]; - - XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 0u); - - loadComplete([NSError errorWithDomain:@"RCTInstanceTests" code:1 userInfo:nil], nil); - - XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 1u); - - [instance invalidate]; -} - -- (void)testDoesNotInitializeDevSettingsAfterInvalidation -{ - OCMStub([_mockDelegate unstableModulesRequiringMainQueueSetup]).andReturn(@[]); - - XCTestExpectation *loadStarted = [self expectationWithDescription:@"bundle load started"]; - __block RCTSourceLoadBlock loadComplete; - OCMStub([_mockDelegate loadBundleAtURL:[OCMArg any] onProgress:[OCMArg any] onComplete:[OCMArg any]]) - .andDo(^(NSInvocation *invocation) { - __unsafe_unretained RCTSourceLoadBlock completion; - [invocation getArgument:&completion atIndex:4]; - loadComplete = [completion copy]; - [loadStarted fulfill]; - }); - - RCTInstance *instance = [self makeInstance]; - [self waitForExpectations:@[ loadStarted ] timeout:5.0]; - - dispatch_semaphore_t jsThreadBlocked = dispatch_semaphore_create(0); - dispatch_semaphore_t releaseJsThread = dispatch_semaphore_create(0); - RCTJSThreadManager *jsThreadManager = [instance valueForKey:@"jsThreadManager"]; - [jsThreadManager dispatchToJSThread:^{ - dispatch_semaphore_signal(jsThreadBlocked); - dispatch_semaphore_wait(releaseJsThread, DISPATCH_TIME_FOREVER); - }]; - XCTAssertEqual(dispatch_semaphore_wait(jsThreadBlocked, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)), 0); - - [instance invalidate]; - - loadComplete([NSError errorWithDomain:@"RCTInstanceTests" code:1 userInfo:nil], nil); - - XCTAssertEqual([FakeDevSettings.initCounts countForObject:FakeDevSettingsInitialize], 0u); - dispatch_semaphore_signal(releaseJsThread); -} - - (void)testBundleLoadAwaitsMainQueueModuleSetup { OCMStub([_mockDelegate unstableModulesRequiringMainQueueSetup]).andReturn(@[ FakeEagerModule.moduleName ]); diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index 5b85b5e97255..c96725c04643 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -521,7 +521,6 @@ - (void)handleBundleLoadingError:(NSError *)error return; } - [_turboModuleManager moduleForName:"DevSettings"]; RCTRedBox *redBox = [_turboModuleManager moduleForName:"RedBox"]; RCTExecuteOnMainQueue(^{ @@ -536,6 +535,9 @@ - (void)handleBundleLoadingError:(NSError *)error - (void)_loadJSBundle:(NSURL *)sourceURL { + // Initialize DevSettings before the request so Metro can reload after an initial bundle failure. + [_turboModuleManager moduleForName:"DevSettings"]; + #if RCT_DEV_MENU && __has_include() { id loadingView =