From f473a2f8f48ed8ce94a1f8a3181a5dabb4c6ad8d Mon Sep 17 00:00:00 2001 From: Christoph Purrer Date: Mon, 1 Jul 2024 09:36:51 -0700 Subject: [PATCH] Add ObjC Turbo Module Event Emitter example (#44914) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44914 Shows a proof of concept how '*strongly typed Turbo Module scoped*' `EventEmitters` can be used in a ObjC Turbo Module. ## Changelog: [iOS] [Added] - Add ObjC Turbo Module Event Emitter example Reviewed By: rshest Differential Revision: D57650830 fbshipit-source-id: c5c2dee4766484e9e58415e33c084999a9ae3bc6 --- .../platform/ios/ReactCommon/RCTTurboModule.h | 24 ++++++++++++-- .../ios/ReactCommon/RCTTurboModule.mm | 13 ++++++++ .../RCTNativeSampleTurboModuleSpec.h | 10 +++++- .../RCTNativeSampleTurboModuleSpec.mm | 31 +++++++++++++++++++ .../ios/ReactCommon/RCTSampleTurboModule.mm | 4 +++ 5 files changed, 79 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h index 97348d06416..f092e3377ad 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h @@ -7,8 +7,6 @@ #pragma once -#import - #import #import @@ -16,6 +14,9 @@ #import #import #import +#import +#import +#import #import #import @@ -27,12 +28,21 @@ namespace facebook::react { class CallbackWrapper; class Instance; +using EventEmitterCallback = std::function; namespace TurboModuleConvertUtils { jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value); id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr jsInvoker); } // namespace TurboModuleConvertUtils +template <> +struct Bridging { + static jsi::Value toJs(jsi::Runtime &rt, const id &value) + { + return TurboModuleConvertUtils::convertObjCObjectToJSIValue(rt, value); + } +}; + /** * ObjC++ specific TurboModule base class. */ @@ -64,6 +74,8 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { protected: void setMethodArgConversionSelector(NSString *methodName, size_t argIndex, NSString *fnName); + void setEventEmitterCallback(EventEmitterCallback eventEmitterCallback); + /** * Why is this virtual? * @@ -154,9 +166,17 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule { } // namespace facebook::react +@interface EventEmitterCallbackWrapper : NSObject { + @public + facebook::react::EventEmitterCallback _eventEmitterCallback; +} +@end + @protocol RCTTurboModule - (std::shared_ptr)getTurboModule: (const facebook::react::ObjCTurboModule::InitParams &)params; +@optional +- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper; @end /** diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm index 4abb7fca9be..2678b19063e 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm @@ -813,5 +813,18 @@ void ObjCTurboModule::setMethodArgConversionSelector(NSString *methodName, size_ methodArgConversionSelectors_[methodName][argIndex] = selectorValue; } +void ObjCTurboModule::setEventEmitterCallback(EventEmitterCallback eventEmitterCallback) +{ + if ([instance_ conformsToProtocol:@protocol(RCTTurboModule)] && + [instance_ respondsToSelector:@selector(setEventEmitterCallback:)]) { + EventEmitterCallbackWrapper *wrapper = [EventEmitterCallbackWrapper new]; + wrapper->_eventEmitterCallback = std::move(eventEmitterCallback); + [(id)instance_ setEventEmitterCallback:wrapper]; + } +} + } // namespace react } // namespace facebook + +@implementation EventEmitterCallbackWrapper +@end diff --git a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTNativeSampleTurboModuleSpec.h b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTNativeSampleTurboModuleSpec.h index 16bb3080528..e37e275e1eb 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTNativeSampleTurboModuleSpec.h +++ b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTNativeSampleTurboModuleSpec.h @@ -43,7 +43,15 @@ @end -@interface NativeSampleTurboModuleSpecBase : NSObject +@interface NativeSampleTurboModuleSpecBase : NSObject { + @protected + facebook::react::EventEmitterCallback _eventEmitterCallback; +} +- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *_Nonnull)eventEmitterCallbackWrapper; +- (void)emitOnPress; +- (void)emitOnClick:(NSString *)value; +- (void)emitOnChange:(NSDictionary *)value; +- (void)emitOnSubmit:(NSArray *)value; @end namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTNativeSampleTurboModuleSpec.mm b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTNativeSampleTurboModuleSpec.mm index dfb393190e7..5ad04fa341f 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTNativeSampleTurboModuleSpec.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTNativeSampleTurboModuleSpec.mm @@ -224,9 +224,40 @@ NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const ObjCTurboMo methodMap_["getObjectAssert"] = MethodMetadata{1, __hostFunction_NativeSampleTurboModuleSpecJSI_getObjectAssert}; methodMap_["promiseAssert"] = MethodMetadata{0, __hostFunction_NativeSampleTurboModuleSpecJSI_promiseAssert}; methodMap_["getConstants"] = MethodMetadata{0, __hostFunction_NativeSampleTurboModuleSpecJSI_getConstants}; + eventEmitterMap_["onPress"] = std::make_shared>(); + eventEmitterMap_["onClick"] = std::make_shared>(); + eventEmitterMap_["onChange"] = std::make_shared>(); + eventEmitterMap_["onSubmit"] = std::make_shared>(); + setEventEmitterCallback([&](const std::string &name, id value) { + static_cast &>(*eventEmitterMap_[name]).emit(value); + }); } } // namespace facebook::react @implementation NativeSampleTurboModuleSpecBase +- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *_Nonnull)eventEmitterCallbackWrapper +{ + _eventEmitterCallback = std::move(eventEmitterCallbackWrapper->_eventEmitterCallback); +} + +- (void)emitOnPress +{ + _eventEmitterCallback("onPress", nil); +} + +- (void)emitOnClick:(NSString *)value +{ + _eventEmitterCallback("onClick", value); +} + +- (void)emitOnChange:(NSDictionary *)value +{ + _eventEmitterCallback("onChange", value); +} + +- (void)emitOnSubmit:(NSArray *)value +{ + _eventEmitterCallback("onSubmit", value); +} @end diff --git a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm index c7d9666a390..2013c70a344 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm @@ -82,6 +82,10 @@ RCT_EXPORT_MODULE() RCT_EXPORT_METHOD(voidFunc) { // Nothing to do + [self emitOnPress]; + [self emitOnClick:@"click"]; + [self emitOnChange:@{@"a" : @1, @"b" : @"two"}]; + [self emitOnSubmit:@[ @{@"a" : @1, @"b" : @"two"}, @{@"a" : @3, @"b" : @"four"} ]]; } RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSNumber *, getBool : (BOOL)arg)