This commit is contained in:
Yamozha
2021-04-02 02:24:13 +03:00
parent c23950b545
commit 7256d79e2c
31493 changed files with 3036630 additions and 0 deletions

View File

@ -0,0 +1,22 @@
require 'json'
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
Pod::Spec.new do |s|
s.name = 'UMReactNativeAdapter'
s.version = package['version']
s.summary = package['description']
s.description = package['description']
s.license = package['license']
s.author = package['author']
s.homepage = package['homepage']
s.platform = :ios, '10.0'
s.source = { git: 'https://github.com/expo/expo.git' }
s.source_files = 'UMReactNativeAdapter/**/*.{h,m}'
s.preserve_paths = 'UMReactNativeAdapter/**/*.{h,m}'
s.requires_arc = true
s.dependency 'React-Core'
s.dependency 'UMCore'
s.dependency 'UMFontInterface'
end

View File

@ -0,0 +1,6 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMCore/UMInternalModule.h>
@interface UMReactFontManager : NSObject <UMInternalModule>
@end

View File

@ -0,0 +1,104 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMReactNativeAdapter/UMReactFontManager.h>
#import <UMFontInterface/UMFontProcessorInterface.h>
#import <React/RCTFont.h>
#import <UMFontInterface/UMFontManagerInterface.h>
#import <UMCore/UMAppLifecycleService.h>
#import <objc/runtime.h>
static dispatch_once_t initializeCurrentFontProcessorsOnce;
static NSPointerArray *currentFontProcessors;
@implementation RCTFont (UMReactFontManager)
+ (UIFont *)UMUpdateFont:(UIFont *)uiFont
withFamily:(NSString *)family
size:(NSNumber *)size
weight:(NSString *)weight
style:(NSString *)style
variant:(NSArray<NSDictionary *> *)variant
scaleMultiplier:(CGFloat)scaleMultiplier
{
UIFont *font;
for (id<UMFontProcessorInterface> fontProcessor in currentFontProcessors) {
font = [fontProcessor updateFont:uiFont withFamily:family size:size weight:weight style:style variant:variant scaleMultiplier:scaleMultiplier];
if (font) {
return font;
}
}
return [self UMUpdateFont:uiFont withFamily:family size:size weight:weight style:style variant:variant scaleMultiplier:scaleMultiplier];
}
@end
/**
* This class is responsible for allowing other modules to register as font processors in React Native.
*
* A font processor is an object conforming to UMFontProcessorInterface and is capable of
* providing an instance of UIFont for given (family, size, weight, style, variant, scaleMultiplier).
*
* To be able to hook into React Native's way of processing fonts we:
* - add a new class method to RCTFont, `UMUpdateFont:withFamily:size:weight:style:variant:scaleMultiplier`
* with UMReactFontManager category.
* - add a new static variable `currentFontProcessors` holding an array of... font processors. This variable
* is shared between the RCTFont's category and UMReactFontManager class.
* - when UMReactFontManager is initialized, we exchange implementations of RCTFont.updateFont...
* and RCTFont.UMUpdateFont... After the class initialized, which happens only once, calling `RCTFont updateFont`
* calls in fact implementation we've defined up here and calling `RCTFont UMUpdateFont` falls back
* to the default implementation. (This is why we call `[self UMUpdateFont]` at the end of that function,
* though it seems like an endless loop, in fact we dispatch to another implementation.)
* - When some module adds a font processor using UMFontManagerInterface, UMReactFontManager adds a weak pointer to it
* to currentFontProcessors array.
* - Implementation logic of `RCTFont.UMUpdateFont` uses current value of currentFontProcessors when processing arguments.
*/
@interface UMReactFontManager ()
@property (nonatomic, strong) NSMutableSet *fontProcessors;
@end
@implementation UMReactFontManager
UM_REGISTER_MODULE();
- (instancetype)init
{
if (self = [super init]) {
_fontProcessors = [NSMutableSet set];
}
return self;
}
+ (const NSArray<Protocol *> *)exportedInterfaces
{
return @[@protocol(UMFontManagerInterface)];
}
+ (void)initialize
{
dispatch_once(&initializeCurrentFontProcessorsOnce, ^{
currentFontProcessors = [NSPointerArray weakObjectsPointerArray];
});
Class rtcClass = [RCTFont class];
SEL rtcUpdate = @selector(updateFont:withFamily:size:weight:style:variant:scaleMultiplier:);
SEL exUpdate = @selector(UMUpdateFont:withFamily:size:weight:style:variant:scaleMultiplier:);
method_exchangeImplementations(class_getClassMethod(rtcClass, rtcUpdate),
class_getClassMethod(rtcClass, exUpdate));
}
# pragma mark - UMFontManager
- (void)addFontProcessor:(id<UMFontProcessorInterface>)processor
{
[_fontProcessors addObject:processor];
[currentFontProcessors compact];
[currentFontProcessors addPointer:(__bridge void * _Nullable)(processor)];
}
@end

View File

@ -0,0 +1,8 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMCore/UMSingletonModule.h>
#import <UMCore/UMLogHandler.h>
@interface UMReactLogHandler : UMSingletonModule <UMLogHandler>
@end

View File

@ -0,0 +1,27 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMReactNativeAdapter/UMReactLogHandler.h>
#import <UMCore/UMDefines.h>
#import <React/RCTLog.h>
@implementation UMReactLogHandler
UM_REGISTER_SINGLETON_MODULE(ReactLogHandler);
- (void)error:(NSString *)message {
RCTLogError(@"%@", message);
}
- (void)fatal:(NSError *)error {
RCTFatal(error);
}
- (void)info:(NSString *)message {
RCTLogInfo(@"%@", message);
}
- (void)warn:(NSString *)message {
RCTLogWarn(@"%@", message);
}
@end

View File

@ -0,0 +1,13 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMCore/UMUIManager.h>
#import <UMCore/UMInternalModule.h>
#import <UMCore/UMAppLifecycleService.h>
#import <UMCore/UMAppLifecycleListener.h>
#import <UMCore/UMModuleRegistryConsumer.h>
#import <UMCore/UMJavaScriptContextProvider.h>
#import <UMReactNativeAdapter/UMBridgeModule.h>
@interface UMReactNativeAdapter : NSObject <UMInternalModule, UMBridgeModule, UMAppLifecycleService, UMUIManager, UMJavaScriptContextProvider, UMModuleRegistryConsumer>
@end

View File

@ -0,0 +1,302 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <JavaScriptCore/JavaScriptCore.h>
#import <UMReactNativeAdapter/UMReactNativeAdapter.h>
#import <React/RCTUIManager.h>
#import <React/RCTBridge+Private.h>
#import <React/RCTAppState.h>
#import <React/RCTImageLoader.h>
@interface UMReactNativeAdapter ()
@property (nonatomic, weak) RCTBridge *bridge;
@property (nonatomic, assign) BOOL isForegrounded;
@property (nonatomic, strong) NSPointerArray *lifecycleListeners;
@end
@interface RCTBridge ()
- (JSGlobalContextRef)jsContextRef;
- (void *)runtime;
- (void)dispatchBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue;
@end
@implementation UMReactNativeAdapter
UM_REGISTER_MODULE();
+ (NSString *)moduleName
{
return nil;
}
+ (const NSArray<Protocol *> *)exportedInterfaces
{
return @[@protocol(UMAppLifecycleService), @protocol(UMUIManager), @protocol(UMJavaScriptContextProvider)];
}
# pragma mark - Lifecycle methods
- (instancetype)init
{
if (self = [super init]) {
_isForegrounded = false;
_lifecycleListeners = [NSPointerArray weakObjectsPointerArray];
}
return self;
}
- (void)setModuleRegistry:(UMModuleRegistry *)moduleRegistry
{
if (moduleRegistry) {
[self startObserving];
}
}
- (void)dealloc
{
[self stopObserving];
}
# pragma mark - Public API
- (void)addUIBlock:(void (^)(NSDictionary<id, UIView *> *))block
{
__weak UMReactNativeAdapter *weakSelf = self;
dispatch_async(_bridge.uiManager.methodQueue, ^{
__strong UMReactNativeAdapter *strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
block(viewRegistry);
}];
}
});
}
- (void)addUIBlock:(void (^)(id))block forView:(id)viewId ofClass:(Class)klass
{
[self addUIBlock:^(UIView *view) {
if (![view isKindOfClass:klass]) {
block(nil);
} else {
block(view);
}
} forView:viewId];
}
- (void)addUIBlock:(void (^)(id))block forView:(id)viewId implementingProtocol:(Protocol *)protocol
{
[self addUIBlock:^(UIView *view) {
if (![view.class conformsToProtocol:protocol]) {
block(nil);
} else {
block(view);
}
} forView:viewId];
}
- (void)dispatchOnClientThread:(dispatch_block_t)block
{
[self.bridge dispatchBlock:block queue:RCTJSThread];
}
- (void)executeUIBlock:(void (^)(NSDictionary<id,UIView *> *))block {
__weak UMReactNativeAdapter *weakSelf = self;
dispatch_async(_bridge.uiManager.methodQueue, ^{
__strong UMReactNativeAdapter *strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
block(viewRegistry);
}];
[strongSelf.bridge.uiManager setNeedsLayout];
}
});
}
- (void)executeUIBlock:(void (^)(id))block forView:(id)viewId implementingProtocol:(Protocol *)protocol {
[self executeUIBlock:^(UIView *view) {
if (![view.class conformsToProtocol:protocol]) {
block(nil);
} else {
block(view);
}
} forView:viewId];
}
- (void)executeUIBlock:(void (^)(id))block forView:(id)viewId ofClass:(Class)klass {
[self executeUIBlock:^(UIView *view) {
if (![view isKindOfClass:klass]) {
block(nil);
} else {
block(view);
}
} forView:viewId];
}
- (void)setBridge:(RCTBridge *)bridge
{
_bridge = bridge;
}
- (void)registerAppLifecycleListener:(id<UMAppLifecycleListener>)listener
{
[_lifecycleListeners addPointer:(__bridge void * _Nullable)(listener)];
}
- (void)unregisterAppLifecycleListener:(id<UMAppLifecycleListener>)listener
{
for (int i = 0; i < _lifecycleListeners.count; i++) {
id pointer = [_lifecycleListeners pointerAtIndex:i];
if (pointer == (__bridge void * _Nullable)(listener) || !pointer) {
[_lifecycleListeners removePointerAtIndex:i];
i--;
}
}
// -(void)compact doesn't work, that's why we have this `|| !pointer` above
// http://www.openradar.me/15396578
[_lifecycleListeners compact];
}
# pragma mark - UMJavaScriptContextProvider
- (JSGlobalContextRef)javaScriptContextRef
{
if ([_bridge respondsToSelector:@selector(jsContextRef)]) {
return _bridge.jsContextRef;
} else if (_bridge.runtime) {
// In react-native 0.59 vm is abstracted by JSI and all JSC specific references are removed
// To access jsc context we are extracting specific offset in jsi::Runtime, JSGlobalContextRef
// is first field inside Runtime class and in memory it's preceded only by pointer to virtual method table.
// WARNING: This is temporary solution that may break with new react-native releases.
return *(((JSGlobalContextRef *)(_bridge.runtime)) + 1);
}
return nil;
}
- (void *)javaScriptRuntimePointer
{
if ([_bridge respondsToSelector:@selector(runtime)]) {
return _bridge.runtime;
} else {
return nil;
}
}
# pragma mark - App state observing
- (void)startObserving
{
for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
UIApplicationDidEnterBackgroundNotification,
UIApplicationDidFinishLaunchingNotification,
UIApplicationWillResignActiveNotification,
UIApplicationWillEnterForegroundNotification,
RCTContentDidAppearNotification,
RCTBridgeWillReloadNotification]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleAppStateDidChange:)
name:name
object:nil];
}
}
- (void)handleAppStateDidChange:(NSNotification *)notification
{
if ([notification.name isEqualToString:RCTContentDidAppearNotification]) {
[self notifyAboutContentDidAppear];
} else if ([notification.name isEqualToString:RCTBridgeWillReloadNotification]) {
[self notifyAboutContentWillReload];
} else if (
_isForegrounded && (
[notification.name isEqualToString:UIApplicationWillResignActiveNotification] ||
[notification.name isEqualToString:UIApplicationWillEnterForegroundNotification] ||
RCTSharedApplication().applicationState == UIApplicationStateBackground
)
) {
[self setAppStateToBackground];
} else if (!_isForegrounded && RCTSharedApplication().applicationState == UIApplicationStateActive) {
[self setAppStateToForeground];
}
}
- (void)setAppStateToBackground
{
if (_isForegrounded) {
[[_lifecycleListeners allObjects] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj onAppBackgrounded];
}];
_isForegrounded = false;
}
}
- (void)setAppStateToForeground
{
if (!_isForegrounded) {
[[_lifecycleListeners allObjects] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj onAppForegrounded];
}];
_isForegrounded = true;
}
}
- (void)notifyAboutContentDidAppear
{
[[_lifecycleListeners allObjects] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj respondsToSelector:@selector(onAppContentDidAppear)]) {
[obj performSelector:@selector(onAppContentDidAppear)];
}
}];
}
- (void)notifyAboutContentWillReload
{
[[_lifecycleListeners allObjects] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj respondsToSelector:@selector(onAppContentWillReload)]) {
[obj performSelector:@selector(onAppContentWillReload)];
}
}];
}
- (void)stopObserving
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
# pragma mark - Internal methods
- (void)addUIBlock:(void (^)(UIView *view))block forView:(id)viewId
{
__weak UMReactNativeAdapter *weakSelf = self;
dispatch_async(_bridge.uiManager.methodQueue, ^{
__strong UMReactNativeAdapter *strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[viewId];
block(view);
}];
}
});
}
- (void)executeUIBlock:(void (^)(UIView *view))block forView:(id)viewId
{
__weak UMReactNativeAdapter *weakSelf = self;
dispatch_async(_bridge.uiManager.methodQueue, ^{
__strong UMReactNativeAdapter *strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[viewId];
block(view);
}];
[strongSelf.bridge.uiManager setNeedsLayout];
}
});
}
@end

View File

@ -0,0 +1,11 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMCore/UMInternalModule.h>
#import <UMCore/UMEventEmitterService.h>
#import <UMCore/UMModuleRegistryConsumer.h>
#import <UMReactNativeAdapter/UMBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface UMReactNativeEventEmitter : RCTEventEmitter <UMInternalModule, UMBridgeModule, UMModuleRegistryConsumer, UMEventEmitterService>
@end

View File

@ -0,0 +1,145 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMReactNativeAdapter/UMReactNativeEventEmitter.h>
#import <UMCore/UMEventEmitter.h>
#import <UMCore/UMExportedModule.h>
#import <UMCore/UMModuleRegistry.h>
@interface UMReactNativeEventEmitter ()
@property (nonatomic, assign) int listenersCount;
@property (nonatomic, weak) UMModuleRegistry *moduleRegistry;
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *modulesListenersCounts;
@end
@implementation UMReactNativeEventEmitter
- (instancetype)init
{
if (self = [super init]) {
_listenersCount = 0;
_modulesListenersCounts = [NSMutableDictionary dictionary];
}
return self;
}
UM_REGISTER_MODULE();
+ (NSString *)moduleName
{
return @"UMReactNativeEventEmitter";
}
+ (const NSArray<Protocol *> *)exportedInterfaces
{
return @[@protocol(UMEventEmitterService)];
}
- (NSArray<NSString *> *)supportedEvents
{
NSMutableSet<NSString *> *eventsAccumulator = [NSMutableSet set];
for (UMExportedModule *exportedModule in [_moduleRegistry getAllExportedModules]) {
if ([exportedModule conformsToProtocol:@protocol(UMEventEmitter)]) {
id<UMEventEmitter> eventEmitter = (id<UMEventEmitter>)exportedModule;
[eventsAccumulator addObjectsFromArray:[eventEmitter supportedEvents]];
}
}
return [eventsAccumulator allObjects];
}
RCT_EXPORT_METHOD(addProxiedListener:(NSString *)moduleName eventName:(NSString *)eventName)
{
[self addListener:eventName];
// Validate module
UMExportedModule *module = [_moduleRegistry getExportedModuleForName:moduleName];
if (RCT_DEBUG && module == nil) {
UMLogError(@"Module for name `%@` has not been found.", moduleName);
return;
} else if (RCT_DEBUG && ![module conformsToProtocol:@protocol(UMEventEmitter)]) {
UMLogError(@"Module `%@` is not an UMEventEmitter, thus it cannot be subscribed to.", moduleName);
return;
}
// Validate eventEmitter
id<UMEventEmitter> eventEmitter = (id<UMEventEmitter>)module;
if (RCT_DEBUG && ![[eventEmitter supportedEvents] containsObject:eventName]) {
UMLogError(@"`%@` is not a supported event type for %@. Supported events are: `%@`",
eventName, moduleName, [[eventEmitter supportedEvents] componentsJoinedByString:@"`, `"]);
}
// Global observing state
_listenersCount += 1;
if (_listenersCount == 1) {
[self startObserving];
}
// Per-module observing state
int newModuleListenersCount = [self moduleListenersCountFor:moduleName] + 1;
if (newModuleListenersCount == 1) {
[eventEmitter startObserving];
}
_modulesListenersCounts[moduleName] = [NSNumber numberWithInt:newModuleListenersCount];
}
RCT_EXPORT_METHOD(removeProxiedListeners:(NSString *)moduleName count:(double)count)
{
[self removeListeners:count];
// Validate module
UMExportedModule *module = [_moduleRegistry getExportedModuleForName:moduleName];
if (RCT_DEBUG && module == nil) {
UMLogError(@"Module for name `%@` has not been found.", moduleName);
return;
} else if (RCT_DEBUG && ![module conformsToProtocol:@protocol(UMEventEmitter)]) {
UMLogError(@"Module `%@` is not an UMEventEmitter, thus it cannot be subscribed to.", moduleName);
return;
}
id<UMEventEmitter> eventEmitter = (id<UMEventEmitter>)module;
// Per-module observing state
int newModuleListenersCount = [self moduleListenersCountFor:moduleName] - count;
if (newModuleListenersCount == 0) {
[eventEmitter stopObserving];
} else if (newModuleListenersCount < 0) {
UMLogError(@"Attempted to remove more `%@` listeners than added", moduleName);
newModuleListenersCount = 0;
}
_modulesListenersCounts[moduleName] = [NSNumber numberWithInt:newModuleListenersCount];
// Global observing state
if (_listenersCount - count < 0) {
UMLogError(@"Attempted to remove more proxied event emitter listeners than added");
_listenersCount = 0;
} else {
_listenersCount -= count;
}
if (_listenersCount == 0) {
[self stopObserving];
}
}
# pragma mark Utilities
- (int)moduleListenersCountFor:(NSString *)moduleName
{
NSNumber *moduleListenersCountNumber = _modulesListenersCounts[moduleName];
int moduleListenersCount = 0;
if (moduleListenersCountNumber != nil) {
moduleListenersCount = [moduleListenersCountNumber intValue];
}
return moduleListenersCount;
}
# pragma mark - UMModuleRegistryConsumer
- (void)setModuleRegistry:(UMModuleRegistry *)moduleRegistry
{
_moduleRegistry = moduleRegistry;
}
@end

View File

@ -0,0 +1,20 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <React/RCTBridgeModule.h>
// Escape hatch for modules that both have to depend on React Native
// and want to be exported as an internal universal module.
#define UM_RCT_REGISTER_MODULE(external_name) \
+ (const NSString *)moduleName { return @#external_name; } \
UM_EXPORT_MODULE_WITH_CUSTOM_LOAD(external_name, \
RCT_EXTERN void RCTRegisterModule(Class); \
RCTRegisterModule(self); \
)
@protocol UMBridgeModule <RCTBridgeModule>
@optional
- (void)setBridge:(RCTBridge *)bridge;
@end

View File

@ -0,0 +1,21 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <UMCore/UMModuleRegistryProvider.h>
// An "adapter" over module registry, for given RCTBridge and NSString
// is able to provide an array of exported RCTBridgeModules. Override
// it and use in your AppDelegate to export different bridge modules
// for different experiences.
@interface UMModuleRegistryAdapter : NSObject
@property (nonatomic, readonly) UMModuleRegistryProvider *moduleRegistryProvider;
- (instancetype)initWithModuleRegistryProvider:(UMModuleRegistryProvider *)moduleRegistryProvider;
- (NSArray<id<RCTBridgeModule>> *)extraModulesForModuleRegistry:(UMModuleRegistry *)moduleRegistry;
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge;
@end

View File

@ -0,0 +1,70 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMReactNativeAdapter/UMNativeModulesProxy.h>
#import <UMReactNativeAdapter/UMViewManagerAdapter.h>
#import <UMReactNativeAdapter/UMModuleRegistryAdapter.h>
#import <UMReactNativeAdapter/UMViewManagerAdapterClassesRegistry.h>
#import <UMReactNativeAdapter/UMModuleRegistryHolderReactModule.h>
@interface UMModuleRegistryAdapter ()
@property (nonatomic, strong) UMModuleRegistryProvider *moduleRegistryProvider;
@property (nonatomic, strong) UMViewManagerAdapterClassesRegistry *viewManagersClassesRegistry;
@end
@implementation UMModuleRegistryAdapter
- (instancetype)initWithModuleRegistryProvider:(UMModuleRegistryProvider *)moduleRegistryProvider
{
if (self = [super init]) {
_moduleRegistryProvider = moduleRegistryProvider;
_viewManagersClassesRegistry = [[UMViewManagerAdapterClassesRegistry alloc] init];
}
return self;
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
return [self extraModulesForModuleRegistry:[_moduleRegistryProvider moduleRegistry]];
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForModuleRegistry:(UMModuleRegistry *)moduleRegistry
{
NSMutableArray<id<RCTBridgeModule>> *extraModules = [NSMutableArray array];
UMNativeModulesProxy *nativeModulesProxy = [[UMNativeModulesProxy alloc] initWithModuleRegistry:moduleRegistry];
[extraModules addObject:nativeModulesProxy];
for (UMViewManager *viewManager in [moduleRegistry getAllViewManagers]) {
Class viewManagerAdapterClass = [_viewManagersClassesRegistry viewManagerAdapterClassForViewManager:viewManager];
[extraModules addObject:[[viewManagerAdapterClass alloc] initWithViewManager:viewManager]];
}
// Silence React Native warning `Base module "%s" does not exist`
// occurring when view manager class is subclassing another class
// that is not RCTViewManager (in our case all the view manager adapters
// subclass UMViewManagerAdapter, so RN expects to find UMViewManagerAdapter
// exported.
[extraModules addObject:[[UMViewManagerAdapter alloc] init]];
// It is possible that among internal modules there are some RCTBridgeModules --
// let's add them to extraModules here.
for (id<UMInternalModule> module in [moduleRegistry getAllInternalModules]) {
if ([module conformsToProtocol:@protocol(RCTBridgeModule)]) {
id<RCTBridgeModule> reactBridgeModule = (id<RCTBridgeModule>)module;
[extraModules addObject:reactBridgeModule];
}
}
// Adding the way to access the module registry from RCTBridgeModules.
[extraModules addObject:[[UMModuleRegistryHolderReactModule alloc] initWithModuleRegistry:moduleRegistry]];
// One could add some modules to the Module Registry after creating it.
// Here is our last call for finalizing initialization.
[moduleRegistry initialize];
return extraModules;
}
@end

View File

@ -0,0 +1,12 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <Foundation/Foundation.h>
#import <UMCore/UMModuleRegistry.h>
#import <React/RCTBridgeModule.h>
@interface UMModuleRegistryHolderReactModule : NSObject <RCTBridgeModule>
- (instancetype)initWithModuleRegistry:(UMModuleRegistry *)moduleRegistry;
- (UMModuleRegistry *)moduleRegistry;
@end

View File

@ -0,0 +1,30 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMReactNativeAdapter/UMModuleRegistryHolderReactModule.h>
@interface UMModuleRegistryHolderReactModule ()
@property (nonatomic, weak) UMModuleRegistry *moduleRegistry;
@end
@implementation UMModuleRegistryHolderReactModule
- (instancetype)initWithModuleRegistry:(UMModuleRegistry *)moduleRegistry
{
if (self = [super init]) {
_moduleRegistry = moduleRegistry;
}
return self;
}
- (UMModuleRegistry *)moduleRegistry
{
return _moduleRegistry;
}
+ (NSString *)moduleName {
return nil;
}
@end

View File

@ -0,0 +1,15 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <React/RCTViewManager.h>
#import <UMCore/UMViewManager.h>
// A registry for view manager adapter classes.
// As we have to create subclasses of UMViewManagerAdapters
// at runtime to be able to respond with proper + (NSString *)moduleName
// to React, let's cache these classes and not create them twice.
@interface UMViewManagerAdapterClassesRegistry : NSObject
- (Class)viewManagerAdapterClassForViewManager:(UMViewManager *)viewManager;
@end

View File

@ -0,0 +1,57 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMReactNativeAdapter/UMViewManagerAdapterClassesRegistry.h>
#import <UMReactNativeAdapter/UMViewManagerAdapter.h>
#import <objc/runtime.h>
static const NSString *viewManagerAdapterModuleNamePrefix = @"ViewManagerAdapter_";
static IMP directEventBlockImplementation = nil;
static dispatch_once_t directEventBlockImplementationOnceToken;
@interface UMViewManagerAdapterClassesRegistry ()
@property (nonatomic, strong) NSMutableDictionary<Class, Class> *viewManagerAdaptersClasses;
@end
@implementation UMViewManagerAdapterClassesRegistry
- (instancetype)init
{
if (self = [super init]) {
_viewManagerAdaptersClasses = [NSMutableDictionary dictionary];
}
return self;
}
- (Class)viewManagerAdapterClassForViewManager:(UMViewManager *)viewManager
{
Class viewManagerClass = [viewManager class];
if (_viewManagerAdaptersClasses[viewManagerClass] == nil) {
_viewManagerAdaptersClasses[(id <NSCopying>)viewManagerClass] = [self _createViewManagerAdapterClassForViewManager:viewManager];
}
return _viewManagerAdaptersClasses[viewManagerClass];
}
- (Class)_createViewManagerAdapterClassForViewManager:(UMViewManager *)viewManager
{
const char *viewManagerClassName = [[viewManagerAdapterModuleNamePrefix stringByAppendingString:[viewManager viewName]] UTF8String];
Class viewManagerAdapterClass = objc_allocateClassPair([UMViewManagerAdapter class], viewManagerClassName, 0);
[self _ensureDirectEventBlockImplementationIsPresent];
for (NSString *eventName in [viewManager supportedEvents]) {
class_addMethod(object_getClass(viewManagerAdapterClass), NSSelectorFromString([@"propConfig_" stringByAppendingString:eventName]), directEventBlockImplementation, "@@:");
}
return viewManagerAdapterClass;
}
- (void)_ensureDirectEventBlockImplementationIsPresent
{
dispatch_once(&directEventBlockImplementationOnceToken, ^{
directEventBlockImplementation = imp_implementationWithBlock(^{
return @[@"RCTDirectEventBlock"];
});
});
}
@end

View File

@ -0,0 +1,16 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <React/RCTBridgeModule.h>
#import <UMCore/UMInternalModule.h>
#import <UMCore/UMModuleRegistry.h>
// RCTBridgeModule capable of receiving method calls from JS and forwarding them
// to proper exported universal modules. Also, it exports important constants to JS, like
// properties of exported methods and modules' constants.
@interface UMNativeModulesProxy : NSObject <RCTBridgeModule>
- (instancetype)initWithModuleRegistry:(UMModuleRegistry *)moduleRegistry;
@end

View File

@ -0,0 +1,160 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMReactNativeAdapter/UMNativeModulesProxy.h>
#import <objc/runtime.h>
#import <React/RCTLog.h>
#import <UMCore/UMEventEmitter.h>
#import <UMCore/UMViewManager.h>
#import <UMReactNativeAdapter/UMViewManagerAdapter.h>
#import <UMReactNativeAdapter/UMViewManagerAdapterClassesRegistry.h>
static const NSString *exportedMethodsNamesKeyPath = @"exportedMethods";
static const NSString *viewManagersNamesKeyPath = @"viewManagersNames";
static const NSString *exportedConstantsKeyPath = @"modulesConstants";
static const NSString *methodInfoKeyKey = @"key";
static const NSString *methodInfoNameKey = @"name";
static const NSString *methodInfoArgumentsCountKey = @"argumentsCount";
@interface UMNativeModulesProxy ()
@property (nonatomic, strong) NSRegularExpression *regexp;
@property (nonatomic, strong) UMModuleRegistry *moduleRegistry;
@property (nonatomic, strong) NSMutableDictionary<const NSString *, NSMutableDictionary<NSString *, NSNumber *> *> *exportedMethodsKeys;
@property (nonatomic, strong) NSMutableDictionary<const NSString *, NSMutableDictionary<NSNumber *, NSString *> *> *exportedMethodsReverseKeys;
@end
@implementation UMNativeModulesProxy
- (instancetype)initWithModuleRegistry:(UMModuleRegistry *)moduleRegistry
{
if (self = [super init]) {
_moduleRegistry = moduleRegistry;
_exportedMethodsKeys = [NSMutableDictionary dictionary];
_exportedMethodsReverseKeys = [NSMutableDictionary dictionary];
}
return self;
}
+ (const NSString *)moduleName
{
return @"NativeUnimoduleProxy";
}
# pragma mark - React API
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (NSDictionary *)constantsToExport
{
NSMutableDictionary <NSString *, id> *exportedModulesConstants = [NSMutableDictionary dictionary];
// Grab all the constants exported by modules
for (UMExportedModule *exportedModule in [_moduleRegistry getAllExportedModules]) {
@try {
exportedModulesConstants[[[exportedModule class] exportedModuleName]] = [exportedModule constantsToExport] ?: [NSNull null];
} @catch (NSException *exception) {
continue;
}
}
// Also add `exportedMethodsNames`
NSMutableDictionary<const NSString *, NSMutableArray<NSMutableDictionary<const NSString *, id> *> *> *exportedMethodsNamesAccumulator = [NSMutableDictionary dictionary];
for (UMExportedModule *exportedModule in [_moduleRegistry getAllExportedModules]) {
const NSString *exportedModuleName = [[exportedModule class] exportedModuleName];
exportedMethodsNamesAccumulator[exportedModuleName] = [NSMutableArray array];
[[exportedModule getExportedMethods] enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull exportedName, NSString * _Nonnull selectorName, BOOL * _Nonnull stop) {
NSMutableDictionary<const NSString *, id> *methodInfo = [NSMutableDictionary dictionaryWithDictionary:@{
methodInfoNameKey: exportedName,
// - 3 is for resolver and rejecter of the promise and the last, empty component
methodInfoArgumentsCountKey: @([[selectorName componentsSeparatedByString:@":"] count] - 3)
}];
[exportedMethodsNamesAccumulator[exportedModuleName] addObject:methodInfo];
}];
[self assignExportedMethodsKeys:exportedMethodsNamesAccumulator[exportedModuleName] forModuleName:exportedModuleName];
}
// Also, add `viewManagersNames` for sanity check and testing purposes -- with names we know what managers to mock on UIManager
NSArray<UMViewManager *> *viewManagers = [_moduleRegistry getAllViewManagers];
NSMutableArray<NSString *> *viewManagersNames = [NSMutableArray arrayWithCapacity:[viewManagers count]];
for (UMViewManager *viewManager in viewManagers) {
[viewManagersNames addObject:[viewManager viewName]];
}
NSMutableDictionary <NSString *, id> *constantsAccumulator = [NSMutableDictionary dictionary];
constantsAccumulator[viewManagersNamesKeyPath] = viewManagersNames;
constantsAccumulator[exportedConstantsKeyPath] = exportedModulesConstants;
constantsAccumulator[exportedMethodsNamesKeyPath] = exportedMethodsNamesAccumulator;
return constantsAccumulator;
}
RCT_EXPORT_METHOD(callMethod:(NSString *)moduleName methodNameOrKey:(id)methodNameOrKey arguments:(NSArray *)arguments resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
UMExportedModule *module = [_moduleRegistry getExportedModuleForName:moduleName];
if (module == nil) {
NSString *reason = [NSString stringWithFormat:@"No exported module was found for name '%@'. Are you sure all the packages are linked correctly?", moduleName];
reject(@"E_NO_MODULE", reason, nil);
return;
}
if (!methodNameOrKey) {
reject(@"E_NO_METHOD", @"No method key or name provided", nil);
return;
}
NSString *methodName;
if ([methodNameOrKey isKindOfClass:[NSString class]]) {
methodName = (NSString *)methodNameOrKey;
} else if ([methodNameOrKey isKindOfClass:[NSNumber class]]) {
methodName = _exportedMethodsReverseKeys[moduleName][(NSNumber *)methodNameOrKey];
} else {
reject(@"E_INV_MKEY", @"Method key is neither a String nor an Integer -- don't know how to map it to method name.", nil);
return;
}
dispatch_async([module methodQueue], ^{
@try {
[module callExportedMethod:methodName withArguments:arguments resolver:resolve rejecter:reject];
} @catch (NSException *e) {
NSString *message = [NSString stringWithFormat:@"An exception was thrown while calling `%@.%@` with arguments `%@`: %@", moduleName, methodName, arguments, e];
reject(@"E_EXC", message, nil);
}
});
}
- (void)assignExportedMethodsKeys:(NSMutableArray<NSMutableDictionary<const NSString *, id> *> *)exportedMethods forModuleName:(const NSString *)moduleName
{
if (!_exportedMethodsKeys[moduleName]) {
_exportedMethodsKeys[moduleName] = [NSMutableDictionary dictionary];
}
if (!_exportedMethodsReverseKeys[moduleName]) {
_exportedMethodsReverseKeys[moduleName] = [NSMutableDictionary dictionary];
}
for (int i = 0; i < [exportedMethods count]; i++) {
NSMutableDictionary<const NSString *, id> *methodInfo = exportedMethods[i];
if (!methodInfo[(NSString *)methodInfoNameKey] || ![methodInfo[methodInfoNameKey] isKindOfClass:[NSString class]]) {
NSString *reason = [NSString stringWithFormat:@"Method info of a method of module %@ has no method name.", moduleName];
@throw [NSException exceptionWithName:@"Empty method name in method info" reason:reason userInfo:nil];
}
NSString *methodName = methodInfo[(NSString *)methodInfoNameKey];
NSNumber *previousMethodKey = _exportedMethodsKeys[moduleName][methodName];
if (previousMethodKey) {
methodInfo[methodInfoKeyKey] = previousMethodKey;
} else {
NSNumber *newKey = @([[_exportedMethodsKeys[moduleName] allValues] count]);
methodInfo[methodInfoKeyKey] = newKey;
_exportedMethodsKeys[moduleName][methodName] = newKey;
_exportedMethodsReverseKeys[moduleName][newKey] = methodName;
}
}
}
@end

View File

@ -0,0 +1,19 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <React/RCTViewManager.h>
#import <UMCore/UMViewManager.h>
#import <UMReactNativeAdapter/UMBridgeModule.h>
// UMViewManagerAdapter is an RN wrapper around UMCore's UMViewManager.
// For each exported view manager is it subclassed so that React Native
// can get proper module name (which is returned by a class method).
//
// Instead of instantiating the subclass by yourself,
// use UMViewManagerAdapterClassesRegistry's
// viewManagerAdapterClassForViewManager:.
@interface UMViewManagerAdapter : RCTViewManager
- (instancetype)initWithViewManager:(UMViewManager *)viewManager;
@end

View File

@ -0,0 +1,53 @@
// Copyright 2018-present 650 Industries. All rights reserved.
#import <UMReactNativeAdapter/UMViewManagerAdapter.h>
@interface UMViewManagerAdapter ()
@property UMViewManager *viewManager;
@end
@implementation UMViewManagerAdapter
- (instancetype)initWithViewManager:(UMViewManager *)viewManager
{
if (self = [super init]) {
_viewManager = viewManager;
}
return self;
}
- (NSArray<NSString *> *)supportedEvents
{
return [_viewManager supportedEvents];
}
// This class is not used directly --- usually it's subclassed
// in runtime by UMNativeModulesProxy for each exported view manager.
// Each created class has different class name, conforming to convention.
// This way we can provide React Native with different RCTViewManagers
// returning different modules names.
+ (NSString *)moduleName
{
return NSStringFromClass(self);
}
- (UIView *)view
{
return [_viewManager view];
}
// The adapter multiplexes custom view properties in one "big object prop" that is passed here.
RCT_CUSTOM_VIEW_PROPERTY(proxiedProperties, NSDictionary, UIView)
{
__weak UMViewManagerAdapter *weakSelf = self;
[json enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
__strong UMViewManagerAdapter *strongSelf = weakSelf;
[strongSelf.viewManager updateProp:key withValue:obj onView:view];
}];
}
@end