feat[TraceUpdateOverlay]: add iOS native component (#41563)

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

Changelog:
[iOS][Added] - support "highlighting updates when components render" feature from React DevTools

Reviewed By: javache

Differential Revision: D51470012

fbshipit-source-id: ba4094ae24d14f58ad08acbb90333bbe49e043c9
This commit is contained in:
Ruslan Lesiutin
2023-11-23 07:53:24 -08:00
committed by Facebook GitHub Bot
parent fd35c1db28
commit 758e59f1d9
9 changed files with 210 additions and 6 deletions
@@ -17,7 +17,6 @@ import type {Overlay} from './TraceUpdateOverlayNativeComponent';
import UIManager from '../../ReactNative/UIManager';
import processColor from '../../StyleSheet/processColor';
import StyleSheet from '../../StyleSheet/StyleSheet';
import Platform from '../../Utilities/Platform';
import View from '../View/View';
import TraceUpdateOverlayNativeComponent, {
Commands,
@@ -26,7 +25,6 @@ import * as React from 'react';
const {useEffect, useRef, useState} = React;
const isNativeComponentReady =
Platform.OS === 'android' &&
UIManager.hasViewManagerConfig('TraceUpdateOverlay');
type Props = {
@@ -39,13 +37,13 @@ export default function TraceUpdateOverlay({
const [overlayDisabled, setOverlayDisabled] = useState(false);
useEffect(() => {
if (!isNativeComponentReady) {
return;
}
const drawTraceUpdates = (
nodesToDraw: Array<{node: InstanceFromReactDevTools, color: string}> = [],
) => {
if (!isNativeComponentReady) {
return;
}
// If overlay is disabled before, now it's enabled.
setOverlayDisabled(false);
@@ -38,6 +38,7 @@ Class<RCTComponentViewProtocol> RCTSafeAreaViewCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTScrollViewCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTSwitchCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTTextInputCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTTraceUpdateOverlayCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTUnimplementedNativeViewCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTViewCls(void) __attribute__((used));
Class<RCTComponentViewProtocol> RCTImageCls(void) __attribute__((used));
@@ -26,6 +26,7 @@ Class<RCTComponentViewProtocol> RCTFabricComponentsProvider(const char *name) {
{"ScrollView", RCTScrollViewCls},
{"Switch", RCTSwitchCls},
{"TextInput", RCTTextInputCls},
{"TraceUpdateOverlay", RCTTraceUpdateOverlayCls},
{"UnimplementedNativeView", RCTUnimplementedNativeViewCls},
{"View", RCTViewCls},
{"Image", RCTImageCls},
@@ -0,0 +1,16 @@
/*
* 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>
#import <React/RCTViewComponentView.h>
#import <react/renderer/components/rncore/RCTComponentViewHelpers.h>
@interface RCTTraceUpdateOverlayComponentView : RCTViewComponentView <RCTTraceUpdateOverlayViewProtocol>
@end
@@ -0,0 +1,65 @@
/*
* 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 "RCTTraceUpdateOverlayComponentView.h"
#import <React/RCTDefines.h>
#import <React/RCTLog.h>
#import <React/RCTTraceUpdateOverlay.h>
#import <react/renderer/components/rncore/ComponentDescriptors.h>
#import <react/renderer/components/rncore/EventEmitters.h>
#import <react/renderer/components/rncore/Props.h>
#import <react/renderer/components/rncore/RCTComponentViewHelpers.h>
#import "RCTFabricComponentsPlugins.h"
using namespace facebook::react;
@implementation RCTTraceUpdateOverlayComponentView {
RCTTraceUpdateOverlay *_overlay;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const TraceUpdateOverlayProps>();
_props = defaultProps;
_overlay = [[RCTTraceUpdateOverlay alloc] initWithFrame:self.bounds];
self.contentView = _overlay;
}
return self;
}
#pragma mark - RCTComponentViewProtocol
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<TraceUpdateOverlayComponentDescriptor>();
}
#pragma mark - Native commands
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
{
RCTTraceUpdateOverlayHandleCommand(self, commandName, args);
}
- (void)draw:(NSString *)overlays
{
[_overlay draw:overlays];
}
@end
Class<RCTComponentViewProtocol> RCTTraceUpdateOverlayCls(void)
{
return RCTTraceUpdateOverlayComponentView.class;
}
@@ -0,0 +1,16 @@
/*
* 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>
#import <React/RCTView.h>
@interface RCTTraceUpdateOverlay : RCTView
- (void)draw:(NSString *)serializedNodes;
@end
@@ -0,0 +1,57 @@
/*
* 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 "RCTTraceUpdateOverlay.h"
#import <React/RCTConvert.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>
@implementation RCTTraceUpdateOverlay
- (void)draw:(NSString *)serializedNodes
{
NSArray *subViewsToRemove = [self subviews];
for (UIView *v in subViewsToRemove) {
[v removeFromSuperview];
}
NSError *error = nil;
id deserializedNodes = RCTJSONParse(serializedNodes, &error);
if (error) {
RCTLogError(@"Failed to parse serialized nodes passed to RCTTraceUpdatesOverlay");
return;
}
if (![deserializedNodes isKindOfClass:[NSArray class]]) {
RCTLogError(@"Expected to receive nodes as an array, got %@", NSStringFromClass([deserializedNodes class]));
return;
}
for (NSDictionary *node in deserializedNodes) {
NSDictionary *nodeRectangle = node[@"rect"];
NSNumber *nodeColor = node[@"color"];
NSNumber *x = nodeRectangle[@"left"];
NSNumber *y = nodeRectangle[@"top"];
NSNumber *width = nodeRectangle[@"width"];
NSNumber *height = nodeRectangle[@"height"];
CGRect rect = CGRectMake(x.doubleValue, y.doubleValue, width.doubleValue, height.doubleValue);
UIView *box = [[UIView alloc] initWithFrame:rect];
box.backgroundColor = [UIColor clearColor];
box.layer.borderWidth = 2.0f;
box.layer.borderColor = [RCTConvert UIColor:nodeColor].CGColor;
[self addSubview:box];
}
}
@end
@@ -0,0 +1,12 @@
/*
* 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 <React/RCTViewManager.h>
@interface RCTTraceUpdateOverlayManager : RCTViewManager
@end
@@ -0,0 +1,38 @@
/*
* 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 "RCTTraceUpdateOverlayManager.h"
#import "RCTTraceUpdateOverlay.h"
#import <React/RCTLog.h>
#import <React/RCTUIManager.h>
#import "RCTBridge.h"
@implementation RCTTraceUpdateOverlayManager
RCT_EXPORT_MODULE(TraceUpdateOverlay)
- (UIView *)view
{
return [RCTTraceUpdateOverlay new];
}
RCT_EXPORT_METHOD(draw : (nonnull NSNumber *)viewTag nodes : (NSString *)serializedNodes)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[viewTag];
if ([view isKindOfClass:[RCTTraceUpdateOverlay class]]) {
[(RCTTraceUpdateOverlay *)view draw:serializedNodes];
} else {
RCTLogError(@"Expected view to be RCTTraceUpdateOverlay, got %@", NSStringFromClass([view class]));
}
}];
}
@end