Skip to content

Commit 417adf5

Browse files
fkgozalifacebook-github-bot
authored andcommitted
Fabric iOS: allow using fallback component for unsupported ones
Summary: This allows an unsupported component to be rendered as a "unimplemented view" for better visualization of which component is missing. It is off by default, but configurable in the component factory. For now, the layout simply follows regular <View />, which means the width/height etc is based on the react component styling. The side effect is that components with 0 height/width won't show up at all. Reviewed By: mdvacca Differential Revision: D14869656 fbshipit-source-id: f31e012fb7dc1c64fcc431ea5aa45079a23a618e
1 parent 63343da commit 417adf5

File tree

8 files changed

+182
-4
lines changed

8 files changed

+182
-4
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
11+
'use strict';
12+
13+
const requireNativeComponent = require('requireNativeComponent');
14+
15+
import type {ViewProps} from 'ViewPropTypes';
16+
import type {ViewStyleProp} from 'StyleSheet';
17+
import type {NativeComponent} from 'ReactNative';
18+
19+
type NativeProps = $ReadOnly<{|
20+
...ViewProps,
21+
name?: ?string,
22+
style?: ?ViewStyleProp,
23+
|}>;
24+
25+
type UnimplementedViewNativeType = Class<NativeComponent<NativeProps>>;
26+
27+
module.exports = ((requireNativeComponent(
28+
'UnimplementedNativeView',
29+
): any): UnimplementedViewNativeType);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @flow
9+
*/
10+
11+
'use strict';
12+
13+
import type {SchemaType} from '../../../packages/react-native-codegen/src/CodegenSchema.js';
14+
15+
const UnimplementedNativeViewSchema: SchemaType = {
16+
modules: {
17+
UnimplementedNativeViewSchema: {
18+
components: {
19+
UnimplementedNativeView: {
20+
extendsProps: [
21+
{
22+
type: 'ReactNativeBuiltInType',
23+
knownTypeName: 'ReactNativeCoreViewProps',
24+
},
25+
],
26+
events: [],
27+
props: [
28+
{
29+
name: 'name',
30+
optional: true,
31+
typeAnnotation: {
32+
type: 'StringTypeAnnotation',
33+
default: '',
34+
},
35+
},
36+
],
37+
},
38+
},
39+
},
40+
},
41+
};
42+
43+
module.exports = UnimplementedNativeViewSchema;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <UIKit/UIKit.h>
9+
10+
#import <React/RCTViewComponentView.h>
11+
12+
NS_ASSUME_NONNULL_BEGIN
13+
14+
@interface RCTUnimplementedNativeComponentView : RCTViewComponentView
15+
16+
@end
17+
18+
NS_ASSUME_NONNULL_END
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import "RCTUnimplementedNativeComponentView.h"
9+
10+
#import <react/components/rncore/EventEmitters.h>
11+
#import <react/components/rncore/Props.h>
12+
#import <react/components/rncore/ShadowNodes.h>
13+
14+
using namespace facebook::react;
15+
16+
@implementation RCTUnimplementedNativeComponentView {
17+
UILabel *_label;
18+
}
19+
20+
#pragma mark - RCTComponentViewProtocol
21+
22+
+ (ComponentHandle)componentHandle
23+
{
24+
return UnimplementedNativeViewShadowNode::Handle();
25+
}
26+
27+
- (instancetype)initWithFrame:(CGRect)frame
28+
{
29+
if (self = [super initWithFrame:frame]) {
30+
static const auto defaultProps = std::make_shared<const UnimplementedNativeViewProps>();
31+
_props = defaultProps;
32+
33+
CGRect bounds = self.bounds;
34+
_label = [[UILabel alloc] initWithFrame:bounds];
35+
_label.backgroundColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.3];
36+
_label.layoutMargins = UIEdgeInsetsMake(12, 12, 12, 12);
37+
_label.lineBreakMode = NSLineBreakByWordWrapping;
38+
_label.numberOfLines = 0;
39+
_label.textAlignment = NSTextAlignmentCenter;
40+
_label.textColor = [UIColor whiteColor];
41+
42+
self.contentView = _label;
43+
}
44+
45+
return self;
46+
}
47+
48+
- (void)updateProps:(SharedProps)props oldProps:(SharedProps)oldProps
49+
{
50+
const auto &oldViewProps = *std::static_pointer_cast<const UnimplementedNativeViewProps>(oldProps ?: _props);
51+
const auto &newViewProps = *std::static_pointer_cast<const UnimplementedNativeViewProps>(props);
52+
53+
[super updateProps:props oldProps:oldProps];
54+
55+
if (oldViewProps.name != newViewProps.name) {
56+
_label.text = [NSString stringWithFormat:@"'%s' is not Fabric compatible yet.", newViewProps.name.c_str()];
57+
}
58+
}
59+
60+
@end

React/Fabric/Mounting/RCTComponentViewFactory.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#import "RCTScrollViewComponentView.h"
1818
#import "RCTSliderComponentView.h"
1919
#import "RCTSwitchComponentView.h"
20+
#import "RCTUnimplementedNativeComponentView.h"
2021
#import "RCTViewComponentView.h"
2122

2223
using namespace facebook::react;
@@ -39,6 +40,7 @@ + (RCTComponentViewFactory *)standardComponentViewFactory
3940
[componentViewFactory registerComponentViewClass:[RCTActivityIndicatorViewComponentView class]];
4041
[componentViewFactory registerComponentViewClass:[RCTSliderComponentView class]];
4142
[componentViewFactory registerComponentViewClass:[RCTSwitchComponentView class]];
43+
[componentViewFactory registerComponentViewClass:[RCTUnimplementedNativeComponentView class]];
4244

4345
return componentViewFactory;
4446
}

ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,12 @@ const ComponentDescriptor &ComponentDescriptorRegistry::at(
9292

9393
auto it = _registryByName.find(unifiedComponentName);
9494
if (it == _registryByName.end()) {
95-
throw std::invalid_argument(
96-
("Unable to find componentDescriptor for " + unifiedComponentName)
97-
.c_str());
95+
if (_fallbackComponentDescriptor == nullptr) {
96+
throw std::invalid_argument(
97+
("Unable to find componentDescriptor for " + unifiedComponentName)
98+
.c_str());
99+
}
100+
return *_fallbackComponentDescriptor.get();
98101
}
99102
return *it->second;
100103
}
@@ -123,5 +126,16 @@ SharedShadowNode ComponentDescriptorRegistry::createNode(
123126
return shadowNode;
124127
}
125128

129+
void ComponentDescriptorRegistry::setFallbackComponentDescriptor(
130+
SharedComponentDescriptor descriptor) {
131+
_fallbackComponentDescriptor = descriptor;
132+
registerComponentDescriptor(descriptor);
133+
}
134+
135+
const SharedComponentDescriptor
136+
ComponentDescriptorRegistry::getFallbackComponentDescriptor() const {
137+
return _fallbackComponentDescriptor;
138+
}
139+
126140
} // namespace react
127141
} // namespace facebook

ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,13 @@ class ComponentDescriptorRegistry {
3939
Tag rootTag,
4040
const folly::dynamic &props,
4141
const SharedEventTarget &eventTarget) const;
42+
void setFallbackComponentDescriptor(SharedComponentDescriptor descriptor);
43+
const SharedComponentDescriptor getFallbackComponentDescriptor() const;
4244

4345
private:
4446
better::map<ComponentHandle, SharedComponentDescriptor> _registryByHandle;
4547
better::map<ComponentName, SharedComponentDescriptor> _registryByName;
48+
SharedComponentDescriptor _fallbackComponentDescriptor;
4649
};
4750

4851
} // namespace react

ReactCommon/fabric/uimanager/UIManager.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,22 @@ SharedShadowNode UIManager::createNode(
1818
SystraceSection s("UIManager::createNode");
1919

2020
auto &componentDescriptor = componentDescriptorRegistry_->at(name);
21+
auto fallbackDescriptor =
22+
componentDescriptorRegistry_->getFallbackComponentDescriptor();
23+
2124
const auto &props = componentDescriptor.cloneProps(nullptr, rawProps);
2225
const auto &state = componentDescriptor.createInitialState(props);
2326

2427
auto shadowNode = componentDescriptor.createShadowNode({
2528
/* .tag = */ tag,
2629
/* .rootTag = */ surfaceId,
27-
/* .props = */ props,
30+
/* .props = */
31+
fallbackDescriptor != nullptr &&
32+
fallbackDescriptor->getComponentHandle() ==
33+
componentDescriptor.getComponentHandle()
34+
? componentDescriptor.cloneProps(
35+
nullptr, RawProps(folly::dynamic::object("name", name)))
36+
: props,
2837
/* .eventEmitter = */
2938
componentDescriptor.createEventEmitter(std::move(eventTarget), tag),
3039
/* .children = */ ShadowNodeFragment::childrenPlaceholder(),

0 commit comments

Comments
 (0)