Skip to content

Commit cfaba29

Browse files
committed
refactor(iOS): hide any C++ symbols from native-public API (#99)
This PR aims to hide any references to C++ symbols from the public API, *when consumed in C mode* while building Swift module. I add some indirection layers & hide all C++ headers from public API. For details see below (it's best to go commit by commit) 👇🏻 > [!note] > I do wonder why the compiler does not complain about rest of the files - we use C++ apis in "old" implementation excessively. My guess would be, that we're kinda lucky in similar fashion to mapbox - most likely RCT_NEW_ARCH_ENABLED is not defined for Swift compiler & all our usage of C++ symbols is behind that flag. I guess it's fine? It'll become a problem, when we get rid of Paper & remove the flag usage, however it is likely that we won't do that for the old impl (it'll be simply replaced by Fabric-only new impl). ## Testing The FabricExample builds fine in "regular setup". When building with static frameworks it fails due to gesture handler 👇🏻 ![image](https://github.com/user-attachments/assets/6388cb23-a355-4c48-9b4a-5f4b4ca7fcd5) If GH is removed, the build passes. Build for dynamic frameworks (tested only w/o GH) also passes locally. I can try bumping GH in follow up <details> <summary>Details of what changed (commits & descriptions)</summary> - **Migrate event emitting code of BottomTabsHost to dedicated class** This is a start of migration of library-wide refactor of our Objective-C++ code. We need to get rid of any references / imports of any C++ code / symbols in our header to make them consumable by our Swift implementation files. This particular commit migrates raw references to react event emitter into calls to wrapper object RNSBottomTabsHostEventEmitter. - **Add RNSReactBaseView** This class is meant to serve as a proxy for `RCTViewComponentView` for our component classes to inherit from. When needed this can be extended for old architecture. For views that do not need inheritance from `RCTViewComponentView` and `UIView<RCTComponentViewProtocol>` is enough separate proxy base class should be created. - **Use new RNSReactBaseView for RNSBottomTabsHostComponentView** This eliminates error with inheriting from "not existing" type. Type forward declaration is not enough unfortunately. - **Remove forward declaration for OnNativeFocusChangePayload** Unneeded. Can add it back, when we'll have multiple types, so that the main definition of RNSBottomTabsHostEventEmitter is not obfuscated. - **Define rnscreens::conversion namespace & nested functions only when in C++ mode** I believe this flag is not needed in implementation file, since .mm will be always compiled with C++. - **Use C-style struct defining syntax when not in C++ mode** I'm not confident in this change. Original code triggered an issue, that the symbol was missing. It was obviously due to struct definition syntax differences between C & C++. Therefore this commit splits C++ & C definitions. My main doubts are about memory layout of these structs & if these can be different & in this can lead to an runtime UB at any point. For now assuming that the type exposed to Swift here won't really matter. - **Make RNSStackScreenComponentView inherit from RNSReactBaseView** - **Make RNSScreenStackHostComponentView inherit from RNSReactBaseView** Also remove imports introducing any C++ symbols from .h file. - **Fix incorrect extension name for RNSBottomTabsScreenEventEmitter** - **Make all C++ headers "project" & set this on "common" subspec** Both {private,project}_header_files work here fine (in terms of project build), however from semantic standpoint "project" make more sense here. See nice summary of these two options: https://stackoverflow.com/a/8016333/8635522. Our C++ pieces are not meant to be consumed by any other target than us. We do not need to expose these symbols for anyone & we won't be consuming them in our Swift code. We might relax this later, when needed. Second thing is that this commit moves the setting from main spec to subspec. It made more sense for me, since the sources in the `common` & `cpp` directories belong to the subspec. At last - w/o this change build of Swift module will fail, complaining that it does not understand C++ symbols. - Fix issue with imports inside nullability audited region - **Make all our Swift symbols & method referenced in Objective-C public** Seems that this is requried for the symbols to be visible in Objective-C. This has obvious downsides - we leak unnecessary symbols to the public API, but this must suffice for now. We can look for a way to tighten things up later. See reference: https://silverhammermba.github.io/blog/2020/02/26/swift-and-obj-c </details>
1 parent 4004887 commit cfaba29

22 files changed

+300
-97
lines changed

FabricExample/ios/FabricExample.xcodeproj/project.pbxproj

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
0C80B921A6F3F58F76C31292 /* libPods-FabricExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-FabricExample.a */; };
1110
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
1211
4338CA5B47FD1D3579126EBC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 06C4D147D69C731B2F1541AC /* PrivacyInfo.xcprivacy */; };
1312
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
13+
837C91B4642520B4D152E2A1 /* libPods-FabricExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 67EDC5A0C3417BC3A60E3943 /* libPods-FabricExample.a */; };
1414
EBA0179C2D12EF04006DCC81 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBA0179B2D12EF04006DCC81 /* AppDelegate.swift */; };
1515
/* End PBXBuildFile section */
1616

@@ -21,7 +21,7 @@
2121
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = FabricExample/Info.plist; sourceTree = "<group>"; };
2222
3B4392A12AC88292D35C810B /* Pods-FabricExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FabricExample.debug.xcconfig"; path = "Target Support Files/Pods-FabricExample/Pods-FabricExample.debug.xcconfig"; sourceTree = "<group>"; };
2323
5709B34CF0A7D63546082F79 /* Pods-FabricExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FabricExample.release.xcconfig"; path = "Target Support Files/Pods-FabricExample/Pods-FabricExample.release.xcconfig"; sourceTree = "<group>"; };
24-
5DCACB8F33CDC322A6C60F78 /* libPods-FabricExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FabricExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
24+
67EDC5A0C3417BC3A60E3943 /* libPods-FabricExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FabricExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
2525
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = FabricExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
2626
EB76649F2D13010D00B432B5 /* FabricExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FabricExample-Bridging-Header.h"; sourceTree = "<group>"; };
2727
EBA0179B2D12EF04006DCC81 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -33,7 +33,7 @@
3333
isa = PBXFrameworksBuildPhase;
3434
buildActionMask = 2147483647;
3535
files = (
36-
0C80B921A6F3F58F76C31292 /* libPods-FabricExample.a in Frameworks */,
36+
837C91B4642520B4D152E2A1 /* libPods-FabricExample.a in Frameworks */,
3737
);
3838
runOnlyForDeploymentPostprocessing = 0;
3939
};
@@ -57,7 +57,7 @@
5757
isa = PBXGroup;
5858
children = (
5959
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
60-
5DCACB8F33CDC322A6C60F78 /* libPods-FabricExample.a */,
60+
67EDC5A0C3417BC3A60E3943 /* libPods-FabricExample.a */,
6161
);
6262
name = Frameworks;
6363
sourceTree = "<group>";
@@ -366,6 +366,17 @@
366366
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
367367
GCC_WARN_UNUSED_FUNCTION = YES;
368368
GCC_WARN_UNUSED_VARIABLE = YES;
369+
HEADER_SEARCH_PATHS = (
370+
"$(inherited)",
371+
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
372+
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core",
373+
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers",
374+
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios",
375+
"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
376+
"${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers",
377+
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
378+
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios",
379+
);
369380
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
370381
LD = "";
371382
LDPLUSPLUS = "";
@@ -443,6 +454,17 @@
443454
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
444455
GCC_WARN_UNUSED_FUNCTION = YES;
445456
GCC_WARN_UNUSED_VARIABLE = YES;
457+
HEADER_SEARCH_PATHS = (
458+
"$(inherited)",
459+
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers",
460+
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core",
461+
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers",
462+
"${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios",
463+
"${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx",
464+
"${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers",
465+
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers",
466+
"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios",
467+
);
446468
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
447469
LD = "";
448470
LDPLUSPLUS = "";

RNScreens.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ Pod::Spec.new do |s|
2121
s.platforms = { :ios => min_supported_ios_version, :tvos => min_supported_tvos_version, :visionos => min_supported_visionos_version }
2222
s.source = { :git => "https://github.com/software-mansion/react-native-screens.git", :tag => "#{s.version}" }
2323
s.source_files = source_files
24-
s.project_header_files = "cpp/**/*.h" # Don't expose C++ headers publicly to allow importing framework into Swift files
2524
s.requires_arc = true
2625

2726
s.pod_target_xcconfig = {
@@ -32,6 +31,7 @@ Pod::Spec.new do |s|
3231
if new_arch_enabled
3332
s.subspec "common" do |ss|
3433
ss.source_files = ["common/cpp/**/*.{cpp,h}", "cpp/**/*.{cpp,h}"]
34+
ss.project_header_files = "common/cpp/**/*.h", "cpp/**/*.h" # Don't expose C++ headers publicly to allow importing framework into Swift files
3535
ss.header_dir = "rnscreens"
3636
ss.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/common/cpp\"" }
3737
end

ios/bottom-tabs/RNSBottomTabsHostComponentView.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
#import <React/RCTViewComponentView.h>
2-
#import <react/renderer/components/rnscreens/EventEmitters.h>
31
#import "RNSBottomTabsHostComponentViewManager.h"
2+
#import "RNSBottomTabsHostEventEmitter.h"
43
#import "RNSEnums.h"
4+
#import "RNSReactBaseView.h"
55
#import "RNSScreenContainer.h"
66

77
NS_ASSUME_NONNULL_BEGIN
@@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
1616
* 2. provider of React state & props for the tab bar controller
1717
* 3. two way communication channel with React (commands & events)
1818
*/
19-
@interface RNSBottomTabsHostComponentView : RCTViewComponentView <RNSScreenContainerDelegate>
19+
@interface RNSBottomTabsHostComponentView : RNSReactBaseView <RNSScreenContainerDelegate>
2020

2121
@end
2222

@@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
2828
@property (nonatomic, strong, readonly, nullable) UIBlurEffect *tabBarBlurEffect;
2929
@property (nonatomic, strong, readonly, nullable) NSNumber *tabBarItemTitleFontSize;
3030

31-
@property (nonatomic, readonly) bool experimental_controlNavigationStateInJS;
31+
@property (nonatomic, readonly) BOOL experimental_controlNavigationStateInJS;
3232

3333
@end
3434

@@ -37,13 +37,11 @@ NS_ASSUME_NONNULL_BEGIN
3737
@interface RNSBottomTabsHostComponentView ()
3838

3939
/**
40-
* This pointer might be `nullptr`! All this method does is a cast of the backing field inherited from
41-
* `RCTViewComponentView`. The nullability of this pointer is therefore determined by `_eventEmitter` lifecycle in the
42-
* super class.
40+
* Use returned object to emit appropriate React Events to Element Tree.
4341
*/
44-
- (std::shared_ptr<const facebook::react::RNSBottomTabsEventEmitter>)reactEventEmitter;
42+
- (nonnull RNSBottomTabsHostEventEmitter *)reactEventEmitter;
4543

46-
- (bool)emitOnNativeFocusChangeRequestSelectedTabScreen:(nonnull RNSBottomTabsScreenComponentView *)tabScreen;
44+
- (BOOL)emitOnNativeFocusChangeRequestSelectedTabScreen:(nonnull RNSBottomTabsScreenComponentView *)tabScreen;
4745

4846
@end
4947

ios/bottom-tabs/RNSBottomTabsHostComponentView.mm

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ @implementation RNSBottomTabsHostComponentView {
2323
RNSTabBarController *_controller;
2424
RNSTabBarControllerDelegate *_controllerDelegate;
2525

26+
RNSBottomTabsHostEventEmitter *_Nonnull _reactEventEmitter;
27+
2628
// RCTViewComponentView does not expose this field, therefore we maintain
2729
// it on our side.
2830
NSMutableArray<RNSBottomTabsScreenComponentView *> *_reactSubviews;
@@ -45,7 +47,9 @@ - (void)initState
4547
_controller = [[RNSTabBarController alloc] initWithTabsHostComponentView:self];
4648
_controllerDelegate = [RNSTabBarControllerDelegate new];
4749
_controller.delegate = _controllerDelegate;
50+
4851
_reactSubviews = [NSMutableArray new];
52+
_reactEventEmitter = [RNSBottomTabsHostEventEmitter new];
4953

5054
_hasModifiedReactSubviewsInCurrentTransaction = NO;
5155
_needsTabBarAppearanceUpdate = NO;
@@ -108,18 +112,15 @@ - (void)markChildUpdated
108112

109113
#pragma mark - React events
110114

111-
- (std::shared_ptr<const react::RNSBottomTabsEventEmitter>)reactEventEmitter
115+
- (nonnull RNSBottomTabsHostEventEmitter *)reactEventEmitter
112116
{
113-
return std::dynamic_pointer_cast<const react::RNSBottomTabsEventEmitter>(_eventEmitter);
117+
RCTAssert(_reactEventEmitter != nil, @"[RNScreens] Attempt to access uninitialized _reactEventEmitter");
118+
return _reactEventEmitter;
114119
}
115120

116-
- (bool)emitOnNativeFocusChangeRequestSelectedTabScreen:(RNSBottomTabsScreenComponentView *)tabScreen
121+
- (BOOL)emitOnNativeFocusChangeRequestSelectedTabScreen:(RNSBottomTabsScreenComponentView *)tabScreen
117122
{
118-
if (const auto eventEmitter = self.reactEventEmitter; eventEmitter != nullptr) {
119-
eventEmitter->onNativeFocusChange({RCTStringFromNSString(tabScreen.tabKey)});
120-
return true;
121-
}
122-
return false;
123+
return [_reactEventEmitter emitOnNativeFocusChange:OnNativeFocusChangePayload{.tabKey = tabScreen.tabKey}];
123124
}
124125

125126
#pragma mark - RCTViewComponentViewProtocol
@@ -182,6 +183,14 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props
182183
[super updateProps:props oldProps:oldProps];
183184
}
184185

186+
- (void)updateEventEmitter:(const facebook::react::EventEmitter::Shared &)eventEmitter
187+
{
188+
[super updateEventEmitter:eventEmitter];
189+
190+
const auto &castedEventEmitter = std::static_pointer_cast<const react::RNSBottomTabsEventEmitter>(eventEmitter);
191+
[_reactEventEmitter updateEventEmitter:castedEventEmitter];
192+
}
193+
185194
- (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask
186195
{
187196
if (_needsTabBarAppearanceUpdate) {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#import <Foundation/Foundation.h>
2+
3+
// Hide C++ symbols from C compiler used when building Swift module
4+
#if defined(__cplusplus)
5+
#import <react/renderer/components/rnscreens/EventEmitters.h>
6+
7+
namespace react = facebook::react;
8+
#endif // __cplusplus
9+
10+
NS_ASSUME_NONNULL_BEGIN
11+
12+
#if defined(__cplusplus)
13+
struct OnNativeFocusChangePayload {
14+
NSString *_Nonnull tabKey;
15+
};
16+
#else
17+
typedef struct {
18+
NSString *_Nonnull tabKey;
19+
} OnNativeFocusChangePayload;
20+
#endif
21+
22+
@interface RNSBottomTabsHostEventEmitter : NSObject
23+
24+
- (BOOL)emitOnNativeFocusChange:(OnNativeFocusChangePayload)payload;
25+
26+
@end
27+
28+
#pragma mark - Hidden from Swift
29+
30+
#if defined(__cplusplus)
31+
32+
@interface RNSBottomTabsHostEventEmitter ()
33+
34+
- (void)updateEventEmitter:(const std::shared_ptr<const react::RNSBottomTabsEventEmitter> &)emitter;
35+
36+
@end
37+
38+
#endif // __cplusplus
39+
40+
NS_ASSUME_NONNULL_END
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#import "RNSBottomTabsHostEventEmitter.h"
2+
3+
#import <React/RCTConversions.h>
4+
#import <React/RCTLog.h>
5+
#import <react/renderer/components/rnscreens/EventEmitters.h>
6+
7+
namespace react = facebook::react;
8+
9+
@implementation RNSBottomTabsHostEventEmitter {
10+
std::shared_ptr<const react::RNSBottomTabsEventEmitter> _reactEventEmitter;
11+
}
12+
13+
- (instancetype)init
14+
{
15+
if (self = [super init]) {
16+
_reactEventEmitter = nullptr;
17+
}
18+
return self;
19+
}
20+
21+
- (void)updateEventEmitter:(const std::shared_ptr<const react::RNSBottomTabsEventEmitter> &)emitter
22+
{
23+
_reactEventEmitter = emitter;
24+
}
25+
26+
- (BOOL)emitOnNativeFocusChange:(OnNativeFocusChangePayload)payload
27+
{
28+
if (_reactEventEmitter != nullptr) {
29+
_reactEventEmitter->onNativeFocusChange({.tabKey = RCTStringFromNSString(payload.tabKey)});
30+
return true;
31+
} else {
32+
RCTLogWarn(@"[RNScreens] Skipped OnNativeFocusChange event emission due to nullish emitter");
33+
return false;
34+
}
35+
}
36+
37+
@end

ios/bottom-tabs/RNSBottomTabsScreenComponentView.h

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#import <React/RCTViewComponentView.h>
2-
#import <react/renderer/components/rnscreens/EventEmitters.h>
1+
#import "RNSBottomTabsScreenEventEmitter.h"
2+
#import "RNSReactBaseView.h"
33

44
NS_ASSUME_NONNULL_BEGIN
55

@@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
1010
* Component view with react managed lifecycle. This view serves as root view in hierarchy
1111
* of a particular tab.
1212
*/
13-
@interface RNSBottomTabsScreenComponentView : RCTViewComponentView
13+
@interface RNSBottomTabsScreenComponentView : RNSReactBaseView
1414

1515
/**
1616
* View controller responsible for managing tab represented by this component view.
@@ -43,24 +43,12 @@ NS_ASSUME_NONNULL_BEGIN
4343

4444
#pragma mark - Events
4545

46-
/**
47-
* These methods can be called to send an appropriate event to ElementTree.
48-
* Returned value denotes whether the event has been successfully dispatched to React event pipeline.
49-
* The returned value of `true` does not mean, that the event has been successfully delivered.
50-
*/
5146
@interface RNSBottomTabsScreenComponentView ()
5247

5348
/**
54-
* This pointer might be `nullptr`! All this method does is a cast of the backing field inherited from
55-
* `RCTViewComponentView`. The nullability of this pointer is therefore determined by `_eventEmitter` lifecycle in the
56-
* super class.
49+
* Use returned object to emit appropriate React Events to Element Tree.
5750
*/
58-
- (std::shared_ptr<const facebook::react::RNSBottomTabsScreenEventEmitter>)reactEventEmitter;
59-
60-
- (bool)emitOnWillAppear;
61-
- (bool)emitOnDidAppear;
62-
- (bool)emitOnWillDisappear;
63-
- (bool)emitOnDidDisappear;
51+
- (nonnull RNSBottomTabsScreenEventEmitter *)reactEventEmitter;
6452

6553
@end
6654

ios/bottom-tabs/RNSBottomTabsScreenComponentView.mm

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
@implementation RNSBottomTabsScreenComponentView {
1616
RNSTabsScreenViewController *_controller;
1717
RNSBottomTabsHostComponentView *__weak _Nullable _reactSuperview;
18+
19+
RNSBottomTabsScreenEventEmitter *_Nonnull _reactEventEmitter;
1820
}
1921

2022
- (instancetype)initWithFrame:(CGRect)frame
@@ -34,6 +36,8 @@ - (void)initState
3436
_controller.view = self;
3537

3638
_reactSuperview = nil;
39+
_reactEventEmitter = [RNSBottomTabsScreenEventEmitter new];
40+
3741
[self resetProps];
3842
}
3943

@@ -54,45 +58,10 @@ - (nullable RNSBottomTabsHostComponentView *)reactSuperview
5458

5559
#pragma mark - Events
5660

57-
- (std::shared_ptr<const facebook::react::RNSBottomTabsScreenEventEmitter>)reactEventEmitter
58-
{
59-
return std::dynamic_pointer_cast<const facebook::react::RNSBottomTabsScreenEventEmitter>(_eventEmitter);
60-
}
61-
62-
- (bool)emitOnWillAppear
61+
- (nonnull RNSBottomTabsScreenEventEmitter *)reactEventEmitter
6362
{
64-
if (const auto eventEmitter = self.reactEventEmitter; eventEmitter != nullptr) {
65-
eventEmitter->onWillAppear({});
66-
return true;
67-
}
68-
return false;
69-
}
70-
71-
- (bool)emitOnDidAppear
72-
{
73-
if (const auto eventEmitter = self.reactEventEmitter; eventEmitter != nullptr) {
74-
eventEmitter->onDidAppear({});
75-
return true;
76-
}
77-
return false;
78-
}
79-
80-
- (bool)emitOnWillDisappear
81-
{
82-
if (const auto eventEmitter = self.reactEventEmitter; eventEmitter != nullptr) {
83-
eventEmitter->onWillDisappear({});
84-
return true;
85-
}
86-
return false;
87-
}
88-
89-
- (bool)emitOnDidDisappear
90-
{
91-
if (const auto eventEmitter = self.reactEventEmitter; eventEmitter != nullptr) {
92-
eventEmitter->onDidDisappear({});
93-
return true;
94-
}
95-
return false;
63+
RCTAssert(_reactEventEmitter != nil, @"[RNScreens] Attempt to access uninitialized _reactEventEmitter");
64+
return _reactEventEmitter;
9665
}
9766

9867
- (nullable RNSTabBarController *)findTabBarController
@@ -158,6 +127,13 @@ - (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetric
158127
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
159128
}
160129

130+
- (void)updateEventEmitter:(const facebook::react::EventEmitter::Shared &)eventEmitter
131+
{
132+
[super updateEventEmitter:eventEmitter];
133+
[_reactEventEmitter
134+
updateEventEmitter:std::static_pointer_cast<const react::RNSBottomTabsScreenEventEmitter>(eventEmitter)];
135+
}
136+
161137
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
162138
{
163139
NSLog(@"TabScreen [%ld] mount [%ld] at %ld", self.tag, childComponentView.tag, index);

0 commit comments

Comments
 (0)