/* * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #import "FlipperReactNativeJavaScriptPluginManager.h" #import #import #import #import "FlipperModule.h" #import "FlipperReactNativeJavaScriptPlugin.h" static uint32_t FlipperResponderKeyGenerator = 0; @implementation FlipperReactNativeJavaScriptPluginManager { __weak FlipperClient *_flipperClient; NSMutableDictionary> *_responders; } + (instancetype)sharedInstance { static FlipperReactNativeJavaScriptPluginManager *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [self new]; }); return sharedInstance; } - (instancetype)init { if (self = [super init]) { _flipperClient = [FlipperClient sharedClient]; _responders = [NSMutableDictionary new]; } return self; } - (void)registerPluginWithModule:(FlipperModule *)module pluginId:(NSString *)pluginId inBackground:(BOOL)inBackground statusCallback:(RCTResponseSenderBlock)statusCallback { if (_flipperClient == nil) { // Flipper is not available in this build statusCallback(@[@"noflipper"]); return; } FlipperReactNativeJavaScriptPlugin *existingPlugin = [self jsPluginWithIdentifier:pluginId]; if (existingPlugin != nil) { existingPlugin.module = module; if (existingPlugin.isConnected) { [existingPlugin fireOnConnect]; } statusCallback(@[@"ok"]); return; } FlipperReactNativeJavaScriptPlugin *newPlugin = [[FlipperReactNativeJavaScriptPlugin alloc] initWithFlipperModule:module pluginId:pluginId inBackground:inBackground]; [_flipperClient addPlugin:newPlugin]; statusCallback(@[@"ok"]); } - (void)sendWithPluginId:(NSString *)pluginId method:(NSString *)method data:(NSString *)data { [[self jsPluginWithIdentifier:pluginId].connection send:method withParams:RCTJSONParse(data, NULL)]; } - (void)reportErrorWithMetadata:(NSString *)reason stackTrace:(NSString *)stackTrace pluginId:(NSString *)pluginId { [[self jsPluginWithIdentifier:pluginId].connection errorWithMessage:reason stackTrace:stackTrace]; } - (void)reportError:(NSString *)error pluginId:(NSString *)pluginId { // Stacktrace NSMutableArray *callstack = NSThread.callStackSymbols.mutableCopy; NSMutableString *callstackString = callstack.firstObject.mutableCopy; [callstack removeObject:callstack.firstObject]; for (NSString *stack in callstack) { [callstackString appendFormat:@"\n %@", stack]; } [[self jsPluginWithIdentifier:pluginId].connection errorWithMessage:error stackTrace:callstackString]; } - (void)subscribeWithModule:(FlipperModule *)module pluginId:(NSString *)pluginId method:(NSString *)method { __weak __typeof(self) weakSelf = self; [[self jsPluginWithIdentifier: pluginId].connection receive:method withBlock:^(NSDictionary * params, id responder) { __typeof(self) strongSelf = weakSelf; NSMutableDictionary *body = [NSMutableDictionary dictionaryWithDictionary:@{ @"plugin": pluginId, @"method": method, @"params": RCTJSONStringify(params, NULL), }]; if (responder != nil) { NSString *responderId = [NSString stringWithFormat:@"%d", FlipperResponderKeyGenerator++]; strongSelf->_responders[responderId] = responder; body[@"responderId"] = responderId; } [module sendEventWithName:@"react-native-flipper-receive-event" body:body]; }]; } - (void)respondSuccessWithResponderId:(NSString *)responderId data:(NSString *)data { id responder = _responders[responderId]; [responder success:RCTJSONParse(data, NULL)]; [_responders removeObjectForKey:responderId]; } - (void)respondErrorWithResponderId:(NSString *)responderId data:(NSString *)data { id responder = _responders[responderId]; [responder error:RCTJSONParse(data, NULL)]; [_responders removeObjectForKey:responderId]; } - (FlipperReactNativeJavaScriptPlugin *)jsPluginWithIdentifier:(NSString *)pluginId { return (FlipperReactNativeJavaScriptPlugin *)[_flipperClient pluginWithIdentifier:pluginId]; } @end