iOS/FlipperKit/FlipperClient.mm (196 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #if FB_SONARKIT_ENABLED #import "FlipperClient.h" #import <Flipper/FlipperCertificateProvider.h> #import <Flipper/FlipperClient.h> #import <Flipper/FlipperSocketProvider.h> #include <folly/io/async/EventBase.h> #include <folly/io/async/ScopedEventBaseThread.h> #include <memory> #import "FlipperClient+Testing.h" #import "FlipperCppWrapperPlugin.h" #import "FlipperKitCertificateProvider.h" #import "FlipperWebSocket.h" #import "SKEnvironmentVariables.h" #include "SKStateUpdateCPPWrapper.h" #if !TARGET_OS_OSX #import <UIKit/UIKit.h> #if !TARGET_OS_SIMULATOR && !TARGET_OS_MACCATALYST #import <FKPortForwarding/FKPortForwardingServer.h> #endif #endif using WrapperPlugin = facebook::flipper::FlipperCppWrapperPlugin; @implementation FlipperClient { facebook::flipper::FlipperClient* _cppClient; folly::ScopedEventBaseThread sonarThread; folly::ScopedEventBaseThread connectionThread; id<FlipperKitCertificateProvider> _certProvider; #if !TARGET_OS_OSX && !TARGET_OS_SIMULATOR && !TARGET_OS_MACCATALYST FKPortForwardingServer* _secureServer; FKPortForwardingServer* _insecureServer; #endif } + (instancetype)sharedClient { static FlipperClient* sharedClient = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ try { sharedClient = [[self alloc] init]; } catch (const std::exception& e) { // fail. sharedClient = nil; } }); return sharedClient; } - (instancetype)init { if (self = [super init]) { NSBundle* bundle = [NSBundle mainBundle]; NSString* appName = [bundle objectForInfoDictionaryKey:(NSString*)kCFBundleNameKey]; NSString* appId = [bundle bundleIdentifier]; NSString* privateAppDirectory = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES)[0]; NSFileManager* manager = [NSFileManager defaultManager]; if ([manager fileExistsAtPath:privateAppDirectory isDirectory:NULL] == NO && ![manager createDirectoryAtPath:privateAppDirectory withIntermediateDirectories:YES attributes:nil error:nil]) { return nil; } NSString* deviceOS; NSString* deviceName; #if !TARGET_OS_OSX #if !TARGET_OS_MACCATALYST deviceOS = @"iOS"; #else deviceOS = @"MacOS"; #endif deviceName = [[UIDevice currentDevice] name]; #if TARGET_OS_SIMULATOR deviceName = [NSString stringWithFormat:@"%@ %@", [[UIDevice currentDevice] model], @"Simulator"]; #endif #else deviceOS = @"MacOS"; deviceName = [[NSHost currentHost] localizedName]; #endif static const std::string UNKNOWN = std::string("unknown"); try { facebook::flipper::FlipperClient::init( {{ "localhost", [deviceOS UTF8String], [deviceName UTF8String], UNKNOWN, [appName UTF8String] ?: UNKNOWN, [appId UTF8String] ?: UNKNOWN, [privateAppDirectory UTF8String], }, sonarThread.getEventBase(), connectionThread.getEventBase(), [SKEnvironmentVariables getInsecurePort], [SKEnvironmentVariables getSecurePort], [SKEnvironmentVariables getAltInsecurePort], [SKEnvironmentVariables getAltSecurePort]}); _cppClient = facebook::flipper::FlipperClient::instance(); facebook::flipper::FlipperSocketProvider::setDefaultProvider( std::make_unique<facebook::flipper::FlipperWebSocketProvider>()); } catch (const std::system_error& e) { // Probably ran out of disk space. return nil; } } return self; } - (void)setCertificateProvider:(id<FlipperKitCertificateProvider>)provider { _certProvider = provider; std::shared_ptr<facebook::flipper::FlipperCertificateProvider>* prov = static_cast< std::shared_ptr<facebook::flipper::FlipperCertificateProvider>*>( [provider getCPPCertificateProvider]); _cppClient->setCertificateProvider(*prov); } - (id<FlipperKitCertificateProvider>)getCertificateProvider { return _certProvider; } - (void)refreshPlugins { _cppClient->refreshPlugins(); } - (void)addPlugin:(NSObject<FlipperPlugin>*)plugin { _cppClient->addPlugin(std::make_shared<WrapperPlugin>(plugin)); } - (void)removePlugin:(NSObject<FlipperPlugin>*)plugin { _cppClient->removePlugin(std::make_shared<WrapperPlugin>(plugin)); } - (NSObject<FlipperPlugin>*)pluginWithIdentifier:(NSString*)identifier { auto cppPlugin = _cppClient->getPlugin([identifier UTF8String]); if (auto wrapper = dynamic_cast<WrapperPlugin*>(cppPlugin.get())) { return wrapper->getObjCPlugin(); } return nil; } - (void)start { #if !TARGET_OS_OSX && !TARGET_OS_SIMULATOR && !TARGET_OS_MACCATALYST _secureServer = [FKPortForwardingServer new]; [_secureServer forwardConnectionsFromPort:9088]; [_secureServer listenForMultiplexingChannelOnPort:9078]; _insecureServer = [FKPortForwardingServer new]; [_insecureServer forwardConnectionsFromPort:9089]; [_insecureServer listenForMultiplexingChannelOnPort:9079]; #endif _cppClient->start(); } - (void)stop { _cppClient->stop(); #if !TARGET_OS_OSX && !TARGET_OS_SIMULATOR && !TARGET_OS_MACCATALYST [_secureServer close]; _secureServer = nil; [_insecureServer close]; _insecureServer = nil; #endif } - (NSString*)getState { return @(_cppClient->getState().c_str()); } - (NSArray*)getStateElements { NSMutableArray<NSDictionary<NSString*, NSString*>*>* const array = [NSMutableArray array]; for (facebook::flipper::StateElement element : _cppClient->getStateElements()) { facebook::flipper::State state = element.state_; NSString* stateString; switch (state) { case facebook::flipper::in_progress: stateString = @"⏳ "; break; case facebook::flipper::success: stateString = @"✅ "; break; case facebook::flipper::failed: stateString = @"❌ "; break; default: stateString = @"❓ "; break; } [array addObject:@{ @"name" : [NSString stringWithUTF8String:element.name_.c_str()], @"state" : stateString }]; } return array; } - (void)subscribeForUpdates:(id<FlipperStateUpdateListener>)controller { auto stateListener = std::make_shared<SKStateUpdateCPPWrapper>(controller); _cppClient->setStateListener(stateListener); } @end @implementation FlipperClient (Testing) - (instancetype)initWithCppClient:(facebook::flipper::FlipperClient*)cppClient { if (self = [super init]) { _cppClient = cppClient; } return self; } @end #endif