remove use of RCTUnsafeExecuteOnMainQueueSync and main thread setup from RCTDeviceInfo (#49478)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49478

changelog: [internal]

Move all main thread resources that RCTDeviceInfo needs to RCTKeyWindowValuesProxy class. That way, RCTDeviceInfo does not needs to use RCTUnsafeExecuteOnMainQueueSync and doesn't require main thread setup.

Reviewed By: javache

Differential Revision: D69747829

fbshipit-source-id: e8280d2f50258ee59043b5c3865b8a95496be8b6
This commit is contained in:
Samuel Susla
2025-02-19 10:29:24 -08:00
committed by Facebook GitHub Bot
parent c5b926b57b
commit 470bc40889
4 changed files with 156 additions and 37 deletions
@@ -8,6 +8,7 @@
#import "RCTInitializeUIKitProxies.h"
#import "RCTInitialAccessibilityValuesProxy.h"
#import "RCTInitialAppStateProxy.h"
#import "RCTKeyWindowValuesProxy.h"
#import "RCTTraitCollectionProxy.h"
#import "RCTWindowSafeAreaProxy.h"
@@ -19,5 +20,6 @@ void RCTInitializeUIKitProxies(void)
[[RCTTraitCollectionProxy sharedInstance] startObservingTraitCollection];
[[RCTInitialAppStateProxy sharedInstance] recordAppState];
[[RCTInitialAccessibilityValuesProxy sharedInstance] recordAccessibilityValues];
[[RCTKeyWindowValuesProxy sharedInstance] startObservingWindowSizeIfNecessary];
});
}
@@ -0,0 +1,23 @@
/*
* 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 <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTKeyWindowValuesProxy : NSObject
+ (instancetype)sharedInstance;
@property (assign, readonly) CGSize windowSize;
@property (assign, readonly) UIInterfaceOrientation currentInterfaceOrientation;
- (void)startObservingWindowSizeIfNecessary;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,116 @@
/*
* 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 "RCTKeyWindowValuesProxy.h"
#import <React/RCTAssert.h>
#import <React/RCTUtils.h>
#import <mutex>
#import <React/RCTConstants.h>
static NSString *const kFrameKeyPath = @"frame";
@implementation RCTKeyWindowValuesProxy {
BOOL _isObserving;
std::mutex _mutex;
CGSize _currentWindowSize;
UIInterfaceOrientation _currentInterfaceOrientation;
}
+ (instancetype)sharedInstance
{
static RCTKeyWindowValuesProxy *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [RCTKeyWindowValuesProxy new];
});
return sharedInstance;
}
- (instancetype)init
{
self = [super init];
if (self) {
_isObserving = NO;
UIView *mainWindow = RCTKeyWindow();
_currentWindowSize = mainWindow ? mainWindow.bounds.size : UIScreen.mainScreen.bounds.size;
}
return self;
}
- (void)startObservingWindowSizeIfNecessary
{
std::lock_guard<std::mutex> lock(_mutex);
if (!_isObserving) {
_isObserving = YES;
// For backwards compatibility, we register for notifications from the main thread only.
// On the new architecture, we are already on the main thread and RCTUnsafeExecuteOnMainQueueSync will simply call
// the block.
RCTUnsafeExecuteOnMainQueueSync(^{
[RCTKeyWindow() addObserver:self forKeyPath:kFrameKeyPath options:NSKeyValueObservingOptionNew context:nil];
});
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(_interfaceOrientationDidChange)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqualToString:kFrameKeyPath]) {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTWindowFrameDidChangeNotification object:self];
{
std::lock_guard<std::mutex> lock(_mutex);
_currentWindowSize = RCTKeyWindow().bounds.size;
}
}
}
- (CGSize)windowSize
{
{
std::lock_guard<std::mutex> lock(_mutex);
if (_isObserving) {
return _currentWindowSize;
}
}
__block CGSize size;
RCTUnsafeExecuteOnMainQueueSync(^{
size = RCTKeyWindow().bounds.size;
});
return size;
}
- (UIInterfaceOrientation)currentInterfaceOrientation
{
{
std::lock_guard<std::mutex> lock(_mutex);
if (_isObserving) {
return _currentInterfaceOrientation;
}
}
__block UIInterfaceOrientation interfaceOrientation;
RCTUnsafeExecuteOnMainQueueSync(^{
interfaceOrientation = RCTKeyWindow().windowScene.interfaceOrientation;
});
return interfaceOrientation;
}
- (void)_interfaceOrientationDidChange
{
std::lock_guard<std::mutex> lock(_mutex);
_currentInterfaceOrientation = RCTKeyWindow().windowScene.interfaceOrientation;
}
@end
@@ -14,7 +14,9 @@
#import <React/RCTEventDispatcherProtocol.h>
#import <React/RCTInitializing.h>
#import <React/RCTInvalidating.h>
#import <React/RCTKeyWindowValuesProxy.h>
#import <React/RCTUtils.h>
#import <React/RCTWindowSafeAreaProxy.h>
#import <atomic>
#import "CoreModulesPlugins.h"
@@ -40,25 +42,14 @@ RCT_EXPORT_MODULE()
- (instancetype)init
{
if (self = [super init]) {
[RCTKeyWindow() addObserver:self forKeyPath:kFrameKeyPath options:NSKeyValueObservingOptionNew context:nil];
[[RCTKeyWindowValuesProxy sharedInstance] startObservingWindowSizeIfNecessary];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqualToString:kFrameKeyPath]) {
[self interfaceFrameDidChange];
[[NSNotificationCenter defaultCenter] postNotificationName:RCTWindowFrameDidChangeNotification object:self];
}
}
+ (BOOL)requiresMainQueueSetup
{
return YES;
return NO;
}
- (dispatch_queue_t)methodQueue
@@ -92,7 +83,7 @@ RCT_EXPORT_MODULE()
#if TARGET_OS_IOS
_currentInterfaceOrientation = RCTKeyWindow().windowScene.interfaceOrientation;
_currentInterfaceOrientation = [RCTKeyWindowValuesProxy sharedInstance].currentInterfaceOrientation;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(interfaceFrameDidChange)
@@ -145,13 +136,8 @@ static BOOL RCTIsIPhoneNotched()
#if TARGET_OS_IOS
dispatch_once(&onceToken, ^{
RCTAssertMainQueue();
// 20pt is the top safeArea value in non-notched devices
UIWindow *keyWindow = RCTKeyWindow();
if (keyWindow) {
isIPhoneNotched = keyWindow.safeAreaInsets.top > 20;
}
isIPhoneNotched = [RCTWindowSafeAreaProxy sharedInstance].currentSafeAreaInsets.top > 20;
});
#endif
@@ -160,13 +146,11 @@ static BOOL RCTIsIPhoneNotched()
static NSDictionary *RCTExportedDimensions(CGFloat fontScale)
{
RCTAssertMainQueue();
UIScreen *mainScreen = UIScreen.mainScreen;
CGSize screenSize = mainScreen.bounds.size;
UIView *mainWindow = RCTKeyWindow();
// We fallback to screen size if a key window is not found.
CGSize windowSize = mainWindow ? mainWindow.bounds.size : screenSize;
CGSize windowSize = [RCTKeyWindowValuesProxy sharedInstance].windowSize;
NSDictionary<NSString *, NSNumber *> *dimsWindow = @{
@"width" : @(windowSize.width),
@@ -202,20 +186,14 @@ static NSDictionary *RCTExportedDimensions(CGFloat fontScale)
- (NSDictionary<NSString *, id> *)getConstants
{
__block NSDictionary<NSString *, id> *constants;
__weak __typeof(self) weakSelf = self;
RCTUnsafeExecuteOnMainQueueSync(^{
constants = @{
@"Dimensions" : [weakSelf _exportedDimensions],
// Note:
// This prop is deprecated and will be removed in a future release.
// Please use this only for a quick and temporary solution.
// Use <SafeAreaView> instead.
@"isIPhoneX_deprecated" : @(RCTIsIPhoneNotched()),
};
});
return constants;
return @{
@"Dimensions" : [self _exportedDimensions],
// Note:
// This prop is deprecated and will be removed in a future release.
// Please use this only for a quick and temporary solution.
// Use <SafeAreaView> instead.
@"isIPhoneX_deprecated" : @(RCTIsIPhoneNotched()),
};
}
- (void)didReceiveNewContentSizeMultiplier