Skip to content

Commit

Permalink
feat[TraceUpdateOverlay]: add iOS native component (#41563)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #41563

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

Reviewed By: javache

Differential Revision: D51470012

fbshipit-source-id: ba4094ae24d14f58ad08acbb90333bbe49e043c9
  • Loading branch information
hoxyq authored and facebook-github-bot committed Nov 23, 2023
1 parent fd35c1d commit 758e59f
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -26,7 +25,6 @@ import * as React from 'react';

const {useEffect, useRef, useState} = React;
const isNativeComponentReady =
Platform.OS === 'android' &&
UIManager.hasViewManagerConfig('TraceUpdateOverlay');

type Props = {
Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
{"ScrollView", RCTScrollViewCls},
{"Switch", RCTSwitchCls},
{"TextInput", RCTTextInputCls},
{"TraceUpdateOverlay", RCTTraceUpdateOverlayCls},
{"UnimplementedNativeView", RCTUnimplementedNativeViewCls},
{"View", RCTViewCls},
{"Image", RCTImageCls},
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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;
}
16 changes: 16 additions & 0 deletions packages/react-native/React/Views/RCTTraceUpdateOverlay.h
Original file line number Diff line number Diff line change
@@ -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
57 changes: 57 additions & 0 deletions packages/react-native/React/Views/RCTTraceUpdateOverlay.m
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions packages/react-native/React/Views/RCTTraceUpdateOverlayManager.h
Original file line number Diff line number Diff line change
@@ -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
38 changes: 38 additions & 0 deletions packages/react-native/React/Views/RCTTraceUpdateOverlayManager.m
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 758e59f

Please sign in to comment.