Implement react-native-flipper on iOS (#795)
Summary: Implement the react-native-flipper native module on iOS. Uses very similar abstractions as on Android. ## Changelog [react-native-flipper] Support iOS Pull Request resolved: https://github.com/facebook/flipper/pull/795 Test Plan: Tested using the RN TicTacToe example app {F228406333} Reviewed By: mweststrate Differential Revision: D19853017 Pulled By: priteshrnandgaonkar fbshipit-source-id: d93d35ff984b9ba75f812c4c8e3c82e4d550f0c0
This commit is contained in:
committed by
Facebook Github Bot
parent
72c1c1fa4d
commit
44f5e35675
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* 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 "Flipper.h"
|
||||
|
||||
|
||||
@implementation Flipper
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
RCT_EXPORT_METHOD(sampleMethod:(NSString *)stringArgument numberParameter:(nonnull NSNumber *)numberArgument callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
// TODO: Implement some actually useful functionality
|
||||
callback(@[[NSString stringWithFormat: @"numberArgument: %@ stringArgument: %@", numberArgument, stringArgument]]);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -5,8 +5,8 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@interface Flipper : NSObject <RCTBridgeModule>
|
||||
@interface FlipperModule : RCTEventEmitter
|
||||
|
||||
@end
|
||||
98
react-native/react-native-flipper/ios/FlipperModule.m
Normal file
98
react-native/react-native-flipper/ios/FlipperModule.m
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 "FlipperModule.h"
|
||||
|
||||
#import "FlipperReactNativeJavaScriptPluginManager.h"
|
||||
|
||||
@implementation FlipperModule
|
||||
{
|
||||
__weak FlipperReactNativeJavaScriptPluginManager *_manager;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithManager:[FlipperReactNativeJavaScriptPluginManager sharedInstance]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithManager:(FlipperReactNativeJavaScriptPluginManager *)manager
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_manager = manager;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE(Flipper)
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents
|
||||
{
|
||||
return @[
|
||||
@"react-native-flipper-plugin-connect",
|
||||
@"react-native-flipper-plugin-disconnect",
|
||||
@"react-native-flipper-receive-event",
|
||||
];
|
||||
}
|
||||
|
||||
- (void)startObserving
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)stopObserving
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(registerPlugin:(NSString *)pluginId
|
||||
inBackground:(BOOL)inBackground
|
||||
statusCallback:(RCTResponseSenderBlock)statusCallback)
|
||||
{
|
||||
[_manager registerPluginWithModule:self
|
||||
pluginId:pluginId
|
||||
inBackground:inBackground
|
||||
statusCallback:statusCallback];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(send:(NSString *)pluginId method:(NSString *)method data:(NSString *)data)
|
||||
{
|
||||
[_manager sendWithPluginId:pluginId method:method data:data];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(reportErrorWithMetadata:(NSString *)pluginId
|
||||
reason:(NSString *)reason
|
||||
stackTrace:(NSString *)stackTrace)
|
||||
{
|
||||
[_manager reportErrorWithMetadata:reason stackTrace:stackTrace pluginId:pluginId];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(reportError:(NSString *)pluginId error:(NSString *)error)
|
||||
{
|
||||
[_manager reportError:error pluginId:pluginId];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(subscribe:(NSString *)pluginId method:(NSString *)method)
|
||||
{
|
||||
[_manager subscribeWithModule:self pluginId:pluginId method:method];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(respondSuccess:(NSString *)responderId data:(NSString *)data)
|
||||
{
|
||||
[_manager respondSuccessWithResponderId:responderId data:data];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(respondError:(NSString *)responderId data:(NSString *)data)
|
||||
{
|
||||
[_manager respondErrorWithResponderId:responderId data:data];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 <FlipperKit/FlipperPlugin.h>
|
||||
#import <FlipperKit/FlipperConnection.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class FlipperModule;
|
||||
|
||||
@interface FlipperReactNativeJavaScriptPlugin : NSObject<FlipperPlugin>
|
||||
|
||||
@property (nonatomic, weak) FlipperModule *module;
|
||||
@property (nonatomic, strong, readonly) id<FlipperConnection> connection;
|
||||
|
||||
- (instancetype)initWithFlipperModule:(FlipperModule *)module
|
||||
pluginId:(NSString *)pluginId
|
||||
inBackground:(BOOL)inBackground;
|
||||
|
||||
- (BOOL)isConnected;
|
||||
|
||||
- (void)fireOnConnect;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 "FlipperReactNativeJavaScriptPlugin.h"
|
||||
|
||||
#import <FlipperKit/FlipperClient.h>
|
||||
#import <FlipperKit/FlipperResponder.h>
|
||||
|
||||
#import "FlipperModule.h"
|
||||
|
||||
@implementation FlipperReactNativeJavaScriptPlugin
|
||||
{
|
||||
NSString *_pluginId;
|
||||
BOOL _inBackground;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFlipperModule:(FlipperModule *)module
|
||||
pluginId:(NSString *)pluginId
|
||||
inBackground:(BOOL)inBackground
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_module = module;
|
||||
_pluginId = pluginId;
|
||||
_inBackground = inBackground;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)identifier
|
||||
{
|
||||
return _pluginId;
|
||||
}
|
||||
|
||||
- (BOOL)runInBackground
|
||||
{
|
||||
return _inBackground;
|
||||
}
|
||||
|
||||
- (void)didConnect:(id<FlipperConnection>)connection
|
||||
{
|
||||
_connection = connection;
|
||||
[self fireOnConnect];
|
||||
}
|
||||
|
||||
- (void)didDisconnect
|
||||
{
|
||||
_connection = nil;
|
||||
[_module sendEventWithName:@"react-native-flipper-plugin-disconnect"
|
||||
body:[self pluginParams]];
|
||||
}
|
||||
|
||||
- (BOOL)isConnected
|
||||
{
|
||||
return _connection != nil;
|
||||
}
|
||||
|
||||
- (void)fireOnConnect
|
||||
{
|
||||
[_module sendEventWithName:@"react-native-flipper-plugin-connect"
|
||||
body:[self pluginParams]];
|
||||
}
|
||||
|
||||
- (id)pluginParams
|
||||
{
|
||||
return @{@"plugin": _pluginId};
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 <React/RCTBridgeModule.h>
|
||||
|
||||
#import <FlipperKit/FlipperClient.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class FlipperModule;
|
||||
|
||||
@interface FlipperReactNativeJavaScriptPluginManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (void)registerPluginWithModule:(FlipperModule *)module
|
||||
pluginId:(NSString *)pluginId
|
||||
inBackground:(BOOL)inBackground
|
||||
statusCallback:(RCTResponseSenderBlock)statusCallback;
|
||||
|
||||
- (void)sendWithPluginId:(NSString *)pluginId method:(NSString *)method data:(NSString *)data;
|
||||
|
||||
- (void)reportErrorWithMetadata:(NSString *)reason
|
||||
stackTrace:(NSString *)stackTrace
|
||||
pluginId:(NSString *)pluginId;
|
||||
|
||||
- (void)reportError:(NSString *)error pluginId:(NSString *)pluginId;
|
||||
|
||||
- (void)subscribeWithModule:(FlipperModule *)module
|
||||
pluginId:(NSString *)pluginId
|
||||
method:(NSString *)method;
|
||||
|
||||
- (void)respondSuccessWithResponderId:(NSString *)responderId data:(NSString *)data;
|
||||
|
||||
- (void)respondErrorWithResponderId:(NSString *)responderId data:(NSString *)data;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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 <FlipperKit/FlipperPlugin.h>
|
||||
#import <FlipperKit/FlipperClient.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import "FlipperModule.h"
|
||||
#import "FlipperReactNativeJavaScriptPlugin.h"
|
||||
|
||||
static uint32_t FlipperResponderKeyGenerator = 0;
|
||||
|
||||
@implementation FlipperReactNativeJavaScriptPluginManager
|
||||
{
|
||||
__weak FlipperClient *_flipperClient;
|
||||
NSMutableDictionary<NSString *, id<FlipperResponder>> *_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<NSString *> *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<FlipperResponder> 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<FlipperResponder> responder = _responders[responderId];
|
||||
[responder success:RCTJSONParse(data, NULL)];
|
||||
[_responders removeObjectForKey:responderId];
|
||||
}
|
||||
|
||||
- (void)respondErrorWithResponderId:(NSString *)responderId data:(NSString *)data
|
||||
{
|
||||
id<FlipperResponder> responder = _responders[responderId];
|
||||
[responder error:RCTJSONParse(data, NULL)];
|
||||
[_responders removeObjectForKey:responderId];
|
||||
}
|
||||
|
||||
- (FlipperReactNativeJavaScriptPlugin *)jsPluginWithIdentifier:(NSString *)pluginId
|
||||
{
|
||||
return (FlipperReactNativeJavaScriptPlugin *)[_flipperClient pluginWithIdentifier:pluginId];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -6,6 +6,7 @@
|
||||
require "json"
|
||||
|
||||
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
||||
compiler_flags = '-DFB_SONARKIT_ENABLED=1'
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "react-native-flipper"
|
||||
@@ -22,10 +23,8 @@ Pod::Spec.new do |s|
|
||||
s.source = { :git => "https://github.com/github_account/react-native-flipper.git", :tag => "#{s.version}" }
|
||||
|
||||
s.source_files = "ios/**/*.{h,m,swift}"
|
||||
s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"${PODS_ROOT}/Headers/Public/FlipperKit\"" }
|
||||
s.requires_arc = true
|
||||
|
||||
s.compiler_flags = compiler_flags
|
||||
s.dependency "React"
|
||||
# ...
|
||||
# s.dependency "..."
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user