From 25a00520d80b8b456b1eccfb106b75929f2f3bc2 Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Tue, 8 Nov 2022 07:06:07 -0800 Subject: [PATCH] Refactor subclassing of RCTEventEmitter (#35106) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/35106 This Diff remove the assert on the initializer in the EventEmitter in place of a more idiomatic check when the method is invoked. It aims to solve an internal error and promotes best practices for the iOS platform. ## Changelog: [iOS][Changed] Refactor RCTEventEmitter initialization Reviewed By: sammy-SC Differential Revision: D40762253 fbshipit-source-id: 83d26616ce147914948e536e9e4b1010758fb690 --- React/Modules/RCTEventEmitter.m | 25 ++++--- .../RNTesterPods.xcodeproj/project.pbxproj | 4 ++ .../RNTesterUnitTests/RCTEventEmitterTests.m | 72 +++++++++++++++++++ 3 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 packages/rn-tester/RNTesterUnitTests/RCTEventEmitterTests.m diff --git a/React/Modules/RCTEventEmitter.m b/React/Modules/RCTEventEmitter.m index 568c773e9bb125..38650c34f0dd45 100644 --- a/React/Modules/RCTEventEmitter.m +++ b/React/Modules/RCTEventEmitter.m @@ -23,17 +23,6 @@ + (NSString *)moduleName return @""; } -+ (void)initialize -{ - [super initialize]; - if (self != [RCTEventEmitter class]) { - RCTAssert( - RCTClassOverridesInstanceMethod(self, @selector(supportedEvents)), - @"You must override the `supportedEvents` method of %@", - self); - } -} - - (instancetype)initWithDisabledObservation { self = [super init]; @@ -43,6 +32,9 @@ - (instancetype)initWithDisabledObservation - (NSArray *)supportedEvents { + NSString *message = + [NSString stringWithFormat:@"%@ must implement the supportedEvents method", NSStringFromClass(self.class)]; + [self _log:message]; return nil; } @@ -147,4 +139,15 @@ - (void)invalidate } } +#pragma mark - Test utilities + +// For testing purposes only. +// This is supposed to be overriden by a subclass in the Tests +// to verified that the error message is actually emitted. +// This is the less intrusive way found to mock the RCTLogError function in unit tests. +- (void)_log:(NSString *)message +{ + RCTLogError(@"%@", message); +} + @end diff --git a/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj b/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj index 73488e795ff4e0..3f1016eaf000f2 100644 --- a/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj +++ b/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 5C60EB1C226440DB0018C04F /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C60EB1B226440DB0018C04F /* AppDelegate.mm */; }; 5CB07C9B226467E60039471C /* RNTesterTurboModuleProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5CB07C99226467E60039471C /* RNTesterTurboModuleProvider.mm */; }; 8145AE06241172D900A3F8DA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8145AE05241172D900A3F8DA /* LaunchScreen.storyboard */; }; + CD10C7A5290BD4EB0033E1ED /* RCTEventEmitterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CD10C7A4290BD4EB0033E1ED /* RCTEventEmitterTests.m */; }; D19DB1AC8A1EBBFA0D14DB66 /* libPods-RNTester.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 25B78D39CC03C49968A739B2 /* libPods-RNTester.a */; }; DCD006323AC907670B0D60A1 /* libPods-RNTesterUnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D4E0A9AD185CE086FAC9BD09 /* libPods-RNTesterUnitTests.a */; }; E7C1241A22BEC44B00DA25C0 /* RNTesterIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E7C1241922BEC44B00DA25C0 /* RNTesterIntegrationTests.m */; }; @@ -101,6 +102,7 @@ 65B6EA573FF170102920BEF4 /* libPods-RNTesterIntegrationTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNTesterIntegrationTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 8145AE05241172D900A3F8DA /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = RNTester/LaunchScreen.storyboard; sourceTree = ""; }; 8A0B7257DD8B2945456B0F61 /* Pods-RNTesterUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterUnitTests.release.xcconfig"; path = "Target Support Files/Pods-RNTesterUnitTests/Pods-RNTesterUnitTests.release.xcconfig"; sourceTree = ""; }; + CD10C7A4290BD4EB0033E1ED /* RCTEventEmitterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTEventEmitterTests.m; sourceTree = ""; }; D4E0A9AD185CE086FAC9BD09 /* libPods-RNTesterUnitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RNTesterUnitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; E771AEEA22B44E3100EA1189 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = RNTester/Info.plist; sourceTree = ""; }; E7C1241922BEC44B00DA25C0 /* RNTesterIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNTesterIntegrationTests.m; sourceTree = ""; }; @@ -329,6 +331,7 @@ E7DB20A022B2BA84005AC45F /* RNTesterUnitTests */ = { isa = PBXGroup; children = ( + CD10C7A4290BD4EB0033E1ED /* RCTEventEmitterTests.m */, E7DB20A322B2BA84005AC45F /* Info.plist */, E7DB20C622B2BAA5005AC45F /* RCTAllocationTests.m */, E7DB20B222B2BAA4005AC45F /* RCTAnimationUtilsTests.m */, @@ -774,6 +777,7 @@ E7DB20D722B2BAA6005AC45F /* RCTModuleInitTests.m in Sources */, E7DB20E522B2BAA6005AC45F /* RCTDevMenuTests.m in Sources */, E7DB20DE22B2BAA6005AC45F /* RCTUnicodeDecodeTests.m in Sources */, + CD10C7A5290BD4EB0033E1ED /* RCTEventEmitterTests.m in Sources */, E7DB20E422B2BAA6005AC45F /* RCTFormatErrorTests.m in Sources */, E7DB20EB22B2BAA6005AC45F /* RCTConvert_YGValueTests.m in Sources */, E7DB20E922B2BAA6005AC45F /* RCTComponentPropsTests.m in Sources */, diff --git a/packages/rn-tester/RNTesterUnitTests/RCTEventEmitterTests.m b/packages/rn-tester/RNTesterUnitTests/RCTEventEmitterTests.m new file mode 100644 index 00000000000000..43db9d015fdf84 --- /dev/null +++ b/packages/rn-tester/RNTesterUnitTests/RCTEventEmitterTests.m @@ -0,0 +1,72 @@ +/* + * 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 +#import + +#pragma mark - Faulty EventEmitter + +@interface RCTFaultyEventEmitter : RCTEventEmitter +@end + +@implementation RCTFaultyEventEmitter { + NSString *_capturedMessage; +} + +- (NSString *)capturedMessage +{ + return _capturedMessage; +} + +- (void)_log:(NSString *)message +{ + _capturedMessage = message; +} +@end + +#pragma mark - Proper EventEmitter + +@interface RCTProperEventEmitter : RCTEventEmitter +@end + +@implementation RCTProperEventEmitter + +- (NSArray *)supportedEvents +{ + return @[ @"myEvent" ]; +} +@end + +#pragma mark - Tests Code + +@interface RCTEventEmitterTests : XCTestCase + +@end + +@implementation RCTEventEmitterTests + +- (void)testEventEmitterSubclass_whenFaultySubclassInvokesSupportedEvents_raiseException +{ + RCTEventEmitter *emitter = [[RCTFaultyEventEmitter alloc] init]; + + NSArray *events = emitter.supportedEvents; + XCTAssertNil(events); + XCTAssertEqualObjects( + ((RCTFaultyEventEmitter *)emitter).capturedMessage, + @"RCTFaultyEventEmitter must implement the supportedEvents method"); +} + +- (void)testEventEmitterSubclass_whenProperSubclassInvokesSupportedEvents_itreturnsTheEvents +{ + RCTEventEmitter *emitter = [[RCTProperEventEmitter alloc] init]; + + NSArray *events = emitter.supportedEvents; + + XCTAssertEqualObjects(events, @[ @"myEvent" ]); +} + +@end