2019-10-16 20:03:47 +03:00
|
|
|
/*
|
2021-12-31 02:08:43 +03:00
|
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
2015-03-23 23:28:42 +03:00
|
|
|
*
|
2018-02-17 05:24:55 +03:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2015-03-23 23:28:42 +03:00
|
|
|
*/
|
2015-02-20 07:10:52 +03:00
|
|
|
|
|
|
|
#import "RCTStatusBarManager.h"
|
2019-11-01 21:54:44 +03:00
|
|
|
#import "CoreModulesPlugins.h"
|
2015-02-20 07:10:52 +03:00
|
|
|
|
2020-10-14 12:37:51 +03:00
|
|
|
#import <React/RCTEventDispatcherProtocol.h>
|
2019-11-01 21:54:44 +03:00
|
|
|
#import <React/RCTLog.h>
|
|
|
|
#import <React/RCTUtils.h>
|
2015-02-20 07:10:52 +03:00
|
|
|
|
2019-11-01 21:54:44 +03:00
|
|
|
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
|
|
|
|
2015-06-05 18:46:17 +03:00
|
|
|
@implementation RCTConvert (UIStatusBar)
|
|
|
|
|
2019-09-11 15:10:06 +03:00
|
|
|
+ (UIStatusBarStyle)UIStatusBarStyle:(id)json RCT_DYNAMIC
|
|
|
|
{
|
|
|
|
static NSDictionary *mapping;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
if (@available(iOS 13.0, *)) {
|
|
|
|
mapping = @{
|
|
|
|
@"default" : @(UIStatusBarStyleDefault),
|
|
|
|
@"light-content" : @(UIStatusBarStyleLightContent),
|
|
|
|
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
|
|
|
|
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
|
|
|
|
@"dark-content" : @(UIStatusBarStyleDarkContent)
|
|
|
|
#else
|
|
|
|
@"dark-content": @(UIStatusBarStyleDefault)
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
} else {
|
|
|
|
mapping = @{
|
|
|
|
@"default" : @(UIStatusBarStyleDefault),
|
|
|
|
@"light-content" : @(UIStatusBarStyleLightContent),
|
|
|
|
@"dark-content" : @(UIStatusBarStyleDefault)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return _RCT_CAST(
|
2020-03-09 08:58:01 +03:00
|
|
|
UIStatusBarStyle,
|
|
|
|
[RCTConvertEnumValue("UIStatusBarStyle", mapping, @(UIStatusBarStyleDefault), json) integerValue]);
|
2019-09-11 15:10:06 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(
|
|
|
|
UIStatusBarAnimation,
|
|
|
|
(@{
|
|
|
|
@"none" : @(UIStatusBarAnimationNone),
|
|
|
|
@"fade" : @(UIStatusBarAnimationFade),
|
|
|
|
@"slide" : @(UIStatusBarAnimationSlide),
|
|
|
|
}),
|
|
|
|
UIStatusBarAnimationNone,
|
|
|
|
integerValue);
|
2015-06-05 18:46:17 +03:00
|
|
|
|
|
|
|
@end
|
2019-11-01 21:54:44 +03:00
|
|
|
|
2020-03-09 08:58:01 +03:00
|
|
|
@interface RCTStatusBarManager () <NativeStatusBarManagerIOSSpec>
|
2019-11-01 21:54:44 +03:00
|
|
|
@end
|
|
|
|
|
2015-02-20 07:10:52 +03:00
|
|
|
@implementation RCTStatusBarManager
|
|
|
|
|
|
|
|
static BOOL RCTViewControllerBasedStatusBarAppearance()
|
|
|
|
{
|
|
|
|
static BOOL value;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
2019-09-11 15:10:06 +03:00
|
|
|
value =
|
|
|
|
[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"]
|
|
|
|
?: @YES boolValue];
|
2015-02-20 07:10:52 +03:00
|
|
|
});
|
2015-03-11 05:03:59 +03:00
|
|
|
|
2015-02-20 07:10:52 +03:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2015-04-08 15:42:43 +03:00
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
|
2019-12-04 18:46:42 +03:00
|
|
|
+ (BOOL)requiresMainQueueSetup
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
Added native event emitter
Summary:
This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/
I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already).
Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`.
JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing:
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... });
You'd now write:
const Keyboard = require('Keyboard');
Keyboard.addListener('keyboardWillShow', (event) => { ... });
Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of:
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...);
Write:
this.addListenerOn(Keyboard, 'keyboardWillShow', ...);
This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module.
As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff.
For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface.
Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem).
Reviewed By: javache
Differential Revision: D3269966
fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
|
|
|
- (NSArray<NSString *> *)supportedEvents
|
2016-05-04 17:06:09 +03:00
|
|
|
{
|
2019-09-11 15:10:06 +03:00
|
|
|
return @[ @"statusBarFrameDidChange", @"statusBarFrameWillChange" ];
|
2016-05-04 17:06:09 +03:00
|
|
|
}
|
|
|
|
|
Added native event emitter
Summary:
This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/
I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already).
Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`.
JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing:
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... });
You'd now write:
const Keyboard = require('Keyboard');
Keyboard.addListener('keyboardWillShow', (event) => { ... });
Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of:
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...);
Write:
this.addListenerOn(Keyboard, 'keyboardWillShow', ...);
This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module.
As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff.
For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface.
Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem).
Reviewed By: javache
Differential Revision: D3269966
fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
|
|
|
- (void)startObserving
|
2015-08-19 16:37:53 +03:00
|
|
|
{
|
2015-11-25 14:09:00 +03:00
|
|
|
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
2019-09-11 15:10:06 +03:00
|
|
|
[nc addObserver:self
|
|
|
|
selector:@selector(applicationDidChangeStatusBarFrame:)
|
|
|
|
name:UIApplicationDidChangeStatusBarFrameNotification
|
|
|
|
object:nil];
|
|
|
|
[nc addObserver:self
|
|
|
|
selector:@selector(applicationWillChangeStatusBarFrame:)
|
|
|
|
name:UIApplicationWillChangeStatusBarFrameNotification
|
|
|
|
object:nil];
|
2015-08-19 16:37:53 +03:00
|
|
|
}
|
|
|
|
|
Added native event emitter
Summary:
This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/
I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already).
Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`.
JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing:
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... });
You'd now write:
const Keyboard = require('Keyboard');
Keyboard.addListener('keyboardWillShow', (event) => { ... });
Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of:
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...);
Write:
this.addListenerOn(Keyboard, 'keyboardWillShow', ...);
This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module.
As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff.
For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface.
Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem).
Reviewed By: javache
Differential Revision: D3269966
fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
|
|
|
- (void)stopObserving
|
2015-08-19 16:37:53 +03:00
|
|
|
{
|
|
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
|
}
|
|
|
|
|
2015-04-20 22:06:02 +03:00
|
|
|
- (dispatch_queue_t)methodQueue
|
|
|
|
{
|
|
|
|
return dispatch_get_main_queue();
|
|
|
|
}
|
|
|
|
|
2015-08-19 16:37:53 +03:00
|
|
|
- (void)emitEvent:(NSString *)eventName forNotification:(NSNotification *)notification
|
|
|
|
{
|
|
|
|
CGRect frame = [notification.userInfo[UIApplicationStatusBarFrameUserInfoKey] CGRectValue];
|
|
|
|
NSDictionary *event = @{
|
2019-09-11 15:10:06 +03:00
|
|
|
@"frame" : @{
|
|
|
|
@"x" : @(frame.origin.x),
|
|
|
|
@"y" : @(frame.origin.y),
|
|
|
|
@"width" : @(frame.size.width),
|
|
|
|
@"height" : @(frame.size.height),
|
2015-08-19 16:37:53 +03:00
|
|
|
},
|
|
|
|
};
|
Added native event emitter
Summary:
This is a solution for the problem I raised in https://www.facebook.com/groups/react.native.community/permalink/768218933313687/
I've added a new native base class, `RCTEventEmitter` as well as an equivalent JS class/module `NativeEventEmitter` (RCTEventEmitter.js and EventEmitter.js were taken already).
Instead of arbitrary modules sending events via `bridge.eventDispatcher`, the idea is that any module that sends events should now subclass `RCTEventEmitter`, and provide an equivalent JS module that subclasses `NativeEventEmitter`.
JS code that wants to observe the events should now observe it via the specific JS module rather than via `RCTDeviceEventEmitter` directly. e.g. to observer a keyboard event, instead of writing:
const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
RCTDeviceEventEmitter.addListener('keyboardWillShow', (event) => { ... });
You'd now write:
const Keyboard = require('Keyboard');
Keyboard.addListener('keyboardWillShow', (event) => { ... });
Within a component, you can also use the `Subscribable.Mixin` as you would previously, but instead of:
this.addListenerOn(RCTDeviceEventEmitter, 'keyboardWillShow', ...);
Write:
this.addListenerOn(Keyboard, 'keyboardWillShow', ...);
This approach allows the native `RCTKeyboardObserver` module to be created lazily the first time a listener is added, and to stop sending events when the last listener is removed. It also allows us to validate that the event strings being observed and omitted match the supported events for that module.
As a proof-of-concept, I've converted the `RCTStatusBarManager` and `RCTKeyboardObserver` modules to use the new system. I'll convert the rest in a follow up diff.
For now, the new `NativeEventEmitter` JS module wraps the `RCTDeviceEventEmitter` JS module, and just uses the native `RCTEventEmitter` module for bookkeeping. This allows for full backwards compatibility (code that is observing the event via `RCTDeviceEventEmitter` instead of the specific module will still work as expected, albeit with a warning). Once all legacy calls have been removed, this could be refactored to something more elegant internally, whilst maintaining the same public interface.
Note: currently, all device events still share a single global namespace, since they're really all registered on the same emitter instance internally. We should move away from that as soon as possible because it's not intuitive and will likely lead to strange bugs if people add generic events such as "onChange" or "onError" to their modules (which is common practice for components, where it's not a problem).
Reviewed By: javache
Differential Revision: D3269966
fbshipit-source-id: 1412daba850cd373020e1086673ba38ef9193050
2016-05-11 16:26:53 +03:00
|
|
|
[self sendEventWithName:eventName body:event];
|
2015-08-19 16:37:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)applicationDidChangeStatusBarFrame:(NSNotification *)notification
|
|
|
|
{
|
|
|
|
[self emitEvent:@"statusBarFrameDidChange" forNotification:notification];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)applicationWillChangeStatusBarFrame:(NSNotification *)notification
|
|
|
|
{
|
|
|
|
[self emitEvent:@"statusBarFrameWillChange" forNotification:notification];
|
|
|
|
}
|
|
|
|
|
2020-03-06 02:55:10 +03:00
|
|
|
RCT_EXPORT_METHOD(getHeight : (RCTResponseSenderBlock)callback)
|
2016-01-04 15:30:37 +03:00
|
|
|
{
|
2019-09-11 15:10:06 +03:00
|
|
|
callback(@[ @{
|
|
|
|
@"height" : @(RCTSharedApplication().statusBarFrame.size.height),
|
|
|
|
} ]);
|
2016-01-04 15:30:37 +03:00
|
|
|
}
|
|
|
|
|
2020-03-06 02:55:10 +03:00
|
|
|
RCT_EXPORT_METHOD(setStyle : (NSString *)style animated : (BOOL)animated)
|
2015-02-20 07:10:52 +03:00
|
|
|
{
|
2020-03-06 02:55:10 +03:00
|
|
|
UIStatusBarStyle statusBarStyle = [RCTConvert UIStatusBarStyle:style];
|
|
|
|
if (RCTViewControllerBasedStatusBarAppearance()) {
|
|
|
|
RCTLogError(@"RCTStatusBarManager module requires that the \
|
|
|
|
UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO");
|
2020-03-06 02:55:10 +03:00
|
|
|
} else {
|
2019-02-25 06:36:23 +03:00
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
2020-03-06 02:55:10 +03:00
|
|
|
[RCTSharedApplication() setStatusBarStyle:statusBarStyle animated:animated];
|
|
|
|
}
|
2019-02-25 06:36:23 +03:00
|
|
|
#pragma clang diagnostic pop
|
2015-02-20 07:10:52 +03:00
|
|
|
}
|
|
|
|
|
2020-03-06 02:55:10 +03:00
|
|
|
RCT_EXPORT_METHOD(setHidden : (BOOL)hidden withAnimation : (NSString *)withAnimation)
|
2015-02-20 07:10:52 +03:00
|
|
|
{
|
2019-11-01 21:54:44 +03:00
|
|
|
UIStatusBarAnimation animation = [RCTConvert UIStatusBarAnimation:withAnimation];
|
2020-03-06 02:55:10 +03:00
|
|
|
if (RCTViewControllerBasedStatusBarAppearance()) {
|
|
|
|
RCTLogError(@"RCTStatusBarManager module requires that the \
|
|
|
|
UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO");
|
2020-03-06 02:55:10 +03:00
|
|
|
} else {
|
2019-02-25 06:36:23 +03:00
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
2020-03-06 02:55:10 +03:00
|
|
|
[RCTSharedApplication() setStatusBarHidden:hidden withAnimation:animation];
|
2019-02-25 06:36:23 +03:00
|
|
|
#pragma clang diagnostic pop
|
2020-03-06 02:55:10 +03:00
|
|
|
}
|
2015-02-20 07:10:52 +03:00
|
|
|
}
|
|
|
|
|
2019-09-11 15:10:06 +03:00
|
|
|
RCT_EXPORT_METHOD(setNetworkActivityIndicatorVisible : (BOOL)visible)
|
2015-07-24 02:19:27 +03:00
|
|
|
{
|
2015-09-22 20:43:56 +03:00
|
|
|
RCTSharedApplication().networkActivityIndicatorVisible = visible;
|
2015-07-24 02:19:27 +03:00
|
|
|
}
|
|
|
|
|
2019-11-01 21:54:44 +03:00
|
|
|
- (facebook::react::ModuleConstants<JS::NativeStatusBarManagerIOS::Constants>)getConstants
|
|
|
|
{
|
Run getConstants method statements on main queue
Summary:
If a NativeModule requires main queue setup, its `getConstants()` method must be executed on the main thead. The legacy NativeModule infra takes care of this for us. With TurboModules, however, all synchronous methods, including `getConstants()`, execute on the JS thread. Therefore, if a TurboModule requires main queue setup, and exports constants, we must execute its `getConstants()` method body on the main queue explicitly.
**Notes:**
- The changes in this diff should be a noop when TurboModules is off, because `RCTUnsafeExecuteOnMainQueueSync` synchronously execute its block on the current thread if the current thread is the main thread.
- If a NativeModule doens't have the `requiresMainQueueSetup` method, but has the `constantsToExport` method, both NativeModules and TurboModules assume that it requires main queue setup.
## Script
```
const exec = require("../lib/exec");
const abspath = require("../lib/abspath");
const relpath = require("../lib/relpath");
const readFile = (filename) => require("fs").readFileSync(filename, "utf8");
const writeFile = (filename, content) =>
require("fs").writeFileSync(filename, content);
function main() {
const tmFiles = exec("cd ~/fbsource && xbgs -n 10000 -l constantsToExport")
.split("\n")
.filter(Boolean);
const filesWithoutConstantsToExport = [];
const filesWithConstantsToExportButNotGetConstants = [];
const filesExplicitlyNotRequiringMainQueueSetup = [];
tmFiles
.filter((filename) => {
if (filename.includes("microsoft-fork-of-react-native")) {
return false;
}
return /\.mm?$/.test(filename);
})
.map(abspath)
.forEach((filename) => {
const code = readFile(filename);
const relFilename = relpath(filename);
if (!/constantsToExport\s*{/.test(code)) {
filesWithoutConstantsToExport.push(relFilename);
return;
}
if (!/getConstants\s*{/.test(code)) {
filesWithConstantsToExportButNotGetConstants.push(relFilename);
return;
}
if (/requiresMainQueueSetup\s*{/.test(code)) {
const requiresMainQueueSetupRegex = /requiresMainQueueSetup\s*{\s*return\s+(?<requiresMainQueueSetup>YES|NO)/;
const requiresMainQueueSetupRegexMatch = requiresMainQueueSetupRegex.exec(
code
);
if (!requiresMainQueueSetupRegexMatch) {
throw new Error(
"Detected requiresMainQueueSetup method in file " +
relFilename +
" but was unable to parse the method return value"
);
}
const {
requiresMainQueueSetup,
} = requiresMainQueueSetupRegexMatch.groups;
if (requiresMainQueueSetup == "NO") {
filesExplicitlyNotRequiringMainQueueSetup.push(relFilename);
return;
}
}
const getConstantsTypeRegex = () => /-\s*\((?<type>.*)\)getConstants\s*{/;
const getConstantsTypeRegexMatch = getConstantsTypeRegex().exec(code);
if (!getConstantsTypeRegexMatch) {
throw new Error(
`Failed to parse return type of getConstants method in file ${relFilename}`
);
}
const getConstantsType = getConstantsTypeRegexMatch.groups.type;
const getConstantsBody = code
.split(getConstantsTypeRegex())[2]
.split("\n}")[0];
const newGetConstantsBody = `
__block ${getConstantsType} constants;
RCTUnsafeExecuteOnMainQueueSync(^{${getConstantsBody
.replace(/\n/g, "\n ")
.replace(/_bridge/g, "self->_bridge")
.replace(/return /g, "constants = ")}
});
return constants;
`;
writeFile(
filename,
code
.replace(getConstantsBody, newGetConstantsBody)
.replace("#import", "#import <React/RCTUtils.h>\n#import")
);
});
console.log("Files without constantsToExport: ");
filesWithoutConstantsToExport.forEach((file) => console.log(file));
console.log();
console.log("Files with constantsToExport but no getConstants: ");
filesWithConstantsToExportButNotGetConstants.forEach((file) =>
console.log(file)
);
console.log();
console.log("Files with requiresMainQueueSetup = NO: ");
filesExplicitlyNotRequiringMainQueueSetup.forEach((file) =>
console.log(file)
);
}
if (!module.parent) {
main();
}
```
Changelog: [Internal]
Reviewed By: fkgozali
Differential Revision: D21797048
fbshipit-source-id: a822a858fecdbe976e6197f8339e509dc7cd917f
2020-06-03 08:58:04 +03:00
|
|
|
__block facebook::react::ModuleConstants<JS::NativeStatusBarManagerIOS::Constants> constants;
|
|
|
|
RCTUnsafeExecuteOnMainQueueSync(^{
|
|
|
|
constants = facebook::react::typedConstants<JS::NativeStatusBarManagerIOS::Constants>({
|
|
|
|
.HEIGHT = RCTSharedApplication().statusBarFrame.size.height,
|
2022-03-23 03:10:18 +03:00
|
|
|
.DEFAULT_BACKGROUND_COLOR = std::nullopt,
|
Run getConstants method statements on main queue
Summary:
If a NativeModule requires main queue setup, its `getConstants()` method must be executed on the main thead. The legacy NativeModule infra takes care of this for us. With TurboModules, however, all synchronous methods, including `getConstants()`, execute on the JS thread. Therefore, if a TurboModule requires main queue setup, and exports constants, we must execute its `getConstants()` method body on the main queue explicitly.
**Notes:**
- The changes in this diff should be a noop when TurboModules is off, because `RCTUnsafeExecuteOnMainQueueSync` synchronously execute its block on the current thread if the current thread is the main thread.
- If a NativeModule doens't have the `requiresMainQueueSetup` method, but has the `constantsToExport` method, both NativeModules and TurboModules assume that it requires main queue setup.
## Script
```
const exec = require("../lib/exec");
const abspath = require("../lib/abspath");
const relpath = require("../lib/relpath");
const readFile = (filename) => require("fs").readFileSync(filename, "utf8");
const writeFile = (filename, content) =>
require("fs").writeFileSync(filename, content);
function main() {
const tmFiles = exec("cd ~/fbsource && xbgs -n 10000 -l constantsToExport")
.split("\n")
.filter(Boolean);
const filesWithoutConstantsToExport = [];
const filesWithConstantsToExportButNotGetConstants = [];
const filesExplicitlyNotRequiringMainQueueSetup = [];
tmFiles
.filter((filename) => {
if (filename.includes("microsoft-fork-of-react-native")) {
return false;
}
return /\.mm?$/.test(filename);
})
.map(abspath)
.forEach((filename) => {
const code = readFile(filename);
const relFilename = relpath(filename);
if (!/constantsToExport\s*{/.test(code)) {
filesWithoutConstantsToExport.push(relFilename);
return;
}
if (!/getConstants\s*{/.test(code)) {
filesWithConstantsToExportButNotGetConstants.push(relFilename);
return;
}
if (/requiresMainQueueSetup\s*{/.test(code)) {
const requiresMainQueueSetupRegex = /requiresMainQueueSetup\s*{\s*return\s+(?<requiresMainQueueSetup>YES|NO)/;
const requiresMainQueueSetupRegexMatch = requiresMainQueueSetupRegex.exec(
code
);
if (!requiresMainQueueSetupRegexMatch) {
throw new Error(
"Detected requiresMainQueueSetup method in file " +
relFilename +
" but was unable to parse the method return value"
);
}
const {
requiresMainQueueSetup,
} = requiresMainQueueSetupRegexMatch.groups;
if (requiresMainQueueSetup == "NO") {
filesExplicitlyNotRequiringMainQueueSetup.push(relFilename);
return;
}
}
const getConstantsTypeRegex = () => /-\s*\((?<type>.*)\)getConstants\s*{/;
const getConstantsTypeRegexMatch = getConstantsTypeRegex().exec(code);
if (!getConstantsTypeRegexMatch) {
throw new Error(
`Failed to parse return type of getConstants method in file ${relFilename}`
);
}
const getConstantsType = getConstantsTypeRegexMatch.groups.type;
const getConstantsBody = code
.split(getConstantsTypeRegex())[2]
.split("\n}")[0];
const newGetConstantsBody = `
__block ${getConstantsType} constants;
RCTUnsafeExecuteOnMainQueueSync(^{${getConstantsBody
.replace(/\n/g, "\n ")
.replace(/_bridge/g, "self->_bridge")
.replace(/return /g, "constants = ")}
});
return constants;
`;
writeFile(
filename,
code
.replace(getConstantsBody, newGetConstantsBody)
.replace("#import", "#import <React/RCTUtils.h>\n#import")
);
});
console.log("Files without constantsToExport: ");
filesWithoutConstantsToExport.forEach((file) => console.log(file));
console.log();
console.log("Files with constantsToExport but no getConstants: ");
filesWithConstantsToExportButNotGetConstants.forEach((file) =>
console.log(file)
);
console.log();
console.log("Files with requiresMainQueueSetup = NO: ");
filesExplicitlyNotRequiringMainQueueSetup.forEach((file) =>
console.log(file)
);
}
if (!module.parent) {
main();
}
```
Changelog: [Internal]
Reviewed By: fkgozali
Differential Revision: D21797048
fbshipit-source-id: a822a858fecdbe976e6197f8339e509dc7cd917f
2020-06-03 08:58:04 +03:00
|
|
|
});
|
2019-11-01 21:54:44 +03:00
|
|
|
});
|
Run getConstants method statements on main queue
Summary:
If a NativeModule requires main queue setup, its `getConstants()` method must be executed on the main thead. The legacy NativeModule infra takes care of this for us. With TurboModules, however, all synchronous methods, including `getConstants()`, execute on the JS thread. Therefore, if a TurboModule requires main queue setup, and exports constants, we must execute its `getConstants()` method body on the main queue explicitly.
**Notes:**
- The changes in this diff should be a noop when TurboModules is off, because `RCTUnsafeExecuteOnMainQueueSync` synchronously execute its block on the current thread if the current thread is the main thread.
- If a NativeModule doens't have the `requiresMainQueueSetup` method, but has the `constantsToExport` method, both NativeModules and TurboModules assume that it requires main queue setup.
## Script
```
const exec = require("../lib/exec");
const abspath = require("../lib/abspath");
const relpath = require("../lib/relpath");
const readFile = (filename) => require("fs").readFileSync(filename, "utf8");
const writeFile = (filename, content) =>
require("fs").writeFileSync(filename, content);
function main() {
const tmFiles = exec("cd ~/fbsource && xbgs -n 10000 -l constantsToExport")
.split("\n")
.filter(Boolean);
const filesWithoutConstantsToExport = [];
const filesWithConstantsToExportButNotGetConstants = [];
const filesExplicitlyNotRequiringMainQueueSetup = [];
tmFiles
.filter((filename) => {
if (filename.includes("microsoft-fork-of-react-native")) {
return false;
}
return /\.mm?$/.test(filename);
})
.map(abspath)
.forEach((filename) => {
const code = readFile(filename);
const relFilename = relpath(filename);
if (!/constantsToExport\s*{/.test(code)) {
filesWithoutConstantsToExport.push(relFilename);
return;
}
if (!/getConstants\s*{/.test(code)) {
filesWithConstantsToExportButNotGetConstants.push(relFilename);
return;
}
if (/requiresMainQueueSetup\s*{/.test(code)) {
const requiresMainQueueSetupRegex = /requiresMainQueueSetup\s*{\s*return\s+(?<requiresMainQueueSetup>YES|NO)/;
const requiresMainQueueSetupRegexMatch = requiresMainQueueSetupRegex.exec(
code
);
if (!requiresMainQueueSetupRegexMatch) {
throw new Error(
"Detected requiresMainQueueSetup method in file " +
relFilename +
" but was unable to parse the method return value"
);
}
const {
requiresMainQueueSetup,
} = requiresMainQueueSetupRegexMatch.groups;
if (requiresMainQueueSetup == "NO") {
filesExplicitlyNotRequiringMainQueueSetup.push(relFilename);
return;
}
}
const getConstantsTypeRegex = () => /-\s*\((?<type>.*)\)getConstants\s*{/;
const getConstantsTypeRegexMatch = getConstantsTypeRegex().exec(code);
if (!getConstantsTypeRegexMatch) {
throw new Error(
`Failed to parse return type of getConstants method in file ${relFilename}`
);
}
const getConstantsType = getConstantsTypeRegexMatch.groups.type;
const getConstantsBody = code
.split(getConstantsTypeRegex())[2]
.split("\n}")[0];
const newGetConstantsBody = `
__block ${getConstantsType} constants;
RCTUnsafeExecuteOnMainQueueSync(^{${getConstantsBody
.replace(/\n/g, "\n ")
.replace(/_bridge/g, "self->_bridge")
.replace(/return /g, "constants = ")}
});
return constants;
`;
writeFile(
filename,
code
.replace(getConstantsBody, newGetConstantsBody)
.replace("#import", "#import <React/RCTUtils.h>\n#import")
);
});
console.log("Files without constantsToExport: ");
filesWithoutConstantsToExport.forEach((file) => console.log(file));
console.log();
console.log("Files with constantsToExport but no getConstants: ");
filesWithConstantsToExportButNotGetConstants.forEach((file) =>
console.log(file)
);
console.log();
console.log("Files with requiresMainQueueSetup = NO: ");
filesExplicitlyNotRequiringMainQueueSetup.forEach((file) =>
console.log(file)
);
}
if (!module.parent) {
main();
}
```
Changelog: [Internal]
Reviewed By: fkgozali
Differential Revision: D21797048
fbshipit-source-id: a822a858fecdbe976e6197f8339e509dc7cd917f
2020-06-03 08:58:04 +03:00
|
|
|
|
|
|
|
return constants;
|
2019-11-01 21:54:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
- (facebook::react::ModuleConstants<JS::NativeStatusBarManagerIOS::Constants>)constantsToExport
|
|
|
|
{
|
|
|
|
return (facebook::react::ModuleConstants<JS::NativeStatusBarManagerIOS::Constants>)[self getConstants];
|
|
|
|
}
|
|
|
|
|
Part 2: Update ObjC++ codegen classes to use ObjCTurboModule::InitParams
Summary:
## Summary
Please check out D21035209.
## Changes
- Codemod all ObjC NativeModule `getTurboModuleWithJsInvoker:nativeInvoker:perfLogger` methods to `getTurboModule:(const ObjCTurboModule::Args)`
## Script
```
var withSpaces = (...args) => args.join('\s*')
var regexString = withSpaces(
'-',
'\(',
'std::shared_ptr',
'<',
'(?<turboModuleClass>(facebook::react::|react::|::|)TurboModule)',
'>',
'\)',
'getTurboModuleWithJsInvoker',
':',
'\(',
'std::shared_ptr',
'<',
'(?<fbNamespace>(facebook::react::|react::|::|))CallInvoker',
'>',
'\)',
'(?<jsInvokerInstance>[A-Za-z0-9]+)',
'nativeInvoker',
':',
'\(',
'std::shared_ptr',
'<',
'(facebook::react::|react::|::|)CallInvoker',
'>',
'\)',
'(?<nativeInvokerInstance>[A-Za-z0-9]+)',
'perfLogger',
':',
'\(',
'id',
'<',
'RCTTurboModulePerformanceLogger',
'>',
'\)',
'(?<perfLoggerInstance>[A-Za-z0-9]+)',
'{',
'return',
'std::make_shared',
'<',
'(?<specName>(facebook::react::|react::|::|)Native[%A-Za-z0-9]+SpecJSI)',
'>',
'\(',
'self',
',',
'\k<jsInvokerInstance>',
',',
'\k<nativeInvokerInstance>',
',',
'\k<perfLoggerInstance>',
'\)',
';',
'}',
)
var replaceString = `- (std::shared_ptr<$<turboModuleClass>>) getTurboModule:(const $<fbNamespace>ObjCTurboModule::InitParams &)params
{
return std::make_shared<$<specName>>(params);
}`
const exec = require('../lib/exec');
const abspath = require('../lib/abspath');
const relpath = require('../lib/relpath');
const readFile = (filename) => require('fs').readFileSync(filename, 'utf8');
const writeFile = (filename, content) => require('fs').writeFileSync(filename, content);
function main() {
const tmFiles = exec('cd ~/fbsource && xbgs -n 10000 -l getTurboModuleWithJsInvoker:').split('\n').filter(Boolean);
tmFiles
.filter((filename) => !filename.includes('microsoft-fork-of-react-native'))
.map(abspath)
.forEach((filename) => {
const source = readFile(filename);
const newSource = source.replace(new RegExp(regexString, 'g'), replaceString);
if (source == newSource) {
console.log(relpath(filename));
}
writeFile(filename, newSource);
});
}
if (!module.parent) {
main();
}
```
## Re-generating diff
```
> hg revert -r .^ --all
> node index.js # run script
```
Changelog: [iOS][Changed] - Make all ObjC NativeModules create TurboModules using ObjCTurboModule::Args
Reviewed By: PeteTheHeat
Differential Revision: D21036265
fbshipit-source-id: 404bcc548d1775ef23d793527606d02fe384a0a2
2020-04-17 03:23:39 +03:00
|
|
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
|
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
2019-11-01 21:54:44 +03:00
|
|
|
{
|
Part 2: Update ObjC++ codegen classes to use ObjCTurboModule::InitParams
Summary:
## Summary
Please check out D21035209.
## Changes
- Codemod all ObjC NativeModule `getTurboModuleWithJsInvoker:nativeInvoker:perfLogger` methods to `getTurboModule:(const ObjCTurboModule::Args)`
## Script
```
var withSpaces = (...args) => args.join('\s*')
var regexString = withSpaces(
'-',
'\(',
'std::shared_ptr',
'<',
'(?<turboModuleClass>(facebook::react::|react::|::|)TurboModule)',
'>',
'\)',
'getTurboModuleWithJsInvoker',
':',
'\(',
'std::shared_ptr',
'<',
'(?<fbNamespace>(facebook::react::|react::|::|))CallInvoker',
'>',
'\)',
'(?<jsInvokerInstance>[A-Za-z0-9]+)',
'nativeInvoker',
':',
'\(',
'std::shared_ptr',
'<',
'(facebook::react::|react::|::|)CallInvoker',
'>',
'\)',
'(?<nativeInvokerInstance>[A-Za-z0-9]+)',
'perfLogger',
':',
'\(',
'id',
'<',
'RCTTurboModulePerformanceLogger',
'>',
'\)',
'(?<perfLoggerInstance>[A-Za-z0-9]+)',
'{',
'return',
'std::make_shared',
'<',
'(?<specName>(facebook::react::|react::|::|)Native[%A-Za-z0-9]+SpecJSI)',
'>',
'\(',
'self',
',',
'\k<jsInvokerInstance>',
',',
'\k<nativeInvokerInstance>',
',',
'\k<perfLoggerInstance>',
'\)',
';',
'}',
)
var replaceString = `- (std::shared_ptr<$<turboModuleClass>>) getTurboModule:(const $<fbNamespace>ObjCTurboModule::InitParams &)params
{
return std::make_shared<$<specName>>(params);
}`
const exec = require('../lib/exec');
const abspath = require('../lib/abspath');
const relpath = require('../lib/relpath');
const readFile = (filename) => require('fs').readFileSync(filename, 'utf8');
const writeFile = (filename, content) => require('fs').writeFileSync(filename, content);
function main() {
const tmFiles = exec('cd ~/fbsource && xbgs -n 10000 -l getTurboModuleWithJsInvoker:').split('\n').filter(Boolean);
tmFiles
.filter((filename) => !filename.includes('microsoft-fork-of-react-native'))
.map(abspath)
.forEach((filename) => {
const source = readFile(filename);
const newSource = source.replace(new RegExp(regexString, 'g'), replaceString);
if (source == newSource) {
console.log(relpath(filename));
}
writeFile(filename, newSource);
});
}
if (!module.parent) {
main();
}
```
## Re-generating diff
```
> hg revert -r .^ --all
> node index.js # run script
```
Changelog: [iOS][Changed] - Make all ObjC NativeModules create TurboModules using ObjCTurboModule::Args
Reviewed By: PeteTheHeat
Differential Revision: D21036265
fbshipit-source-id: 404bcc548d1775ef23d793527606d02fe384a0a2
2020-04-17 03:23:39 +03:00
|
|
|
return std::make_shared<facebook::react::NativeStatusBarManagerIOSSpecJSI>(params);
|
2019-11-01 21:54:44 +03:00
|
|
|
}
|
|
|
|
|
2015-02-20 07:10:52 +03:00
|
|
|
@end
|
2019-11-01 21:54:44 +03:00
|
|
|
|
2020-03-09 08:58:01 +03:00
|
|
|
Class RCTStatusBarManagerCls(void)
|
|
|
|
{
|
2019-11-01 21:54:44 +03:00
|
|
|
return RCTStatusBarManager.class;
|
|
|
|
}
|