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
This commit is contained in:
Christoph Purrer
2024-07-01 09:36:51 -07:00
committed by Facebook GitHub Bot
parent 8b121c5071
commit f473a2f8f4
5 changed files with 79 additions and 3 deletions
@@ -7,8 +7,6 @@
#pragma once
#import <memory>
#import <Foundation/Foundation.h>
#import <React/RCTBridge.h>
@@ -16,6 +14,9 @@
#import <React/RCTModuleMethod.h>
#import <ReactCommon/CallInvoker.h>
#import <ReactCommon/TurboModule.h>
#import <react/bridging/bridging.h>
#import <functional>
#import <memory>
#import <string>
#import <unordered_map>
@@ -27,12 +28,21 @@ namespace facebook::react {
class CallbackWrapper;
class Instance;
using EventEmitterCallback = std::function<void(const std::string &, id)>;
namespace TurboModuleConvertUtils {
jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value);
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker);
} // namespace TurboModuleConvertUtils
template <>
struct Bridging<id> {
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 <NSObject>
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params;
@optional
- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper;
@end
/**
@@ -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<RCTTurboModule>)instance_ setEventEmitterCallback:wrapper];
}
}
} // namespace react
} // namespace facebook
@implementation EventEmitterCallbackWrapper
@end
@@ -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 {
@@ -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<AsyncEventEmitter<id>>();
eventEmitterMap_["onClick"] = std::make_shared<AsyncEventEmitter<id>>();
eventEmitterMap_["onChange"] = std::make_shared<AsyncEventEmitter<id>>();
eventEmitterMap_["onSubmit"] = std::make_shared<AsyncEventEmitter<id>>();
setEventEmitterCallback([&](const std::string &name, id value) {
static_cast<AsyncEventEmitter<id> &>(*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
@@ -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)