From b416ca189469ebcf043d0f238492c99a543e4cf7 Mon Sep 17 00:00:00 2001 From: Peter Abbondanzo Date: Sun, 11 Oct 2026 13:25:32 -0700 Subject: [PATCH] Process Settings notifications off the posting thread (#58978) Summary: `SettingsManager` cleans the entire user-defaults dictionary inline whenever a defaults change notification arrives. A write on the main thread therefore performs unrelated, potentially expensive work before returning. Put the asynchronous update path behind a default-off runtime gate. With the gate off, preserve synchronous snapshot cleaning and event delivery, every intermediate update, the default module queue, and the existing lifecycle registration. With the gate on, process notifications on a dedicated serial background method queue. Coalesce pending notifications to the latest defaults state, while allowing a notification during a snapshot to schedule a trailing update. Keep JavaScript mutations on the same queue and suppress their notifications at posting time. Drop queued work after invalidation without blocking teardown or retaining the module. The `settingsUpdated` event name and full JSON-cleaned payload remain unchanged. Enabled-mode rapid external changes may be reported as one latest-state update rather than separate intermediate transitions. Synchronous constants export is unchanged. Declare the Settings pod's feature-flags dependency so dynamic-framework builds can link the gate. Changelog: [iOS][Fixed] - Add an opt-in asynchronous Settings update path to avoid blocking user-defaults writers Differential Revision: D124504338 --- .../Libraries/Settings/RCTSettingsManager.mm | 78 ++- .../Settings/React-RCTSettings.podspec | 1 + .../Tests/Settings/RCTSettingsManagerTests.mm | 454 ++++++++++++++++++ .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 +- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 170 ++++--- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 11 + .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 23 files changed, 740 insertions(+), 100 deletions(-) create mode 100644 packages/react-native/React/Tests/Settings/RCTSettingsManagerTests.mm diff --git a/packages/react-native/Libraries/Settings/RCTSettingsManager.mm b/packages/react-native/Libraries/Settings/RCTSettingsManager.mm index af0bd521f02e..95308a9713d9 100644 --- a/packages/react-native/Libraries/Settings/RCTSettingsManager.mm +++ b/packages/react-native/Libraries/Settings/RCTSettingsManager.mm @@ -11,16 +11,24 @@ #import #import #import +#import #import +#include +#include + #import "RCTSettingsPlugins.h" -@interface RCTSettingsManager () +@interface RCTSettingsManager () @end @implementation RCTSettingsManager { - BOOL _ignoringUpdates; + std::atomic _ignoringUpdates; + std::atomic _updateScheduled; + std::atomic _invalidated; + bool _useAsyncUpdates; NSUserDefaults *_defaults; + dispatch_queue_t _methodQueue; } @synthesize moduleRegistry = _moduleRegistry; @@ -40,7 +48,14 @@ - (instancetype)init - (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults { if ((self = [super init]) != nullptr) { + _ignoringUpdates = false; + _updateScheduled = false; + _invalidated = false; _defaults = defaults; + _useAsyncUpdates = facebook::react::ReactNativeFeatureFlags::enableAsyncSettingsManagerUpdatesIOS(); + if (_useAsyncUpdates) { + _methodQueue = dispatch_queue_create("com.facebook.react.SettingsManager", DISPATCH_QUEUE_SERIAL); + } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) @@ -61,18 +76,71 @@ - (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults {.settings = RCTJSONClean([_defaults dictionaryRepresentation])}); } +- (dispatch_queue_t)methodQueue +{ + return _methodQueue; +} + +- (BOOL)respondsToSelector:(SEL)selector +{ + if (selector == @selector(invalidate) && !_useAsyncUpdates) { + return NO; + } + return [super respondsToSelector:selector]; +} + +- (void)invalidate +{ + if (!_useAsyncUpdates) { + return; + } + _invalidated = true; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSUserDefaultsDidChangeNotification object:_defaults]; +} + - (void)userDefaultsDidChange:(NSNotification *)note { if (_ignoringUpdates) { return; } + if (!_useAsyncUpdates) { + NSDictionary *settings = RCTJSONClean([_defaults dictionaryRepresentation]); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"settingsUpdated" body:settings]; +#pragma clang diagnostic pop + return; + } + + if (_invalidated || _updateScheduled.exchange(true)) { + return; + } + + __weak RCTSettingsManager *weakSelf = self; + dispatch_async(_methodQueue, ^{ + RCTSettingsManager *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + + // Clear before reading so a notification during the snapshot schedules a trailing update. + strongSelf->_updateScheduled = false; + if (strongSelf->_invalidated) { + return; + } + + NSDictionary *settings = RCTJSONClean([strongSelf->_defaults dictionaryRepresentation]); + if (strongSelf->_invalidated) { + return; + } + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - [[_moduleRegistry moduleForName:"EventDispatcher"] - sendDeviceEventWithName:@"settingsUpdated" - body:RCTJSONClean([_defaults dictionaryRepresentation])]; + [[strongSelf->_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"settingsUpdated" + body:settings]; #pragma clang diagnostic pop + }); } /** diff --git a/packages/react-native/Libraries/Settings/React-RCTSettings.podspec b/packages/react-native/Libraries/Settings/React-RCTSettings.podspec index c721ace08e5d..f77af0581ac7 100644 --- a/packages/react-native/Libraries/Settings/React-RCTSettings.podspec +++ b/packages/react-native/Libraries/Settings/React-RCTSettings.podspec @@ -44,6 +44,7 @@ Pod::Spec.new do |s| s.dependency "React-jsi" s.dependency "React-Core/RCTSettingsHeaders" + add_dependency(s, "React-featureflags") add_dependency(s, "React-RCTFBReactNativeSpec") add_dependency(s, "ReactCommon", :subspec => "turbomodule/core", :additional_framework_paths => ["react/nativemodule/core"]) add_dependency(s, "React-NativeModulesApple", :additional_framework_paths => ["build/generated/ios"]) diff --git a/packages/react-native/React/Tests/Settings/RCTSettingsManagerTests.mm b/packages/react-native/React/Tests/Settings/RCTSettingsManagerTests.mm new file mode 100644 index 000000000000..a74861367663 --- /dev/null +++ b/packages/react-native/React/Tests/Settings/RCTSettingsManagerTests.mm @@ -0,0 +1,454 @@ +/* + * 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 +#import +#import +#import +#import + +#include +#include +#include + +@interface RCTSettingsManager () +@end + +@interface RCTSettingsTestDefaults : NSUserDefaults +@property (nonatomic, strong) NSMutableDictionary *values; +@property (atomic, assign) BOOL readOnMainThread; +@property (atomic, assign) NSTimeInterval snapshotDelay; +@property (atomic, assign) NSUInteger snapshotCount; +@property (nonatomic, copy) void (^onSnapshot)(NSDictionary *); +@end + +@implementation RCTSettingsTestDefaults + +- (instancetype)init +{ + if ((self = [super init])) { + _values = [NSMutableDictionary new]; + } + return self; +} + +- (NSDictionary *)dictionaryRepresentation +{ + self.readOnMainThread = [NSThread isMainThread]; + [NSThread sleepForTimeInterval:self.snapshotDelay]; + NSDictionary *snapshot; + @synchronized(self) { + self.snapshotCount++; + snapshot = [self.values copy]; + } + if (self.onSnapshot != nil) { + self.onSnapshot(snapshot); + } + return snapshot; +} + +- (void)setObject:(id)value forKey:(NSString *)key +{ + @synchronized(self) { + self.values[key] = value; + } + [[NSNotificationCenter defaultCenter] postNotificationName:NSUserDefaultsDidChangeNotification object:self]; +} + +- (void)removeObjectForKey:(NSString *)key +{ + @synchronized(self) { + [self.values removeObjectForKey:key]; + } + [[NSNotificationCenter defaultCenter] postNotificationName:NSUserDefaultsDidChangeNotification object:self]; +} + +- (BOOL)synchronize +{ + return YES; +} + +@end + +@interface RCTSettingsTestEventDispatcher : NSObject +@property (nonatomic, copy) void (^onEvent)(NSString *, NSDictionary *); +@end + +@implementation RCTSettingsTestEventDispatcher + +- (void)sendDeviceEventWithName:(NSString *)name body:(NSDictionary *)body +{ + self.onEvent(name, body); +} + +@end + +@interface RCTSettingsTestModuleRegistry : RCTModuleRegistry +@property (nonatomic, strong) RCTSettingsTestEventDispatcher *dispatcher; +@end + +@implementation RCTSettingsTestModuleRegistry + +- (id)moduleForName:(const char *)moduleName +{ + return self.dispatcher; +} + +@end + +@interface RCTSettingsManagerTests : XCTestCase +@end + +@implementation RCTSettingsManagerTests { + RCTSettingsTestDefaults *_defaults; + RCTSettingsManager *_manager; + RCTSettingsTestModuleRegistry *_registry; +} + +- (void)setUp +{ + [super setUp]; + class AsyncSettingsFlags : public facebook::react::ReactNativeFeatureFlagsDefaults { + public: + bool enableAsyncSettingsManagerUpdatesIOS() override + { + return true; + } + }; + facebook::react::ReactNativeFeatureFlags::dangerouslyReset(); + facebook::react::ReactNativeFeatureFlags::override(std::make_unique()); + _defaults = [RCTSettingsTestDefaults new]; + _manager = [[RCTSettingsManager alloc] initWithUserDefaults:_defaults]; + _registry = [RCTSettingsTestModuleRegistry new]; + _registry.dispatcher = [RCTSettingsTestEventDispatcher new]; + _manager.moduleRegistry = _registry; + XCTAssertTrue([_manager respondsToSelector:@selector(invalidate)]); +} + +- (void)tearDown +{ + [_manager invalidate]; + if (_manager.methodQueue != nil) { + dispatch_sync( + _manager.methodQueue, + ^{ + }); + } + _manager = nil; + facebook::react::ReactNativeFeatureFlags::dangerouslyReset(); + [super tearDown]; +} + +- (void)useDefaultFeatureFlags +{ + [_manager invalidate]; + _manager = nil; + facebook::react::ReactNativeFeatureFlags::dangerouslyReset(); + _manager = [[RCTSettingsManager alloc] initWithUserDefaults:_defaults]; + _manager.moduleRegistry = _registry; +} + +- (void)testFlagDisabledPreservesSynchronousUpdates +{ + [self useDefaultFeatureFlags]; + XCTAssertFalse(facebook::react::ReactNativeFeatureFlags::enableAsyncSettingsManagerUpdatesIOS()); + _defaults.snapshotDelay = 0.2; + _defaults.values[@"date"] = [NSDate dateWithTimeIntervalSince1970:0]; + __block BOOL delivered = NO; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertEqualObjects(name, @"settingsUpdated"); + XCTAssertEqualObjects(body[@"token"], @"new-token"); + XCTAssertEqualObjects(body[@"date"], [NSNull null]); + delivered = YES; + }; + void (^write)(void) = ^{ + NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate]; + [self->_defaults setObject:@"new-token" forKey:@"token"]; + XCTAssertTrue(delivered); + XCTAssertGreaterThanOrEqual([NSDate timeIntervalSinceReferenceDate] - start, 0.15); + }; + if ([NSThread isMainThread]) { + write(); + } else { + dispatch_sync(dispatch_get_main_queue(), write); + } + XCTAssertTrue(_defaults.readOnMainThread); + XCTAssertEqual(_defaults.snapshotCount, 1u); +} + +- (void)testFlagDisabledPreservesEveryIntermediateUpdate +{ + [self useDefaultFeatureFlags]; + NSMutableArray *updates = [NSMutableArray new]; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + [updates addObject:body[@"external"] ?: [NSNull null]]; + }; + [_defaults setObject:@"A" forKey:@"external"]; + [_defaults setObject:@"B" forKey:@"external"]; + [_defaults setObject:@"A" forKey:@"external"]; + XCTAssertEqualObjects(updates, (@[ @"A", @"B", @"A" ])); + XCTAssertEqual(_defaults.snapshotCount, 3u); +} + +- (void)testFlagDisabledPreservesDefaultMethodQueue +{ + [self useDefaultFeatureFlags]; + XCTAssertFalse([_manager respondsToSelector:@selector(invalidate)]); + XCTAssertNil(_manager.methodQueue); + dispatch_queue_t queue = dispatch_queue_create("com.facebook.react.SettingsManagerTests", DISPATCH_QUEUE_SERIAL); + [_manager setValue:queue forKey:@"methodQueue"]; + XCTAssertEqual(_manager.methodQueue, queue); +} + +- (void)testFlagDisabledDoesNotEchoJavaScriptWrites +{ + [self useDefaultFeatureFlags]; + __block NSUInteger eventCount = 0; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + eventCount++; + }; + [_manager setValues:@{@"local" : @"value"}]; + XCTAssertEqualObjects(_defaults.values[@"local"], @"value"); + [_manager deleteValues:@[ @"local" ]]; + XCTAssertNil(_defaults.values[@"local"]); + XCTAssertEqual(eventCount, 0u); + XCTAssertEqual(_defaults.snapshotCount, 0u); +} + +- (void)testFlagDisabledPreservesNotificationsAfterInvalidation +{ + [self useDefaultFeatureFlags]; + __block NSUInteger eventCount = 0; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + eventCount++; + }; + [_manager invalidate]; + [_defaults setObject:@"value" forKey:@"external"]; + XCTAssertEqual(eventCount, 1u); + XCTAssertEqual(_defaults.snapshotCount, 1u); +} + +- (void)testNotificationDoesNotBlockPostingThread +{ + _defaults.snapshotDelay = 0.2; + XCTestExpectation *event = [self expectationWithDescription:@"settingsUpdated"]; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + XCTAssertEqualObjects(name, @"settingsUpdated"); + XCTAssertEqualObjects(body[@"token"], @"new-token"); + [event fulfill]; + }; + + __block NSTimeInterval duration; + void (^write)(void) = ^{ + NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate]; + [self->_defaults setObject:@"new-token" forKey:@"token"]; + duration = [NSDate timeIntervalSinceReferenceDate] - start; + }; + if ([NSThread isMainThread]) { + write(); + } else { + dispatch_sync(dispatch_get_main_queue(), write); + } + [self waitForExpectations:@[ event ] timeout:5]; + XCTAssertLessThan(duration, 0.1); + XCTAssertFalse(_defaults.readOnMainThread); +} + +- (void)testLargeNestedSettingsAreCleanedOffMainThread +{ + NSMutableDictionary *entries = [NSMutableDictionary new]; + for (NSUInteger index = 0; index < 5000; index++) { + entries[[NSString stringWithFormat:@"entry-%lu", (unsigned long)index]] = + @{@"nested" : @{@"string" : @"value", @"number" : @42, @"date" : [NSDate dateWithTimeIntervalSince1970:0]}}; + } + _defaults.values[@"config"] = entries; + XCTestExpectation *event = [self expectationWithDescription:@"cleaned settings"]; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + XCTAssertEqual([body[@"config"] count], 5000u); + XCTAssertEqualObjects(body[@"config"][@"entry-0"][@"nested"][@"date"], [NSNull null]); + XCTAssertEqualObjects(body[@"config"][@"entry-4999"][@"nested"][@"number"], @42); + [event fulfill]; + }; + void (^write)(void) = ^{ + [self->_defaults setObject:@"new-token" forKey:@"token"]; + }; + if ([NSThread isMainThread]) { + write(); + } else { + dispatch_sync(dispatch_get_main_queue(), write); + } + [self waitForExpectations:@[ event ] timeout:5]; + XCTAssertFalse(_defaults.readOnMainThread); +} + +- (void)testJavaScriptWritesDoNotEchoUpdates +{ + XCTestExpectation *event = [self expectationWithDescription:@"no settingsUpdated echo"]; + event.inverted = YES; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + [event fulfill]; + }; + dispatch_sync(_manager.methodQueue, ^{ + [self->_manager setValues:@{@"local" : @"value"}]; + XCTAssertEqualObjects(self->_defaults.values[@"local"], @"value"); + [self->_manager deleteValues:@[ @"local" ]]; + XCTAssertNil(self->_defaults.values[@"local"]); + }); + XCTAssertEqual(_defaults.snapshotCount, 0u); + [self waitForExpectations:@[ event ] timeout:0.3]; +} + +- (void)testBackgroundNotificationStillDeliversSettings +{ + XCTestExpectation *event = [self expectationWithDescription:@"background settings"]; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + XCTAssertEqualObjects(body[@"background"], @YES); + [event fulfill]; + }; + dispatch_queue_t queue = dispatch_queue_create("com.facebook.react.SettingsManagerTests", DISPATCH_QUEUE_SERIAL); + dispatch_async(queue, ^{ + [self->_defaults setObject:@YES forKey:@"background"]; + }); + [self waitForExpectations:@[ event ] timeout:5]; +} + +- (void)testMainQueueRemainsResponsiveDuringSnapshot +{ + dispatch_semaphore_t releaseSnapshot = dispatch_semaphore_create(0); + XCTestExpectation *snapshot = [self expectationWithDescription:@"snapshot entered"]; + XCTestExpectation *heartbeat = [self expectationWithDescription:@"main queue heartbeat"]; + XCTestExpectation *event = [self expectationWithDescription:@"settings delivered"]; + _defaults.onSnapshot = ^(NSDictionary *body) { + XCTAssertFalse([NSThread isMainThread]); + [snapshot fulfill]; + XCTAssertEqual(dispatch_semaphore_wait(releaseSnapshot, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)), 0); + }; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + XCTAssertFalse([NSThread isMainThread]); + [event fulfill]; + }; + dispatch_async(dispatch_get_main_queue(), ^{ + [self->_defaults setObject:@"value" forKey:@"external"]; + dispatch_async(dispatch_get_main_queue(), ^{ + [heartbeat fulfill]; + }); + }); + [self waitForExpectations:@[ snapshot, heartbeat ] timeout:3]; + dispatch_semaphore_signal(releaseSnapshot); + [self waitForExpectations:@[ event ] timeout:3]; +} + +- (void)testBurstNotificationsCoalesceToLatestSettings +{ + dispatch_semaphore_t releaseQueue = dispatch_semaphore_create(0); + XCTestExpectation *blocked = [self expectationWithDescription:@"queue blocked"]; + dispatch_async(_manager.methodQueue, ^{ + [blocked fulfill]; + XCTAssertEqual(dispatch_semaphore_wait(releaseQueue, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)), 0); + }); + [self waitForExpectations:@[ blocked ] timeout:3]; + + XCTestExpectation *event = [self expectationWithDescription:@"latest settings"]; + __block NSUInteger eventCount = 0; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + eventCount++; + XCTAssertEqualObjects(body[@"external"], @99); + [event fulfill]; + }; + for (NSUInteger index = 0; index < 100; index++) { + [_defaults setObject:@(index) forKey:@"external"]; + } + dispatch_semaphore_signal(releaseQueue); + [self waitForExpectations:@[ event ] timeout:3]; + dispatch_sync( + _manager.methodQueue, + ^{ + }); + XCTAssertEqual(eventCount, 1u); + XCTAssertEqual(_defaults.snapshotCount, 1u); +} + +- (void)testNotificationDuringSnapshotSchedulesTrailingUpdate +{ + dispatch_semaphore_t releaseSnapshot = dispatch_semaphore_create(0); + XCTestExpectation *snapshot = [self expectationWithDescription:@"first snapshot captured"]; + XCTestExpectation *events = [self expectationWithDescription:@"ordered updates"]; + events.expectedFulfillmentCount = 2; + __block NSUInteger snapshotCount = 0; + _defaults.onSnapshot = ^(NSDictionary *body) { + if (++snapshotCount == 1) { + [snapshot fulfill]; + XCTAssertEqual(dispatch_semaphore_wait(releaseSnapshot, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)), 0); + } + }; + __block NSUInteger eventCount = 0; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + XCTAssertEqualObjects(body[@"external"], @(++eventCount)); + [events fulfill]; + }; + [_defaults setObject:@1 forKey:@"external"]; + [self waitForExpectations:@[ snapshot ] timeout:3]; + [_defaults setObject:@2 forKey:@"external"]; + dispatch_semaphore_signal(releaseSnapshot); + [self waitForExpectations:@[ events ] timeout:3]; + dispatch_sync( + _manager.methodQueue, + ^{ + }); + XCTAssertEqual(eventCount, 2u); + XCTAssertEqual(_defaults.snapshotCount, 2u); +} + +- (void)testInvalidationDropsPendingUpdate +{ + dispatch_semaphore_t releaseQueue = dispatch_semaphore_create(0); + XCTestExpectation *blocked = [self expectationWithDescription:@"queue blocked"]; + dispatch_async(_manager.methodQueue, ^{ + [blocked fulfill]; + XCTAssertEqual(dispatch_semaphore_wait(releaseQueue, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)), 0); + }); + [self waitForExpectations:@[ blocked ] timeout:3]; + __block NSUInteger eventCount = 0; + _registry.dispatcher.onEvent = ^(NSString *name, NSDictionary *body) { + eventCount++; + }; + [_defaults setObject:@1 forKey:@"external"]; + [_manager invalidate]; + [_defaults setObject:@2 forKey:@"external"]; + dispatch_semaphore_signal(releaseQueue); + dispatch_sync( + _manager.methodQueue, + ^{ + }); + XCTAssertEqual(eventCount, 0u); + XCTAssertEqual(_defaults.snapshotCount, 0u); +} + +- (void)testPendingUpdateDoesNotRetainManager +{ + dispatch_queue_t queue = _manager.methodQueue; + dispatch_semaphore_t releaseQueue = dispatch_semaphore_create(0); + XCTestExpectation *blocked = [self expectationWithDescription:@"queue blocked"]; + dispatch_async(queue, ^{ + [blocked fulfill]; + XCTAssertEqual(dispatch_semaphore_wait(releaseQueue, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)), 0); + }); + [self waitForExpectations:@[ blocked ] timeout:3]; + [_defaults setObject:@1 forKey:@"external"]; + __weak RCTSettingsManager *weakManager = _manager; + _manager = nil; + XCTAssertNil(weakManager); + dispatch_semaphore_signal(releaseQueue); + dispatch_sync( + queue, + ^{ + }); + XCTAssertEqual(_defaults.snapshotCount, 0u); +} + +@end diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 07ef963a6612..df972590cb62 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<72ad53973a7de132705b9ec1a72aac08>> + * @generated SignedSource<<419f5268e3cb03e505af8fe32154fb42>> */ /** @@ -126,6 +126,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableAsyncDiskCacheCheckInMultiSourceImageAndroid(): Boolean = accessor.enableAsyncDiskCacheCheckInMultiSourceImageAndroid() + /** + * Process SettingsManager defaults notifications on a dedicated serial queue on iOS. + */ + @JvmStatic + public fun enableAsyncSettingsManagerUpdatesIOS(): Boolean = accessor.enableAsyncSettingsManagerUpdatesIOS() + /** * Feature flag to enable the new bridgeless architecture. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 31815488c19a..5702facd1dd8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<60051ebbc387ff06db855ed661340217>> + * @generated SignedSource<> */ /** @@ -36,6 +36,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableAndroidAutoOffscreenCompositingForElevationCache: Boolean? = null private var enableAndroidTextMeasurementOptimizationsCache: Boolean? = null private var enableAsyncDiskCacheCheckInMultiSourceImageAndroidCache: Boolean? = null + private var enableAsyncSettingsManagerUpdatesIOSCache: Boolean? = null private var enableBridgelessArchitectureCache: Boolean? = null private var enableCppPropsIteratorSetterCache: Boolean? = null private var enableCustomFocusSearchOnClippedElementsAndroidCache: Boolean? = null @@ -256,6 +257,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableAsyncSettingsManagerUpdatesIOS(): Boolean { + var cached = enableAsyncSettingsManagerUpdatesIOSCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableAsyncSettingsManagerUpdatesIOS() + enableAsyncSettingsManagerUpdatesIOSCache = cached + } + return cached + } + override fun enableBridgelessArchitecture(): Boolean { var cached = enableBridgelessArchitectureCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 88728162c941..55a263e2061a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<9ed55c4c14d640c0fb2bd6c8fb7ef557>> + * @generated SignedSource<<9d8d6d33a151f5956fae23edc44108a5>> */ /** @@ -60,6 +60,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableAsyncDiskCacheCheckInMultiSourceImageAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableAsyncSettingsManagerUpdatesIOS(): Boolean + @DoNotStrip @JvmStatic public external fun enableBridgelessArchitecture(): Boolean @DoNotStrip @JvmStatic public external fun enableCppPropsIteratorSetter(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index b80430013c55..bfb6d1439624 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2170527111c8b3c34c213c24a9948931>> + * @generated SignedSource<<5f7b160241f08f962a0358ce69d914ef>> */ /** @@ -55,6 +55,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableAsyncDiskCacheCheckInMultiSourceImageAndroid(): Boolean = false + override fun enableAsyncSettingsManagerUpdatesIOS(): Boolean = false + override fun enableBridgelessArchitecture(): Boolean = true override fun enableCppPropsIteratorSetter(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index ddef18a5642f..87083e886766 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<1ebd0bb8e98c1b31518c8eba05be919c>> */ /** @@ -40,6 +40,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableAndroidAutoOffscreenCompositingForElevationCache: Boolean? = null private var enableAndroidTextMeasurementOptimizationsCache: Boolean? = null private var enableAsyncDiskCacheCheckInMultiSourceImageAndroidCache: Boolean? = null + private var enableAsyncSettingsManagerUpdatesIOSCache: Boolean? = null private var enableBridgelessArchitectureCache: Boolean? = null private var enableCppPropsIteratorSetterCache: Boolean? = null private var enableCustomFocusSearchOnClippedElementsAndroidCache: Boolean? = null @@ -276,6 +277,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableAsyncSettingsManagerUpdatesIOS(): Boolean { + var cached = enableAsyncSettingsManagerUpdatesIOSCache + if (cached == null) { + cached = currentProvider.enableAsyncSettingsManagerUpdatesIOS() + accessedFeatureFlags.add("enableAsyncSettingsManagerUpdatesIOS") + enableAsyncSettingsManagerUpdatesIOSCache = cached + } + return cached + } + override fun enableBridgelessArchitecture(): Boolean { var cached = enableBridgelessArchitectureCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 399cbb4f0f91..8ff722b6876a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<256b97731c59551616a8e0cbc777f3fa>> + * @generated SignedSource<<02ba59febfe006256c838500cc9bbf61>> */ /** @@ -55,6 +55,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableAsyncDiskCacheCheckInMultiSourceImageAndroid(): Boolean + @DoNotStrip public fun enableAsyncSettingsManagerUpdatesIOS(): Boolean + @DoNotStrip public fun enableBridgelessArchitecture(): Boolean @DoNotStrip public fun enableCppPropsIteratorSetter(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 1c7c222ebe99..33e78e125605 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2b4f896552dd54c3ec858ca788ab4254>> + * @generated SignedSource<> */ /** @@ -135,6 +135,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableAsyncSettingsManagerUpdatesIOS() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableAsyncSettingsManagerUpdatesIOS"); + return method(javaProvider_); + } + bool enableBridgelessArchitecture() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableBridgelessArchitecture"); @@ -669,6 +675,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableAsyncDiskCacheCheckInMultiSourceI return ReactNativeFeatureFlags::enableAsyncDiskCacheCheckInMultiSourceImageAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::enableAsyncSettingsManagerUpdatesIOS( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableAsyncSettingsManagerUpdatesIOS(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableBridgelessArchitecture( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableBridgelessArchitecture(); @@ -1123,6 +1134,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableAsyncDiskCacheCheckInMultiSourceImageAndroid", JReactNativeFeatureFlagsCxxInterop::enableAsyncDiskCacheCheckInMultiSourceImageAndroid), + makeNativeMethod( + "enableAsyncSettingsManagerUpdatesIOS", + JReactNativeFeatureFlagsCxxInterop::enableAsyncSettingsManagerUpdatesIOS), makeNativeMethod( "enableBridgelessArchitecture", JReactNativeFeatureFlagsCxxInterop::enableBridgelessArchitecture), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index 1cc1567097c5..1419e6551f2d 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<228fff4f0ef0e530bf50203050adb9b0>> + * @generated SignedSource<<9916203529aef50a87192d70176bf624>> */ /** @@ -78,6 +78,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableAsyncDiskCacheCheckInMultiSourceImageAndroid( facebook::jni::alias_ref); + static bool enableAsyncSettingsManagerUpdatesIOS( + facebook::jni::alias_ref); + static bool enableBridgelessArchitecture( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index a3c89045c15e..c31b0b377592 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2c4c3259015438d07cc137e9288db04a>> + * @generated SignedSource<> */ /** @@ -90,6 +90,10 @@ bool ReactNativeFeatureFlags::enableAsyncDiskCacheCheckInMultiSourceImageAndroid return getAccessor().enableAsyncDiskCacheCheckInMultiSourceImageAndroid(); } +bool ReactNativeFeatureFlags::enableAsyncSettingsManagerUpdatesIOS() { + return getAccessor().enableAsyncSettingsManagerUpdatesIOS(); +} + bool ReactNativeFeatureFlags::enableBridgelessArchitecture() { return getAccessor().enableBridgelessArchitecture(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 937527653893..520ba940bdad 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<73ae8b45c0961bc60eeb804d4d40bd07>> + * @generated SignedSource<<7b78cfdbd41f5405c3222a23ab1dd5b8>> */ /** @@ -121,6 +121,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableAsyncDiskCacheCheckInMultiSourceImageAndroid(); + /** + * Process SettingsManager defaults notifications on a dedicated serial queue on iOS. + */ + RN_EXPORT static bool enableAsyncSettingsManagerUpdatesIOS(); + /** * Feature flag to enable the new bridgeless architecture. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 0f3cf823a5a1..678767f0c266 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<46fc995d3bb226c417f9c6f8f8089360>> + * @generated SignedSource<<29e19cbfd6645bcf3ca47559a2b581e3>> */ /** @@ -317,6 +317,24 @@ bool ReactNativeFeatureFlagsAccessor::enableAsyncDiskCacheCheckInMultiSourceImag return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableAsyncSettingsManagerUpdatesIOS() { + auto flagValue = enableAsyncSettingsManagerUpdatesIOS_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(16, "enableAsyncSettingsManagerUpdatesIOS"); + + flagValue = currentProvider_->enableAsyncSettingsManagerUpdatesIOS(); + enableAsyncSettingsManagerUpdatesIOS_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableBridgelessArchitecture() { auto flagValue = enableBridgelessArchitecture_.load(); @@ -326,7 +344,7 @@ bool ReactNativeFeatureFlagsAccessor::enableBridgelessArchitecture() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(16, "enableBridgelessArchitecture"); + markFlagAsAccessed(17, "enableBridgelessArchitecture"); flagValue = currentProvider_->enableBridgelessArchitecture(); enableBridgelessArchitecture_ = flagValue; @@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::enableCppPropsIteratorSetter() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(17, "enableCppPropsIteratorSetter"); + markFlagAsAccessed(18, "enableCppPropsIteratorSetter"); flagValue = currentProvider_->enableCppPropsIteratorSetter(); enableCppPropsIteratorSetter_ = flagValue; @@ -362,7 +380,7 @@ bool ReactNativeFeatureFlagsAccessor::enableCustomFocusSearchOnClippedElementsAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(18, "enableCustomFocusSearchOnClippedElementsAndroid"); + markFlagAsAccessed(19, "enableCustomFocusSearchOnClippedElementsAndroid"); flagValue = currentProvider_->enableCustomFocusSearchOnClippedElementsAndroid(); enableCustomFocusSearchOnClippedElementsAndroid_ = flagValue; @@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::enableDestroyShadowTreeRevisionAsync() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(19, "enableDestroyShadowTreeRevisionAsync"); + markFlagAsAccessed(20, "enableDestroyShadowTreeRevisionAsync"); flagValue = currentProvider_->enableDestroyShadowTreeRevisionAsync(); enableDestroyShadowTreeRevisionAsync_ = flagValue; @@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableDoubleMeasurementFixAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(20, "enableDoubleMeasurementFixAndroid"); + markFlagAsAccessed(21, "enableDoubleMeasurementFixAndroid"); flagValue = currentProvider_->enableDoubleMeasurementFixAndroid(); enableDoubleMeasurementFixAndroid_ = flagValue; @@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enableEagerRootViewAttachment() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(21, "enableEagerRootViewAttachment"); + markFlagAsAccessed(22, "enableEagerRootViewAttachment"); flagValue = currentProvider_->enableEagerRootViewAttachment(); enableEagerRootViewAttachment_ = flagValue; @@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::enableExclusivePropsUpdateAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(22, "enableExclusivePropsUpdateAndroid"); + markFlagAsAccessed(23, "enableExclusivePropsUpdateAndroid"); flagValue = currentProvider_->enableExclusivePropsUpdateAndroid(); enableExclusivePropsUpdateAndroid_ = flagValue; @@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricCommitBranching() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(23, "enableFabricCommitBranching"); + markFlagAsAccessed(24, "enableFabricCommitBranching"); flagValue = currentProvider_->enableFabricCommitBranching(); enableFabricCommitBranching_ = flagValue; @@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricCommitBranchingMergeOnMainThre // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(24, "enableFabricCommitBranchingMergeOnMainThread"); + markFlagAsAccessed(25, "enableFabricCommitBranchingMergeOnMainThread"); flagValue = currentProvider_->enableFabricCommitBranchingMergeOnMainThread(); enableFabricCommitBranchingMergeOnMainThread_ = flagValue; @@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricLogs() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(25, "enableFabricLogs"); + markFlagAsAccessed(26, "enableFabricLogs"); flagValue = currentProvider_->enableFabricLogs(); enableFabricLogs_ = flagValue; @@ -506,7 +524,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFlexboxAutoMinSizeInStrictMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(26, "enableFlexboxAutoMinSizeInStrictMode"); + markFlagAsAccessed(27, "enableFlexboxAutoMinSizeInStrictMode"); flagValue = currentProvider_->enableFlexboxAutoMinSizeInStrictMode(); enableFlexboxAutoMinSizeInStrictMode_ = flagValue; @@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::enableFontScaleChangesUpdatingLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(27, "enableFontScaleChangesUpdatingLayout"); + markFlagAsAccessed(28, "enableFontScaleChangesUpdatingLayout"); flagValue = currentProvider_->enableFontScaleChangesUpdatingLayout(); enableFontScaleChangesUpdatingLayout_ = flagValue; @@ -542,7 +560,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSCompressedTextFrameAdjustment() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(28, "enableIOSCompressedTextFrameAdjustment"); + markFlagAsAccessed(29, "enableIOSCompressedTextFrameAdjustment"); flagValue = currentProvider_->enableIOSCompressedTextFrameAdjustment(); enableIOSCompressedTextFrameAdjustment_ = flagValue; @@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSTextBaselineOffsetPerLine() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(29, "enableIOSTextBaselineOffsetPerLine"); + markFlagAsAccessed(30, "enableIOSTextBaselineOffsetPerLine"); flagValue = currentProvider_->enableIOSTextBaselineOffsetPerLine(); enableIOSTextBaselineOffsetPerLine_ = flagValue; @@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSViewClipToPaddingBox() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(30, "enableIOSViewClipToPaddingBox"); + markFlagAsAccessed(31, "enableIOSViewClipToPaddingBox"); flagValue = currentProvider_->enableIOSViewClipToPaddingBox(); enableIOSViewClipToPaddingBox_ = flagValue; @@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(31, "enableImagePrefetchingAndroid"); + markFlagAsAccessed(32, "enableImagePrefetchingAndroid"); flagValue = currentProvider_->enableImagePrefetchingAndroid(); enableImagePrefetchingAndroid_ = flagValue; @@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImageTransparentTintColor() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(32, "enableImageTransparentTintColor"); + markFlagAsAccessed(33, "enableImageTransparentTintColor"); flagValue = currentProvider_->enableImageTransparentTintColor(); enableImageTransparentTintColor_ = flagValue; @@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImmediateUpdateModeForContentOffsetC // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(33, "enableImmediateUpdateModeForContentOffsetChanges"); + markFlagAsAccessed(34, "enableImmediateUpdateModeForContentOffsetChanges"); flagValue = currentProvider_->enableImmediateUpdateModeForContentOffsetChanges(); enableImmediateUpdateModeForContentOffsetChanges_ = flagValue; @@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImperativeEvents() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(34, "enableImperativeEvents"); + markFlagAsAccessed(35, "enableImperativeEvents"); flagValue = currentProvider_->enableImperativeEvents(); enableImperativeEvents_ = flagValue; @@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImperativeFocus() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(35, "enableImperativeFocus"); + markFlagAsAccessed(36, "enableImperativeFocus"); flagValue = currentProvider_->enableImperativeFocus(); enableImperativeFocus_ = flagValue; @@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::enableInteropViewManagerClassLookUpOptimiz // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(36, "enableInteropViewManagerClassLookUpOptimizationIOS"); + markFlagAsAccessed(37, "enableInteropViewManagerClassLookUpOptimizationIOS"); flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS(); enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue; @@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIntersectionObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(37, "enableIntersectionObserverByDefault"); + markFlagAsAccessed(38, "enableIntersectionObserverByDefault"); flagValue = currentProvider_->enableIntersectionObserverByDefault(); enableIntersectionObserverByDefault_ = flagValue; @@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::enableKeyEvents() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(38, "enableKeyEvents"); + markFlagAsAccessed(39, "enableKeyEvents"); flagValue = currentProvider_->enableKeyEvents(); enableKeyEvents_ = flagValue; @@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(40, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(40, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(41, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::enableModuleArgumentNSNullConversionIOS() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(41, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(42, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMountingCoordinatorPullModelAndroid( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "enableMountingCoordinatorPullModelAndroid"); + markFlagAsAccessed(43, "enableMountingCoordinatorPullModelAndroid"); flagValue = currentProvider_->enableMountingCoordinatorPullModelAndroid(); enableMountingCoordinatorPullModelAndroid_ = flagValue; @@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMutationObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(43, "enableMutationObserverByDefault"); + markFlagAsAccessed(44, "enableMutationObserverByDefault"); flagValue = currentProvider_->enableMutationObserverByDefault(); enableMutationObserverByDefault_ = flagValue; @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "enableNativeCSSParsing"); + markFlagAsAccessed(45, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreallocatedPropsDiffOnInsertAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "enablePreallocatedPropsDiffOnInsertAndroid"); + markFlagAsAccessed(46, "enablePreallocatedPropsDiffOnInsertAndroid"); flagValue = currentProvider_->enablePreallocatedPropsDiffOnInsertAndroid(); enablePreallocatedPropsDiffOnInsertAndroid_ = flagValue; @@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreparedTextLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "enablePreparedTextLayout"); + markFlagAsAccessed(47, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(48, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableResizeObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "enableResizeObserverByDefault"); + markFlagAsAccessed(49, "enableResizeObserverByDefault"); flagValue = currentProvider_->enableResizeObserverByDefault(); enableResizeObserverByDefault_ = flagValue; @@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "enableSwiftUIBasedFilters"); + markFlagAsAccessed(50, "enableSwiftUIBasedFilters"); flagValue = currentProvider_->enableSwiftUIBasedFilters(); enableSwiftUIBasedFilters_ = flagValue; @@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "enableViewCulling"); + markFlagAsAccessed(51, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "enableViewRecycling"); + markFlagAsAccessed(52, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForImage() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(52, "enableViewRecyclingForImage"); + markFlagAsAccessed(53, "enableViewRecyclingForImage"); flagValue = currentProvider_->enableViewRecyclingForImage(); enableViewRecyclingForImage_ = flagValue; @@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "enableViewRecyclingForScrollView"); + markFlagAsAccessed(54, "enableViewRecyclingForScrollView"); flagValue = currentProvider_->enableViewRecyclingForScrollView(); enableViewRecyclingForScrollView_ = flagValue; @@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "enableViewRecyclingForText"); + markFlagAsAccessed(55, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(55, "enableViewRecyclingForView"); + markFlagAsAccessed(56, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(56, "enableVirtualViewContainerStateExperimental"); + markFlagAsAccessed(57, "enableVirtualViewContainerStateExperimental"); flagValue = currentProvider_->enableVirtualViewContainerStateExperimental(); enableVirtualViewContainerStateExperimental_ = flagValue; @@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fixBorderlessRippleAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "fixBorderlessRippleAndroid"); + markFlagAsAccessed(58, "fixBorderlessRippleAndroid"); flagValue = currentProvider_->fixBorderlessRippleAndroid(); fixBorderlessRippleAndroid_ = flagValue; @@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(59, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::fixYogaFlexBasisFitContentInMainAxis() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "fixYogaFlexBasisFitContentInMainAxis"); + markFlagAsAccessed(60, "fixYogaFlexBasisFitContentInMainAxis"); flagValue = currentProvider_->fixYogaFlexBasisFitContentInMainAxis(); fixYogaFlexBasisFitContentInMainAxis_ = flagValue; @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "fuseboxAssertSingleHostState"); + markFlagAsAccessed(61, "fuseboxAssertSingleHostState"); flagValue = currentProvider_->fuseboxAssertSingleHostState(); fuseboxAssertSingleHostState_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "fuseboxEnabledRelease"); + markFlagAsAccessed(62, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxFrameRecordingEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "fuseboxFrameRecordingEnabled"); + markFlagAsAccessed(63, "fuseboxFrameRecordingEnabled"); flagValue = currentProvider_->fuseboxFrameRecordingEnabled(); fuseboxFrameRecordingEnabled_ = flagValue; @@ -1172,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxScreenshotCaptureEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "fuseboxScreenshotCaptureEnabled"); + markFlagAsAccessed(64, "fuseboxScreenshotCaptureEnabled"); flagValue = currentProvider_->fuseboxScreenshotCaptureEnabled(); fuseboxScreenshotCaptureEnabled_ = flagValue; @@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxWebSocketEventsEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "fuseboxWebSocketEventsEnabled"); + markFlagAsAccessed(65, "fuseboxWebSocketEventsEnabled"); flagValue = currentProvider_->fuseboxWebSocketEventsEnabled(); fuseboxWebSocketEventsEnabled_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::optimizedAnimatedPropUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "optimizedAnimatedPropUpdates"); + markFlagAsAccessed(66, "optimizedAnimatedPropUpdates"); flagValue = currentProvider_->optimizedAnimatedPropUpdates(); optimizedAnimatedPropUpdates_ = flagValue; @@ -1226,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(67, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "perfIssuesEnabled"); + markFlagAsAccessed(68, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "perfMonitorV2Enabled"); + markFlagAsAccessed(69, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1280,7 +1298,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "preparedTextCacheSize"); + markFlagAsAccessed(70, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(71, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2Android() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "redBoxV2Android"); + markFlagAsAccessed(72, "redBoxV2Android"); flagValue = currentProvider_->redBoxV2Android(); redBoxV2Android_ = flagValue; @@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2IOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "redBoxV2IOS"); + markFlagAsAccessed(73, "redBoxV2IOS"); flagValue = currentProvider_->redBoxV2IOS(); redBoxV2IOS_ = flagValue; @@ -1352,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(74, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1370,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(75, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1388,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(75, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(76, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipBoundsWithOverflow() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "syncAndroidClipBoundsWithOverflow"); + markFlagAsAccessed(77, "syncAndroidClipBoundsWithOverflow"); flagValue = currentProvider_->syncAndroidClipBoundsWithOverflow(); syncAndroidClipBoundsWithOverflow_ = flagValue; @@ -1424,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(78, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(79, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "updateRuntimeShadowNodeReferencesOnCommitThread"); + markFlagAsAccessed(80, "updateRuntimeShadowNodeReferencesOnCommitThread"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread(); updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue; @@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(81, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1496,7 +1514,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "useFabricInterop"); + markFlagAsAccessed(82, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1514,7 +1532,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(83, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1532,7 +1550,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "useNestedScrollViewAndroid"); + markFlagAsAccessed(84, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1550,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "useSharedAnimatedBackend"); + markFlagAsAccessed(85, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1568,7 +1586,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(85, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(86, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1586,7 +1604,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(86, "useTurboModuleInterop"); + markFlagAsAccessed(87, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1604,7 +1622,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(87, "viewCullingOutsetRatio"); + markFlagAsAccessed(88, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1622,7 +1640,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(88, "viewTransitionEnabled"); + markFlagAsAccessed(89, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1640,7 +1658,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionUseHardwareBitmapAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(89, "viewTransitionUseHardwareBitmapAndroid"); + markFlagAsAccessed(90, "viewTransitionUseHardwareBitmapAndroid"); flagValue = currentProvider_->viewTransitionUseHardwareBitmapAndroid(); viewTransitionUseHardwareBitmapAndroid_ = flagValue; @@ -1658,7 +1676,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(90, "virtualViewPrerenderRatio"); + markFlagAsAccessed(91, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 5233e208552e..b3a4d3a1dd85 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<8a2af1b0fe78648b4ded0504f1b05b27>> */ /** @@ -50,6 +50,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableAndroidAutoOffscreenCompositingForElevation(); bool enableAndroidTextMeasurementOptimizations(); bool enableAsyncDiskCacheCheckInMultiSourceImageAndroid(); + bool enableAsyncSettingsManagerUpdatesIOS(); bool enableBridgelessArchitecture(); bool enableCppPropsIteratorSetter(); bool enableCustomFocusSearchOnClippedElementsAndroid(); @@ -136,7 +137,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 91> accessedFeatureFlags_; + std::array, 92> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -154,6 +155,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableAndroidAutoOffscreenCompositingForElevation_; std::atomic> enableAndroidTextMeasurementOptimizations_; std::atomic> enableAsyncDiskCacheCheckInMultiSourceImageAndroid_; + std::atomic> enableAsyncSettingsManagerUpdatesIOS_; std::atomic> enableBridgelessArchitecture_; std::atomic> enableCppPropsIteratorSetter_; std::atomic> enableCustomFocusSearchOnClippedElementsAndroid_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 18e5f25979bc..0948fe29bcee 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<9218c0c441b0b937737d2fae33fd1e35>> + * @generated SignedSource<> */ /** @@ -93,6 +93,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableAsyncSettingsManagerUpdatesIOS() override { + return false; + } + bool enableBridgelessArchitecture() override { return true; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 9113ffb35fdc..e0d968cef153 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<419516aab11d7e44485bb29e696a75dd>> + * @generated SignedSource<> */ /** @@ -191,6 +191,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableAsyncDiskCacheCheckInMultiSourceImageAndroid(); } + bool enableAsyncSettingsManagerUpdatesIOS() override { + auto value = values_["enableAsyncSettingsManagerUpdatesIOS"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableAsyncSettingsManagerUpdatesIOS(); + } + bool enableBridgelessArchitecture() override { auto value = values_["enableBridgelessArchitecture"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index a29aacbef32e..85fb6ca9f379 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<20cd3794c25292e956b0cf3bad90a6f4>> */ /** @@ -43,6 +43,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableAndroidAutoOffscreenCompositingForElevation() = 0; virtual bool enableAndroidTextMeasurementOptimizations() = 0; virtual bool enableAsyncDiskCacheCheckInMultiSourceImageAndroid() = 0; + virtual bool enableAsyncSettingsManagerUpdatesIOS() = 0; virtual bool enableBridgelessArchitecture() = 0; virtual bool enableCppPropsIteratorSetter() = 0; virtual bool enableCustomFocusSearchOnClippedElementsAndroid() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 65dad77ae6f4..6637a118b9bd 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -124,6 +124,11 @@ bool NativeReactNativeFeatureFlags::enableAsyncDiskCacheCheckInMultiSourceImageA return ReactNativeFeatureFlags::enableAsyncDiskCacheCheckInMultiSourceImageAndroid(); } +bool NativeReactNativeFeatureFlags::enableAsyncSettingsManagerUpdatesIOS( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableAsyncSettingsManagerUpdatesIOS(); +} + bool NativeReactNativeFeatureFlags::enableBridgelessArchitecture( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableBridgelessArchitecture(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 9e1273933741..c4de7f755db4 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<8c268a7135862b4565c9976f30c607a7>> */ /** @@ -70,6 +70,8 @@ class NativeReactNativeFeatureFlags bool enableAsyncDiskCacheCheckInMultiSourceImageAndroid(jsi::Runtime& runtime); + bool enableAsyncSettingsManagerUpdatesIOS(jsi::Runtime& runtime); + bool enableBridgelessArchitecture(jsi::Runtime& runtime); bool enableCppPropsIteratorSetter(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 592bc1dc87ec..67400552c4bb 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -225,6 +225,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableAsyncSettingsManagerUpdatesIOS: { + defaultValue: false, + metadata: { + dateAdded: '2026-10-10', + description: + 'Process SettingsManager defaults notifications on a dedicated serial queue on iOS.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, enableBridgelessArchitecture: { defaultValue: true, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 958225bf8e88..02c9c7a5b392 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -66,6 +66,7 @@ export type ReactNativeFeatureFlags = Readonly<{ enableAndroidAutoOffscreenCompositingForElevation: Getter, enableAndroidTextMeasurementOptimizations: Getter, enableAsyncDiskCacheCheckInMultiSourceImageAndroid: Getter, + enableAsyncSettingsManagerUpdatesIOS: Getter, enableBridgelessArchitecture: Getter, enableCppPropsIteratorSetter: Getter, enableCustomFocusSearchOnClippedElementsAndroid: Getter, @@ -286,6 +287,10 @@ export const enableAndroidTextMeasurementOptimizations: Getter = create * Runs multi-source image disk-cache checks asynchronously on Android. */ export const enableAsyncDiskCacheCheckInMultiSourceImageAndroid: Getter = createNativeFlagGetter('enableAsyncDiskCacheCheckInMultiSourceImageAndroid', false); +/** + * Process SettingsManager defaults notifications on a dedicated serial queue on iOS. + */ +export const enableAsyncSettingsManagerUpdatesIOS: Getter = createNativeFlagGetter('enableAsyncSettingsManagerUpdatesIOS', false); /** * Feature flag to enable the new bridgeless architecture. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index b5585a64a217..82eb167d78c4 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<26f30821669e81ddb8c654ea71bbaca6>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -41,6 +41,7 @@ export interface Spec extends TurboModule { readonly enableAndroidAutoOffscreenCompositingForElevation?: () => boolean; readonly enableAndroidTextMeasurementOptimizations?: () => boolean; readonly enableAsyncDiskCacheCheckInMultiSourceImageAndroid?: () => boolean; + readonly enableAsyncSettingsManagerUpdatesIOS?: () => boolean; readonly enableBridgelessArchitecture?: () => boolean; readonly enableCppPropsIteratorSetter?: () => boolean; readonly enableCustomFocusSearchOnClippedElementsAndroid?: () => boolean;