Skip to content

Commit

Permalink
Add RCTDevSettings module
Browse files Browse the repository at this point in the history
Summary:
This decouples non-UI logic from RCTDevMenu into a new module RCTDevSettings.

**Motivation**: This allows developers to change dev settings without depending on the built-in dev menu, e.g. if they want to introduce their own UI, or have other devtools logic that doesn't depend on an action sheet.

It also introduces the RCTDevSettingsDataSource protocol for storing dev tools preferences. This could allow a developer to implement alternative behaviors, e.g. loading the settings from some other config, changing settings based on the user, deciding not to persist some settings, or something else.

The included data source implementation, RCTDevSettingsUserDefaultsDataSource, uses NSUserDefaults and is backwards compatible with the older implementation, so **no workflows or dependent code will break, and old saved settings will persist.**

The RCTDevMenu interface has not changed and is therefore also backwards-compatible, though
some methods are now deprecated.

In order to ensure that RCTDevSettings
Closes #11613

Reviewed By: mmmulani

Differential Revision: D4571773

Pulled By: javache

fbshipit-source-id: 25555d0a6eaa81f694343e079ed02439e5845fbc
  • Loading branch information
terribleben authored and facebook-github-bot committed Feb 24, 2017
1 parent bb1f851 commit 6a14f0b
Show file tree
Hide file tree
Showing 10 changed files with 1,099 additions and 832 deletions.
1 change: 1 addition & 0 deletions Libraries/WebSocket/RCTWebSocketExecutor.m
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ - (void)setUp
if (!_url) {
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];

// TODO t16297016: this seems to be unused, remove?
NSInteger port = [standardDefaults integerForKey:@"websocket-executor-port"];
if (!port) {
port = [[[_bridge bundleURL] port] integerValue] ?: 8081;
Expand Down
4 changes: 2 additions & 2 deletions React/CxxBridge/RCTCxxBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#import <React/RCTCxxModule.h>
#import <React/RCTCxxUtils.h>
#import <React/RCTDevLoadingView.h>
#import <React/RCTDevMenu.h>
#import <React/RCTDevSettings.h>
#import <React/RCTDisplayLink.h>
#import <React/RCTJavaScriptLoader.h>
#import <React/RCTLog.h>
Expand Down Expand Up @@ -403,7 +403,7 @@ - (void)start
executorFactory.reset(new JSCExecutorFactory("", folly::dynamic::object
("UseCustomJSC", (bool)useCustomJSC)
#if RCT_PROFILE
("StartSamplingProfilerOnInit", (bool)self.devMenu.startSamplingProfilerOnLaunch)
("StartSamplingProfilerOnInit", (bool)self.devSettings.startSamplingProfilerOnLaunch)
#endif
));
} else {
Expand Down
26 changes: 11 additions & 15 deletions React/Executors/RCTJSCExecutor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#import "RCTBridge+Private.h"
#import "RCTDefines.h"
#import "RCTDevMenu.h"
#import "RCTDevSettings.h"
#import "RCTJSCErrorHandling.h"
#import "RCTJSCProfiler.h"
#import "RCTJavaScriptLoader.h"
Expand All @@ -38,8 +39,6 @@
RCT_EXTERN NSString *const RCTFBJSContextClassKey = @"_RCTFBJSContextClassKey";
RCT_EXTERN NSString *const RCTFBJSValueClassKey = @"_RCTFBJSValueClassKey";

static NSString *const RCTJSCProfilerEnabledDefaultsKey = @"RCTJSCProfilerEnabled";

struct __attribute__((packed)) ModuleData {
uint32_t offset;
uint32_t size;
Expand Down Expand Up @@ -168,15 +167,21 @@ @implementation RCTJSCExecutor
#if RCT_DEV
static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
{
__weak RCTBridge *weakBridge = bridge;
__weak RCTDevSettings *devSettings = bridge.devSettings;
if (RCTJSCProfilerIsSupported()) {
[bridge.devMenu addItem:[RCTDevMenuItem toggleItemWithKey:RCTJSCProfilerEnabledDefaultsKey title:@"Start Profiling" selectedTitle:@"Stop Profiling" handler:^(BOOL shouldStart) {
[bridge.devMenu addItem:[RCTDevMenuItem buttonItemWithTitleBlock:^NSString *{
return devSettings.isJSCProfilingEnabled ? @"Stop Profiling" : @"Start Profiling";
} handler:^{
BOOL shouldStart = !devSettings.isJSCProfilingEnabled;
devSettings.isJSCProfilingEnabled = shouldStart;
if (shouldStart != RCTJSCProfilerIsProfiling(context)) {
if (shouldStart) {
RCTJSCProfilerStart(context);
} else {
NSString *outputFile = RCTJSCProfilerStop(context);
NSData *profileData = [NSData dataWithContentsOfFile:outputFile options:NSDataReadingMappedIfSafe error:NULL];
RCTProfileSendResult(bridge, @"cpu-profile", profileData);
RCTProfileSendResult(weakBridge, @"cpu-profile", profileData);
}
}
}]];
Expand Down Expand Up @@ -309,7 +314,7 @@ - (void)setUp
} else {
if (self->_useCustomJSCLibrary) {
JSC_configureJSCForIOS(true, RCTJSONStringify(@{
@"StartSamplingProfilerOnInit": @(self->_bridge.devMenu.startSamplingProfilerOnLaunch)
@"StartSamplingProfilerOnInit": @(self->_bridge.devSettings.startSamplingProfilerOnLaunch)
}, NULL).UTF8String);
}
contextRef = JSC_JSGlobalContextCreateInGroup(self->_useCustomJSCLibrary, nullptr, nullptr);
Expand Down Expand Up @@ -401,16 +406,7 @@ - (void)setUp
if (!strongSelf.valid || !weakContext) {
return;
}

// JSPokeSamplingProfiler() toggles the profiling process
JSGlobalContextRef ctx = weakContext.JSGlobalContextRef;
JSValueRef jsResult = JSC_JSPokeSamplingProfiler(ctx);

if (JSC_JSValueGetType(ctx, jsResult) != kJSTypeNull) {
NSString *results = [[JSC_JSValue(ctx) valueWithJSValueRef:jsResult inContext:weakContext] toObject];
JSCSamplingProfiler *profilerModule = [strongSelf->_bridge moduleForClass:[JSCSamplingProfiler class]];
[profilerModule operationCompletedWithResults:results];
}
[weakSelf.bridge.devSettings toggleJSCSamplingProfiler];
}]];

// Allow for the profiler to be poked from JS code as well
Expand Down
53 changes: 19 additions & 34 deletions React/Modules/RCTDevMenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,59 +20,44 @@
@interface RCTDevMenu : NSObject

/**
* Is the menu enabled. The menu is enabled by default if RCT_DEV=1, but
* you may wish to disable it so that you can provide your own shake handler.
* Deprecated, use RCTDevSettings instead.
*/
@property (nonatomic, assign) BOOL shakeToShow;
@property (nonatomic, assign) BOOL shakeToShow DEPRECATED_ATTRIBUTE;

/**
* Enables performance profiling.
* Deprecated, use RCTDevSettings instead.
*/
@property (nonatomic, assign) BOOL profilingEnabled;
@property (nonatomic, assign) BOOL profilingEnabled DEPRECATED_ATTRIBUTE;

/**
* Enables starting of profiling sampler on launch
* Deprecated, use RCTDevSettings instead.
*/
@property (nonatomic, assign) BOOL startSamplingProfilerOnLaunch;
@property (nonatomic, assign) BOOL liveReloadEnabled DEPRECATED_ATTRIBUTE;

/**
* Enables automatic polling for JS code changes. Only applicable when
* running the app from a server.
* Deprecated, use RCTDevSettings instead.
*/
@property (nonatomic, assign) BOOL liveReloadEnabled;

/**
* Enables hot loading. Currently not supported in open source.
*/
@property (nonatomic, assign) BOOL hotLoadingEnabled;

/**
* Shows the FPS monitor for the JS and Main threads.
*/
@property (nonatomic, assign) BOOL showFPS;
@property (nonatomic, assign) BOOL hotLoadingEnabled DEPRECATED_ATTRIBUTE;

/**
* Presented items in development menu
*/
@property (nonatomic, copy, readonly) NSArray<RCTDevMenuItem *> *presentedItems;


/**
* Detect if actions sheet (development menu) is shown
*/
- (BOOL)isActionSheetShown;


/**
* Manually show the dev menu (can be called from JS).
*/
- (void)show;

/**
* Manually reload the application. Equivalent to calling [bridge reload]
* directly, but can be called from JS.
* Deprecated, use -[RCTBRidge reload] instead.
*/
- (void)reload;
- (void)reload DEPRECATED_ATTRIBUTE;

/**
* Deprecated. Use the `-addItem:` method instead.
Expand All @@ -88,6 +73,8 @@

@end

typedef NSString *(^RCTDevMenuItemTitleBlock)(void);

/**
* Developer menu item, used to expose additional functionality via the menu.
*/
Expand All @@ -98,18 +85,16 @@
* action.
*/
+ (instancetype)buttonItemWithTitle:(NSString *)title
handler:(void(^)(void))handler;
handler:(dispatch_block_t)handler;

/**
* This creates an item with a toggle behavior. The key is used to store the
* state of the toggle. For toggle items, the handler will be called immediately
* after the item is added if the item was already selected when the module was
* last loaded.
* This creates an item with a simple push-button interface, used to trigger an
* action. getTitleForPresentation is called each time the item is about to be
* presented, and should return the item's title.
*/
+ (instancetype)toggleItemWithKey:(NSString *)key
title:(NSString *)title
selectedTitle:(NSString *)selectedTitle
handler:(void(^)(BOOL selected))handler;
+ (instancetype)buttonItemWithTitleBlock:(RCTDevMenuItemTitleBlock)titleBlock
handler:(dispatch_block_t)handler;

@end

/**
Expand Down
Loading

0 comments on commit 6a14f0b

Please sign in to comment.