2015-02-04 updates
- Unbreak ReactKit | Nick Lockwood - Refactor | Nick Lockwood - [ReactNative] fix touch cancel behavior | Spencer Ahrens - [ReactNative iOS] Fix responder issue with textInput | Eric Vicenti - [ReactNative] README updates - file watching troubleshooting, one-time code drop -> currently private repo | Spencer Ahrens - [ReactKit] Re-add README linebreaks | Ben Alpert
This commit is contained in:
parent
6153fffb30
commit
fd8b7dee77
|
@ -16,7 +16,7 @@
|
|||
* @providesModule ResponderEventPlugin
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var EventConstants = require('EventConstants');
|
||||
var EventPluginUtils = require('EventPluginUtils');
|
||||
|
@ -27,6 +27,7 @@ var ResponderSyntheticEvent = require('ResponderSyntheticEvent');
|
|||
var ResponderTouchHistoryStore = require('ResponderTouchHistoryStore');
|
||||
|
||||
var accumulate = require('accumulate');
|
||||
var invariant = require('invariant');
|
||||
var keyOf = require('keyOf');
|
||||
|
||||
var isStartish = EventPluginUtils.isStartish;
|
||||
|
@ -43,6 +44,12 @@ var executeDispatchesInOrderStopAtTrue =
|
|||
*/
|
||||
var responderID = null;
|
||||
|
||||
/**
|
||||
* Count of current touches. A textInput should become responder iff the
|
||||
* the selection changes while there is a touch on the screen.
|
||||
*/
|
||||
var trackedTouchCount = 0;
|
||||
|
||||
/**
|
||||
* Last reported number of active touches.
|
||||
*/
|
||||
|
@ -426,7 +433,8 @@ function setResponderAndExtractTransfer(
|
|||
function canTriggerTransfer(topLevelType, topLevelTargetID) {
|
||||
return topLevelTargetID && (
|
||||
topLevelType === EventConstants.topLevelTypes.topScroll ||
|
||||
topLevelType === EventConstants.topLevelTypes.topSelectionChange ||
|
||||
(trackedTouchCount > 0 &&
|
||||
topLevelType === EventConstants.topLevelTypes.topSelectionChange) ||
|
||||
isStartish(topLevelType) ||
|
||||
isMoveish(topLevelType)
|
||||
);
|
||||
|
@ -489,6 +497,15 @@ var ResponderEventPlugin = {
|
|||
topLevelTargetID,
|
||||
nativeEvent) {
|
||||
|
||||
if (isStartish(topLevelType)) {
|
||||
trackedTouchCount += 1;
|
||||
} else if (isEndish(topLevelType)) {
|
||||
trackedTouchCount -= 1;
|
||||
invariant(
|
||||
trackedTouchCount >= 0,
|
||||
'Ended a touch event which was not counted in trackedTouchCount.'
|
||||
);
|
||||
}
|
||||
|
||||
ResponderTouchHistoryStore.recordTouchTrack(topLevelType, nativeEvent);
|
||||
|
||||
|
@ -507,12 +524,10 @@ var ResponderEventPlugin = {
|
|||
// (`onResponderRelease/onResponderTerminate`).
|
||||
var isResponderTouchStart = responderID && isStartish(topLevelType);
|
||||
var isResponderTouchMove = responderID && isMoveish(topLevelType);
|
||||
var isResponderTouchTerminate = responderID && topLevelType === EventConstants.topLevelTypes.topTouchCancel;
|
||||
var isResponderTouchEnd = responderID && isEndish(topLevelType);
|
||||
var incrementalTouch =
|
||||
isResponderTouchStart ? eventTypes.responderStart :
|
||||
isResponderTouchMove ? eventTypes.responderMove :
|
||||
isResponderTouchTerminate ? eventTypes.responderTerminate :
|
||||
isResponderTouchEnd ? eventTypes.responderEnd :
|
||||
null;
|
||||
|
||||
|
@ -524,17 +539,24 @@ var ResponderEventPlugin = {
|
|||
extracted = accumulate(extracted, gesture);
|
||||
}
|
||||
|
||||
var isResponderTerminate =
|
||||
responderID &&
|
||||
topLevelType === EventConstants.topLevelTypes.topTouchCancel;
|
||||
var isResponderRelease =
|
||||
responderID && isEndish(topLevelType) && noResponderTouches(nativeEvent);
|
||||
if (isResponderRelease) {
|
||||
var release = ResponderSyntheticEvent.getPooled(
|
||||
eventTypes.responderRelease,
|
||||
responderID,
|
||||
nativeEvent
|
||||
);
|
||||
release.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
EventPropagators.accumulateDirectDispatches(release);
|
||||
extracted = accumulate(extracted, release);
|
||||
responderID &&
|
||||
!isResponderTerminate &&
|
||||
isEndish(topLevelType) &&
|
||||
noResponderTouches(nativeEvent);
|
||||
var finalTouch =
|
||||
isResponderTerminate ? eventTypes.responderTerminate :
|
||||
isResponderRelease ? eventTypes.responderRelease :
|
||||
null;
|
||||
if (finalTouch) {
|
||||
var finalEvent =
|
||||
ResponderSyntheticEvent.getPooled(finalTouch, responderID, nativeEvent);
|
||||
finalEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
EventPropagators.accumulateDirectDispatches(finalEvent);
|
||||
extracted = accumulate(extracted, finalEvent);
|
||||
changeResponder(null);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* Copyright 2013-2014 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @providesModule ExecutionEnvironment
|
||||
*
|
||||
* Stubs SignedSource<<7afee88a05412d0c4eef54817419648e>>
|
||||
*/
|
||||
|
||||
/*jslint evil: true */
|
||||
|
||||
"use strict";
|
||||
|
||||
var canUseDOM = false;
|
||||
|
||||
/**
|
||||
* Simple, lightweight module assisting with the detection and context of
|
||||
* Worker. Helps avoid circular dependencies and allows code to reason about
|
||||
* whether or not they are in a Worker, even if they never include the main
|
||||
* `ReactWorker` dependency.
|
||||
*/
|
||||
var ExecutionEnvironment = {
|
||||
|
||||
canUseDOM: canUseDOM,
|
||||
|
||||
canUseWorkers: typeof Worker !== 'undefined',
|
||||
|
||||
canUseEventListeners:
|
||||
canUseDOM && !!(window.addEventListener || window.attachEvent),
|
||||
|
||||
canUseViewport: canUseDOM && !!window.screen,
|
||||
|
||||
isInWorker: !canUseDOM // For now, this is true - might change in the future.
|
||||
|
||||
};
|
||||
|
||||
module.exports = ExecutionEnvironment;
|
|
@ -2,13 +2,12 @@
|
|||
* @providesModule Touchable
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
var BoundingDimensions = require('BoundingDimensions');
|
||||
var Position = require('Position');
|
||||
var TouchEventUtils = require('TouchEventUtils');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var keyMirror = require('keyMirror');
|
||||
var queryLayoutByID = require('queryLayoutByID');
|
||||
|
||||
|
@ -277,20 +276,20 @@ var LONG_PRESS_ALLOWED_MOVEMENT = 10;
|
|||
* +
|
||||
* | RESPONDER_GRANT (HitRect)
|
||||
* v
|
||||
* +---------------------------+ DELAY +-------------------------+ T - DELAY +------------------------------+
|
||||
* |RESPONDER_INACTIVE_PRESS_IN|+-------->|RESPONDER_ACTIVE_PRESS_IN| +------------> |RESPONDER_ACTIVE_LONG_PRESS_IN|
|
||||
* +---------------------------+ DELAY +-------------------------+ T - DELAY +------------------------------+
|
||||
* |RESPONDER_INACTIVE_PRESS_IN|+-------->|RESPONDER_ACTIVE_PRESS_IN| +------------> |RESPONDER_ACTIVE_LONG_PRESS_IN|
|
||||
* +---------------------------+ +-------------------------+ +------------------------------+
|
||||
* + ^ + ^ + ^
|
||||
* |LEAVE_ |ENTER_ |LEAVE_ |ENTER_ |LEAVE_ |ENTER_
|
||||
* |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT
|
||||
* | | | | | |
|
||||
* v + v + v +
|
||||
* +----------------------------+ DELAY +--------------------------+ +-------------------------------+
|
||||
* |RESPONDER_INACTIVE_PRESS_OUT|+------->|RESPONDER_ACTIVE_PRESS_OUT| |RESPONDER_ACTIVE_LONG_PRESS_OUT|
|
||||
* +----------------------------+ +--------------------------+ +-------------------------------+
|
||||
* +----------------------------+ DELAY +--------------------------+ +-------------------------------+
|
||||
* |RESPONDER_INACTIVE_PRESS_OUT|+------->|RESPONDER_ACTIVE_PRESS_OUT| |RESPONDER_ACTIVE_LONG_PRESS_OUT|
|
||||
* +----------------------------+ +--------------------------+ +-------------------------------+
|
||||
*
|
||||
* T - DELAY => LONG_PRESS_THRESHOLD - DELAY
|
||||
*
|
||||
* T - DELAY => LONG_PRESS_THRESHOLD - DELAY
|
||||
*
|
||||
* Not drawn are the side effects of each transition. The most important side
|
||||
* effect is the `touchableHandlePress` abstract method invocation that occurs
|
||||
* when a responder is released while in either of the "Press" states.
|
||||
|
@ -344,7 +343,7 @@ var TouchableMixin = {
|
|||
*
|
||||
*/
|
||||
touchableHandleResponderGrant: function(e, dispatchID) {
|
||||
// Since e is used in a callback invoked on another event loop
|
||||
// Since e is used in a callback invoked on another event loop
|
||||
// (as in setTimeout etc), we need to call e.persist() on the
|
||||
// event to make sure it doesn't get reused in the event object pool.
|
||||
e.persist();
|
||||
|
@ -420,9 +419,9 @@ var TouchableMixin = {
|
|||
var movedDistance = this._getDistanceBetweenPoints(pageX, pageY, this.pressInLocation.pageX, this.pressInLocation.pageY);
|
||||
if (movedDistance > LONG_PRESS_ALLOWED_MOVEMENT) {
|
||||
this._cancelLongPressDelayTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var isTouchWithinActive =
|
||||
pageX > positionOnActivate.left - pressExpandLeft &&
|
||||
pageY > positionOnActivate.top - pressExpandTop &&
|
||||
|
@ -556,18 +555,19 @@ var TouchableMixin = {
|
|||
*/
|
||||
_receiveSignal: function(signal, e) {
|
||||
var curState = this.state.touchable.touchState;
|
||||
invariant(
|
||||
Transitions[curState] && Transitions[curState][signal],
|
||||
'You have supplied either an unrecognized signal or current state %s',
|
||||
curState
|
||||
);
|
||||
if (!(Transitions[curState] && Transitions[curState][signal])) {
|
||||
throw new Error(
|
||||
'Unrecognized signal `' + signal + '` or state `' + curState +
|
||||
'` for Touchable responder `' + this.state.touchable.responderID + '`'
|
||||
);
|
||||
}
|
||||
var nextState = Transitions[curState][signal];
|
||||
invariant(
|
||||
nextState !== States.ERROR,
|
||||
'Some assumptions about the state machine were violated. This is the ' +
|
||||
'fault of Touchable.js. This case has been modeled and caught as an ' +
|
||||
'error transition.'
|
||||
);
|
||||
if (nextState === States.ERROR) {
|
||||
throw new Error(
|
||||
'Touchable cannot transition from `' + curState + '` to `' + signal +
|
||||
'` for responder `' + this.state.touchable.responderID + '`'
|
||||
);
|
||||
}
|
||||
if (curState !== nextState) {
|
||||
this._performSideEffectsForTransition(curState, nextState, signal, e);
|
||||
this.state.touchable.touchState = nextState;
|
||||
|
@ -580,7 +580,7 @@ var TouchableMixin = {
|
|||
},
|
||||
|
||||
_isHighlight: function (state) {
|
||||
return state === States.RESPONDER_ACTIVE_PRESS_IN ||
|
||||
return state === States.RESPONDER_ACTIVE_PRESS_IN ||
|
||||
state === States.RESPONDER_ACTIVE_LONG_PRESS_IN;
|
||||
},
|
||||
|
||||
|
@ -626,15 +626,15 @@ var TouchableMixin = {
|
|||
|
||||
if (IsPressingIn[curState] && signal === Signals.LONG_PRESS_DETECTED) {
|
||||
this.touchableHandleLongPress && this.touchableHandleLongPress();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (newIsHighlight && !curIsHighlight) {
|
||||
this._savePressInLocation(e);
|
||||
this.touchableHandleActivePressIn && this.touchableHandleActivePressIn();
|
||||
} else if (!newIsHighlight && curIsHighlight) {
|
||||
this.touchableHandleActivePressOut && this.touchableHandleActivePressOut();
|
||||
}
|
||||
|
||||
|
||||
if (IsPressingIn[curState] && signal === Signals.RESPONDER_RELEASE) {
|
||||
var hasLongPressHandler = !!this.touchableHandleLongPress;
|
||||
var pressIsLongButStillCallOnPress =
|
||||
|
@ -660,4 +660,3 @@ var Touchable = {
|
|||
};
|
||||
|
||||
module.exports = Touchable;
|
||||
|
||||
|
|
35
README.md
35
README.md
|
@ -1,6 +1,6 @@
|
|||
**Warning: This is a one-time code drop to accompany the ReactJS conference
|
||||
**Warning: This is currently a private repo to accompany the ReactJS conference
|
||||
talk, and is not accessible outside the official conference attendees - please
|
||||
don't share this code.**
|
||||
do not share this code.**
|
||||
|
||||
This is also a very early alpha release. There are certainly bugs and missing
|
||||
features. Some things may even be well-documented in JS, but missing from the
|
||||
|
@ -67,11 +67,14 @@ the same library.**
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
Xcode will break if you have two examples open at the same time.
|
||||
|
||||
Jest testing does not yet work on node versions after 0.10.x.
|
||||
|
||||
You can verify the packager is working by loading the [bundle](http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle) in your browser and
|
||||
+ Xcode will break if you have two examples open at the same time.
|
||||
+ If `npm start` fails with log spew like:
|
||||
```
|
||||
2015-02-02 10:56 node[24294] (FSEvents.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-21)
|
||||
```
|
||||
then you've hit the node file watching bug - `brew install watchman` should fix the issue.
|
||||
+ Jest testing does not yet work on node versions after 0.10.x.
|
||||
+ You can verify the packager is working by loading the [bundle](http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle) in your browser and
|
||||
inspecting the contents.
|
||||
|
||||
Please report any other issues you encounter so we can fix them ASAP.
|
||||
|
@ -92,27 +95,27 @@ the responder system.
|
|||
|
||||
# FAQ
|
||||
|
||||
Q. How does debugging work? Can I set breakpoints in my JS?
|
||||
Q. How does debugging work? Can I set breakpoints in my JS?
|
||||
A. We are going to add the ability to use the Chrome developer tools soon. We
|
||||
are very passionate about building the best possible developer experience.
|
||||
|
||||
Q. When is this coming to Android/Windows/OS X/etc?
|
||||
Q. When is this coming to Android/Windows/OS X/etc?
|
||||
A. We're working on Android, and we are excited to release it as soon as we can.
|
||||
We are looking forward to the community helping us target other platforms as
|
||||
well :)
|
||||
|
||||
Q. How do I create my own app?
|
||||
Q. How do I create my own app?
|
||||
A. Copy the entire `Examples/TicTacToe` folder, rename stuff in Xcode, and
|
||||
replace the `TicTacToeApp.js` with your own. Then, in `AppDelegate.m`, update
|
||||
`moduleName` to match your call to
|
||||
`Bundler.registerComponent(<moduleName>, <componentName>)` at the bottom of your
|
||||
JS file, and update `jsCodeLocation` to match your JS file name and location.
|
||||
|
||||
Q. Can I submit my own React Native app to the App Store?
|
||||
Q. Can I submit my own React Native app to the App Store?
|
||||
A. Not yet, but you will be able to soon. If you build something you want to
|
||||
submit to the App Store, come talk to us ASAP.
|
||||
|
||||
Q. How do I deploy to my device?
|
||||
Q. How do I deploy to my device?
|
||||
A. You can change `localhost` in `AppDelegate.m` to your laptop's IP address and
|
||||
grab the bundle over the same Wi-Fi network. You can also download the bundle
|
||||
that the React packager generates, save it to the file `main.jsbundle`, and add it
|
||||
|
@ -120,20 +123,20 @@ as a static resource in your Xcode project. Then set the `jsCodeLocation` in
|
|||
`AppDelegate.m` to point to that file and deploy to your device like you would
|
||||
any other app.
|
||||
|
||||
Q. What's up with this private repo? Why aren't you just open sourcing it now?
|
||||
Q. What's up with this private repo? Why aren't you just open sourcing it now?
|
||||
A. We want input from the React community before we open the floodgates so we
|
||||
can incorporate your feedback, and we also have a bunch more features we want to
|
||||
add to make a more complete offering before we open source.
|
||||
|
||||
Q. Do you have to ship a JS runtime with your apps?
|
||||
Q. Do you have to ship a JS runtime with your apps?
|
||||
A. No, we just use the JavaScriptCore public API that is part of iOS 7 and
|
||||
later.
|
||||
|
||||
Q. How do I add more native capabilities?
|
||||
Q. How do I add more native capabilities?
|
||||
A. React Native is designed to be extensible - come talk to us, we would love to
|
||||
work with you.
|
||||
|
||||
Q. Can I reuse existing iOS code?
|
||||
Q. Can I reuse existing iOS code?
|
||||
A. Yes, React Native is designed to be extensible and allow integration of all
|
||||
sorts of native components, such as `UINavigationController` (available as
|
||||
`<NavigatorIOS>`), `MKMapView` (not available yet), or your own custom
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTExport.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTInvalidating.h"
|
||||
#import "RCTJavaScriptExecutor.h"
|
||||
|
||||
@protocol RCTNativeModule;
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
@class RCTRootView;
|
||||
|
||||
|
@ -29,31 +27,65 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
|
|||
}
|
||||
|
||||
/**
|
||||
* Async batched bridge used to communicate with `RCTJavaScriptAppEngine`.
|
||||
* Async batched bridge used to communicate with the JavaScript application.
|
||||
*/
|
||||
@interface RCTBridge : NSObject <RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor;
|
||||
/**
|
||||
* The designated initializer. This creates a new bridge on top of the specified
|
||||
* executor. The bridge should then be used for all subsequent communication
|
||||
* with the JavaScript code running in the executor.
|
||||
*/
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* This method is used to call functions in the JavaScript application context.
|
||||
* It is primarily intended for use by modules that require two-way communication
|
||||
* with the JavaScript code.
|
||||
*/
|
||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args;
|
||||
|
||||
/**
|
||||
* This method is used to execute a new application script. It is called
|
||||
* internally whenever a JS application bundle is loaded/reloaded, but should
|
||||
* probably not be used at any other time.
|
||||
*/
|
||||
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
|
||||
/**
|
||||
* The event dispatcher is a wrapper around -enqueueJSCall:args: that provides a
|
||||
* higher-level interface for sending UI events such as touches and text input.
|
||||
*/
|
||||
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
|
||||
|
||||
/**
|
||||
* The shadow queue is used to execute callbacks from the JavaScript code. All
|
||||
* native hooks (e.g. exported module methods) will be executed on the shadow
|
||||
* queue.
|
||||
*/
|
||||
@property (nonatomic, readonly) dispatch_queue_t shadowQueue;
|
||||
|
||||
// For use in implementing delegates, which may need to queue responses.
|
||||
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)callbackID;
|
||||
|
||||
/**
|
||||
* Register a root view with the bridge. Theorectically, a single bridge can
|
||||
* support multiple root views, however this feature is not currently exposed
|
||||
* and may eventually be removed.
|
||||
*/
|
||||
- (void)registerRootView:(RCTRootView *)rootView;
|
||||
|
||||
/**
|
||||
* Global logging function will print to both xcode and js debugger consoles.
|
||||
* Global logging function that will print to both xcode and JS debugger consoles.
|
||||
*
|
||||
* NOTE: Use via RCTLog* macros defined in RCTLog.h
|
||||
* TODO (#5906496): should log function be exposed here, or could it be a module?
|
||||
*/
|
||||
+ (void)log:(NSArray *)objects level:(NSString *)level;
|
||||
|
||||
/**
|
||||
* Method to check that a valid executor exists with which to log
|
||||
*/
|
||||
+ (BOOL)hasValidJSExecutor;
|
||||
|
||||
@end
|
||||
|
|
|
@ -2,27 +2,18 @@
|
|||
|
||||
#import "RCTBridge.h"
|
||||
|
||||
#import <dlfcn.h>
|
||||
#import <mach-o/getsect.h>
|
||||
#import <mach-o/dyld.h>
|
||||
#import <objc/message.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "RCTModuleMethod.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTInvalidating.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
/**
|
||||
* Functions are the one thing that aren't automatically converted to OBJC
|
||||
* blocks, according to this revert: http://trac.webkit.org/changeset/144489
|
||||
* They must be expressed as `JSValue`s.
|
||||
*
|
||||
* But storing callbacks causes reference cycles!
|
||||
* http://stackoverflow.com/questions/19202248/how-can-i-use-jsmanagedvalue-to-avoid-a-reference-cycle-without-the-jsvalue-gett
|
||||
* We'll live with the leak for now, but need to clean this up asap:
|
||||
* Passing a reference to the `context` to the bridge would make it easy to
|
||||
* execute JS. We can add `JSManagedValue`s to protect against this. The same
|
||||
* needs to be done in `RCTTiming` and friends.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Must be kept in sync with `MessageQueue.js`.
|
||||
*/
|
||||
|
@ -35,13 +26,148 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
|||
RCTBridgeFieldFlushDateMillis
|
||||
};
|
||||
|
||||
static NSDictionary *RCTNativeModuleClasses(void)
|
||||
/**
|
||||
* This private class is used as a container for exported method info
|
||||
*/
|
||||
@interface RCTModuleMethod : NSObject
|
||||
|
||||
@property (readonly, nonatomic, assign) SEL selector;
|
||||
@property (readonly, nonatomic, copy) NSString *JSMethodName;
|
||||
@property (readonly, nonatomic, assign) NSUInteger arity;
|
||||
@property (readonly, nonatomic, copy) NSIndexSet *blockArgumentIndexes;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTModuleMethod
|
||||
|
||||
- (instancetype)initWithSelector:(SEL)selector
|
||||
JSMethodName:(NSString *)JSMethodName
|
||||
arity:(NSUInteger)arity
|
||||
blockArgumentIndexes:(NSIndexSet *)blockArgumentIndexes
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_selector = selector;
|
||||
_JSMethodName = [JSMethodName copy];
|
||||
_arity = arity;
|
||||
_blockArgumentIndexes = [blockArgumentIndexes copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
NSString *blocks = @"no block args";
|
||||
if (self.blockArgumentIndexes.count > 0) {
|
||||
NSMutableString *indexString = [NSMutableString string];
|
||||
[self.blockArgumentIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
||||
[indexString appendFormat:@", %tu", idx];
|
||||
}];
|
||||
blocks = [NSString stringWithFormat:@"block args at %@", [indexString substringFromIndex:2]];
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat:@"<%@: %p; exports -%@ as %@; %@>", NSStringFromClass(self.class), self, NSStringFromSelector(self.selector), self.JSMethodName, blocks];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#ifdef __LP64__
|
||||
typedef uint64_t RCTExportValue;
|
||||
typedef struct section_64 RCTExportSection;
|
||||
#define RCTGetSectByNameFromHeader getsectbynamefromheader_64
|
||||
#else
|
||||
typedef uint32_t RCTExportValue;
|
||||
typedef struct section RCTExportSection;
|
||||
#define RCTGetSectByNameFromHeader getsectbynamefromheader
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function parses the exported methods inside RCTBridgeModules and
|
||||
* generates a dictionary of arrays of RCTModuleMethod objects, keyed
|
||||
* by module name.
|
||||
*/
|
||||
static NSDictionary *RCTExportedMethodsByModule(void)
|
||||
{
|
||||
static NSMutableDictionary *methodsByModule;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
Dl_info info;
|
||||
dladdr(&RCTExportedMethodsByModule, &info);
|
||||
|
||||
const RCTExportValue mach_header = (RCTExportValue)info.dli_fbase;
|
||||
const RCTExportSection *section = RCTGetSectByNameFromHeader((void *)mach_header, "__DATA", "RCTExport");
|
||||
|
||||
if (section == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
methodsByModule = [NSMutableDictionary dictionary];
|
||||
NSCharacterSet *plusMinusCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"+-"];
|
||||
|
||||
for (RCTExportValue addr = section->offset;
|
||||
addr < section->offset + section->size;
|
||||
addr += sizeof(id) * 2) {
|
||||
|
||||
const char **entry = (const char **)(mach_header + addr);
|
||||
NSScanner *scanner = [NSScanner scannerWithString:@(entry[0])];
|
||||
|
||||
NSString *plusMinus;
|
||||
if (![scanner scanCharactersFromSet:plusMinusCharacterSet intoString:&plusMinus]) continue;
|
||||
if (![scanner scanString:@"[" intoString:NULL]) continue;
|
||||
|
||||
NSString *className;
|
||||
if (![scanner scanUpToString:@" " intoString:&className]) continue;
|
||||
[scanner scanString:@" " intoString:NULL];
|
||||
|
||||
NSString *selectorName;
|
||||
if (![scanner scanUpToString:@"]" intoString:&selectorName]) continue;
|
||||
|
||||
Class class = NSClassFromString(className);
|
||||
if (class == Nil) continue;
|
||||
|
||||
SEL selector = NSSelectorFromString(selectorName);
|
||||
Method method = ([plusMinus characterAtIndex:0] == '+' ? class_getClassMethod : class_getInstanceMethod)(class, selector);
|
||||
if (method == nil) continue;
|
||||
|
||||
unsigned int argumentCount = method_getNumberOfArguments(method);
|
||||
NSMutableIndexSet *blockArgumentIndexes = [NSMutableIndexSet indexSet];
|
||||
static const char *blockType = @encode(typeof(^{}));
|
||||
for (unsigned int i = 2; i < argumentCount; i++) {
|
||||
char *type = method_copyArgumentType(method, i);
|
||||
if (!strcmp(type, blockType)) {
|
||||
[blockArgumentIndexes addIndex:i - 2];
|
||||
}
|
||||
free(type);
|
||||
}
|
||||
|
||||
NSString *JSMethodName = strlen(entry[1]) ? @(entry[1]) : [NSStringFromSelector(selector) componentsSeparatedByString:@":"][0];
|
||||
RCTModuleMethod *moduleMethod =
|
||||
[[RCTModuleMethod alloc] initWithSelector:selector
|
||||
JSMethodName:JSMethodName
|
||||
arity:method_getNumberOfArguments(method) - 2
|
||||
blockArgumentIndexes:blockArgumentIndexes];
|
||||
|
||||
NSString *moduleName = [class respondsToSelector:@selector(moduleName)] ? [class moduleName] : className;
|
||||
NSArray *moduleMap = methodsByModule[moduleName];
|
||||
methodsByModule[moduleName] = (moduleMap != nil) ? [moduleMap arrayByAddingObject:moduleMethod] : @[moduleMethod];
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return methodsByModule;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function scans all classes available at runtime and returns a dictionary
|
||||
* of classes that implement the RTCBridgeModule protocol, keyed by module name.
|
||||
*/
|
||||
static NSDictionary *RCTBridgeModuleClasses(void)
|
||||
{
|
||||
static NSMutableDictionary *modules;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
modules = [NSMutableDictionary dictionary];
|
||||
|
||||
|
||||
unsigned int classCount;
|
||||
Class *classes = objc_copyClassList(&classCount);
|
||||
for (unsigned int i = 0; i < classCount; i++) {
|
||||
|
@ -53,17 +179,17 @@ static NSDictionary *RCTNativeModuleClasses(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (![cls conformsToProtocol:@protocol(RCTNativeModule)]) {
|
||||
// Not an RCTNativeModule
|
||||
if (![cls conformsToProtocol:@protocol(RCTBridgeModule)]) {
|
||||
// Not an RCTBridgeModule
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get module name
|
||||
NSString *moduleName = [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
|
||||
|
||||
|
||||
// Check module name is unique
|
||||
id existingClass = modules[moduleName];
|
||||
RCTCAssert(existingClass == Nil, @"Attempted to register RCTNativeModule class %@ for the name '%@', but name was already registered by class %@", cls, moduleName, existingClass);
|
||||
RCTCAssert(existingClass == Nil, @"Attempted to register RCTBridgeModule class %@ for the name '%@', but name was already registered by class %@", cls, moduleName, existingClass);
|
||||
modules[moduleName] = cls;
|
||||
}
|
||||
|
||||
|
@ -73,11 +199,166 @@ static NSDictionary *RCTNativeModuleClasses(void)
|
|||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructs the remote modules configuration data structure,
|
||||
* which represents the native modules and methods that will be called
|
||||
* by JS. A numeric ID is assigned to each module and method, which will
|
||||
* be used to communicate via the bridge. The structure of each
|
||||
* module is as follows:
|
||||
*
|
||||
* "ModuleName1": {
|
||||
* "moduleID": 0,
|
||||
* "methods": {
|
||||
* "methodName1": {
|
||||
* "methodID": 0,
|
||||
* "type": "remote"
|
||||
* },
|
||||
* "methodName2": {
|
||||
* "methodID": 1,
|
||||
* "type": "remote"
|
||||
* },
|
||||
* etc...
|
||||
* },
|
||||
* "constants": {
|
||||
* ...
|
||||
* }
|
||||
* },
|
||||
* etc...
|
||||
*/
|
||||
static NSMutableDictionary *RCTRemoteModulesByID;
|
||||
static NSDictionary *RCTRemoteModulesConfig()
|
||||
{
|
||||
static NSMutableDictionary *remoteModules;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
RCTRemoteModulesByID = [[NSMutableDictionary alloc] init];
|
||||
|
||||
remoteModules = [[NSMutableDictionary alloc] init];
|
||||
[RCTExportedMethodsByModule() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, NSArray *rawMethods, BOOL *stop) {
|
||||
|
||||
NSMutableDictionary *methods = [NSMutableDictionary dictionaryWithCapacity:rawMethods.count];
|
||||
[rawMethods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger methodID, BOOL *stop) {
|
||||
methods[method.JSMethodName] = @{
|
||||
@"methodID": @(methodID),
|
||||
@"type": @"remote",
|
||||
};
|
||||
}];
|
||||
|
||||
NSDictionary *module = @{
|
||||
@"moduleID": @(remoteModules.count),
|
||||
@"methods": methods
|
||||
};
|
||||
|
||||
Class cls = RCTBridgeModuleClasses()[moduleName];
|
||||
if (RCTClassOverridesClassMethod(cls, @selector(constantsToExport))) {
|
||||
module = [module mutableCopy];
|
||||
((NSMutableDictionary *)module)[@"constants"] = [cls constantsToExport];
|
||||
}
|
||||
remoteModules[moduleName] = module;
|
||||
|
||||
// Add module lookup
|
||||
RCTRemoteModulesByID[module[@"moduleID"]] = moduleName;
|
||||
|
||||
}];
|
||||
});
|
||||
|
||||
return remoteModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* As above, but for local modules/methods, which represent JS classes
|
||||
* and methods that will be called by the native code via the bridge.
|
||||
* Structure is essentially the same as for remote modules:
|
||||
*
|
||||
* "ModuleName1": {
|
||||
* "moduleID": 0,
|
||||
* "methods": {
|
||||
* "methodName1": {
|
||||
* "methodID": 0,
|
||||
* "type": "local"
|
||||
* },
|
||||
* "methodName2": {
|
||||
* "methodID": 1,
|
||||
* "type": "local"
|
||||
* },
|
||||
* etc...
|
||||
* }
|
||||
* },
|
||||
* etc...
|
||||
*/
|
||||
static NSMutableDictionary *RCTLocalModuleIDs;
|
||||
static NSMutableDictionary *RCTLocalMethodIDs;
|
||||
static NSDictionary *RCTLocalModulesConfig()
|
||||
{
|
||||
static NSMutableDictionary *localModules;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
RCTLocalModuleIDs = [[NSMutableDictionary alloc] init];
|
||||
RCTLocalMethodIDs = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSMutableArray *JSMethods = [[NSMutableArray alloc] init];
|
||||
|
||||
// Add globally used methods
|
||||
[JSMethods addObjectsFromArray:@[
|
||||
@"Bundler.runApplication",
|
||||
@"RCTEventEmitter.receiveEvent",
|
||||
@"RCTEventEmitter.receiveTouches",
|
||||
]];
|
||||
|
||||
// NOTE: these methods are currently unused in the OSS project
|
||||
// @"Dimensions.set",
|
||||
// @"RCTDeviceEventEmitter.emit",
|
||||
// @"RCTNativeAppEventEmitter.emit",
|
||||
// @"ReactIOS.unmountComponentAtNodeAndRemoveContainer",
|
||||
|
||||
// Register individual methods from modules
|
||||
for (Class cls in RCTBridgeModuleClasses().allValues) {
|
||||
if (RCTClassOverridesClassMethod(cls, @selector(JSMethods))) {
|
||||
[JSMethods addObjectsFromArray:[cls JSMethods]];
|
||||
}
|
||||
}
|
||||
|
||||
localModules = [[NSMutableDictionary alloc] init];
|
||||
for (NSString *moduleDotMethod in JSMethods) {
|
||||
|
||||
NSArray *parts = [moduleDotMethod componentsSeparatedByString:@"."];
|
||||
RCTCAssert(parts.count == 2, @"'%@' is not a valid JS method definition - expected 'Module.method' format.", moduleDotMethod);
|
||||
|
||||
// Add module if it doesn't already exist
|
||||
NSString *moduleName = parts[0];
|
||||
NSDictionary *module = localModules[moduleName];
|
||||
if (!module) {
|
||||
module = @{
|
||||
@"moduleID": @(localModules.count),
|
||||
@"methods": [[NSMutableDictionary alloc] init]
|
||||
};
|
||||
localModules[moduleName] = module;
|
||||
}
|
||||
|
||||
// Add method if it doesn't already exist
|
||||
NSString *methodName = parts[1];
|
||||
NSMutableDictionary *methods = module[@"methods"];
|
||||
if (!methods[methodName]) {
|
||||
methods[methodName] = @{
|
||||
@"methodID": @(methods.count),
|
||||
@"type": @"local"
|
||||
};
|
||||
}
|
||||
|
||||
// Add module and method lookup
|
||||
RCTLocalModuleIDs[moduleDotMethod] = module[@"moduleID"];
|
||||
RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@"methodID"];
|
||||
}
|
||||
});
|
||||
|
||||
return localModules;
|
||||
}
|
||||
|
||||
@implementation RCTBridge
|
||||
{
|
||||
NSMutableDictionary *_moduleInstances;
|
||||
NSMutableDictionary *_moduleIDLookup;
|
||||
NSMutableDictionary *_methodIDLookup;
|
||||
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||
}
|
||||
|
||||
|
@ -90,10 +371,10 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
_latestJSExecutor = _javaScriptExecutor;
|
||||
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
|
||||
// Instantiate modules
|
||||
_moduleInstances = [[NSMutableDictionary alloc] init];
|
||||
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
[RCTBridgeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
if (_moduleInstances[moduleName] == nil) {
|
||||
if ([moduleClass instancesRespondToSelector:@selector(initWithBridge:)]) {
|
||||
_moduleInstances[moduleName] = [[moduleClass alloc] initWithBridge:self];
|
||||
|
@ -103,17 +384,27 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
}
|
||||
}];
|
||||
|
||||
_moduleIDLookup = [[NSMutableDictionary alloc] init];
|
||||
_methodIDLookup = [[NSMutableDictionary alloc] init];
|
||||
[self doneRegisteringModules];
|
||||
// Inject module data into JS context
|
||||
NSString *configJSON = RCTJSONStringify(@{
|
||||
@"remoteModuleConfig": RCTRemoteModulesConfig(),
|
||||
@"localModulesConfig": RCTLocalModulesConfig()
|
||||
}, NULL);
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
[_javaScriptExecutor injectJSONText:configJSON asGlobalObjectNamed:@"__fbBatchedBridgeConfig" callback:^(id err) {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)) != 0) {
|
||||
RCTLogMustFix(@"JavaScriptExecutor took too long to inject JSON object");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
RCTAssert(!self.valid, @"must call -invalidate before -dealloc; TODO: why not call it here then?");
|
||||
RCTAssert(!self.valid, @"must call -invalidate before -dealloc");
|
||||
}
|
||||
|
||||
#pragma mark - RCTInvalidating
|
||||
|
@ -129,7 +420,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
_latestJSExecutor = nil;
|
||||
}
|
||||
_javaScriptExecutor = nil;
|
||||
|
||||
|
||||
dispatch_sync(_shadowQueue, ^{
|
||||
// Make sure all dispatchers have been executed before continuing
|
||||
// TODO: is this still needed?
|
||||
|
@ -157,11 +448,11 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
*/
|
||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
||||
{
|
||||
NSNumber *moduleID = _moduleIDLookup[moduleDotMethod];
|
||||
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
|
||||
RCTAssert(moduleID, @"Module '%@' not registered.",
|
||||
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
|
||||
|
||||
NSNumber *methodID = _methodIDLookup[moduleDotMethod];
|
||||
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
|
||||
RCTAssert(methodID, @"Method '%@' not registered.", moduleDotMethod);
|
||||
|
||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||
|
@ -177,7 +468,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
onComplete(scriptLoadError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
[_javaScriptExecutor executeJSCall:@"BatchedBridge"
|
||||
method:@"flushedQueue"
|
||||
arguments:@[]
|
||||
|
@ -193,19 +484,19 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
|
||||
{
|
||||
NSTimeInterval startJS = RCTTGetAbsoluteTime();
|
||||
|
||||
|
||||
RCTJavaScriptCallback processResponse = ^(id objcValue, NSError *error) {
|
||||
NSTimeInterval startNative = RCTTGetAbsoluteTime();
|
||||
[self _handleBuffer:objcValue];
|
||||
|
||||
|
||||
NSTimeInterval end = RCTTGetAbsoluteTime();
|
||||
NSTimeInterval timeJS = startNative - startJS;
|
||||
NSTimeInterval timeNative = end - startNative;
|
||||
|
||||
|
||||
// TODO: surface this performance information somewhere
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"PERF" object:nil userInfo:@{@"JS": @(timeJS * 1000000), @"Native": @(timeNative * 1000000)}];
|
||||
};
|
||||
|
||||
|
||||
[_javaScriptExecutor executeJSCall:module
|
||||
method:method
|
||||
arguments:args
|
||||
|
@ -229,12 +520,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
if (buffer == nil || buffer == (id)kCFNull) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (![buffer isKindOfClass:[NSArray class]]) {
|
||||
RCTLogMustFix(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class]));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NSArray *requestsArray = (NSArray *)buffer;
|
||||
NSUInteger bufferRowCount = [requestsArray count];
|
||||
NSUInteger expectedFieldsCount = RCTBridgeFieldResponseReturnValues + 1;
|
||||
|
@ -242,7 +533,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
RCTLogMustFix(@"Must pass all fields to buffer - expected %zd, saw %zd", expectedFieldsCount, bufferRowCount);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (NSUInteger fieldIndex = RCTBridgeFieldRequestModuleIDs; fieldIndex <= RCTBridgeFieldParamss; fieldIndex++) {
|
||||
id field = [requestsArray objectAtIndex:fieldIndex];
|
||||
if (![field isKindOfClass:[NSArray class]]) {
|
||||
|
@ -250,27 +541,27 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs];
|
||||
NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs];
|
||||
NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss];
|
||||
|
||||
|
||||
NSUInteger numRequests = [moduleIDs count];
|
||||
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count];
|
||||
if (!allSame) {
|
||||
RCTLogMustFix(@"Invalid data message - all must be length: %zd", numRequests);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (NSUInteger i = 0; i < numRequests; i++) {
|
||||
@autoreleasepool {
|
||||
[self _handleRequestNumber:i
|
||||
moduleID:[moduleIDs[i] integerValue]
|
||||
moduleID:moduleIDs[i]
|
||||
methodID:[methodIDs[i] integerValue]
|
||||
params:paramsArrays[i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
for (id target in _moduleInstances.objectEnumerator) {
|
||||
|
@ -282,7 +573,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
}
|
||||
|
||||
- (BOOL)_handleRequestNumber:(NSUInteger)i
|
||||
moduleID:(NSInteger)moduleID
|
||||
moduleID:(NSNumber *)moduleID
|
||||
methodID:(NSInteger)methodID
|
||||
params:(NSArray *)params
|
||||
{
|
||||
|
@ -291,13 +582,15 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
return NO;
|
||||
}
|
||||
|
||||
if (moduleID < 0 || moduleID >= RCTExportedMethodsByModule().count) {
|
||||
NSString *moduleName = RCTRemoteModulesByID[moduleID];
|
||||
if (!moduleName) {
|
||||
RCTLogMustFix(@"Unknown moduleID: %@", moduleID);
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *moduleName = RCTExportedModuleNameAtSortedIndex(moduleID);
|
||||
NSArray *methods = RCTExportedMethodsByModule()[moduleName];
|
||||
if (methodID < 0 || methodID >= methods.count) {
|
||||
if (methodID >= methods.count) {
|
||||
RCTLogMustFix(@"Unknown methodID: %zd for module: %@", methodID, moduleName);
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
@ -321,13 +614,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
// invocation should not continue.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// TODO: we should just store module instances by index, since that's how we look them up anyway
|
||||
id target = strongSelf->_moduleInstances[moduleName];
|
||||
RCTAssert(target != nil, @"No module found for name '%@'", moduleName);
|
||||
|
||||
SEL selector = method.selector;
|
||||
|
||||
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
[invocation setArgument:&target atIndex:0];
|
||||
|
@ -335,7 +627,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
|
||||
// Retain used blocks until after invocation completes.
|
||||
NS_VALID_UNTIL_END_OF_SCOPE NSMutableArray *blocks = [NSMutableArray array];
|
||||
|
||||
|
||||
[params enumerateObjectsUsingBlock:^(id param, NSUInteger idx, BOOL *stop) {
|
||||
if ([param isEqual:[NSNull null]]) {
|
||||
param = nil;
|
||||
|
@ -344,9 +636,11 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
[blocks addObject:block];
|
||||
param = block;
|
||||
}
|
||||
|
||||
|
||||
NSUInteger argIdx = idx + 2;
|
||||
|
||||
|
||||
// TODO: can we do this lookup in advance and cache the logic instead of
|
||||
// recalculating it every time for every parameter?
|
||||
BOOL shouldSet = YES;
|
||||
const char *argumentType = [methodSignature getArgumentTypeAtIndex:argIdx];
|
||||
switch (argumentType[0]) {
|
||||
|
@ -357,7 +651,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
shouldSet = NO;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case '*':
|
||||
if ([param isKindOfClass:[NSString class]]) {
|
||||
const char *string = [param UTF8String];
|
||||
|
@ -365,36 +659,38 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
shouldSet = NO;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// TODO: it seems like an error if the param doesn't respond
|
||||
// so we should probably surface that error rather than failing silently
|
||||
#define CASE(_value, _type, _selector) \
|
||||
case _value: \
|
||||
if ([param respondsToSelector:@selector(_selector)]) { \
|
||||
_type value = [param _selector]; \
|
||||
[invocation setArgument:&value atIndex:argIdx]; \
|
||||
shouldSet = NO; \
|
||||
} \
|
||||
break;
|
||||
|
||||
CASE('c', char, charValue)
|
||||
CASE('C', unsigned char, unsignedCharValue)
|
||||
CASE('s', short, shortValue)
|
||||
CASE('S', unsigned short, unsignedShortValue)
|
||||
CASE('i', int, intValue)
|
||||
CASE('I', unsigned int, unsignedIntValue)
|
||||
CASE('l', long, longValue)
|
||||
CASE('L', unsigned long, unsignedLongValue)
|
||||
CASE('q', long long, longLongValue)
|
||||
CASE('Q', unsigned long long, unsignedLongLongValue)
|
||||
CASE('f', float, floatValue)
|
||||
CASE('d', double, doubleValue)
|
||||
CASE('B', BOOL, boolValue)
|
||||
|
||||
case _value: \
|
||||
if ([param respondsToSelector:@selector(_selector)]) { \
|
||||
_type value = [param _selector]; \
|
||||
[invocation setArgument:&value atIndex:argIdx]; \
|
||||
shouldSet = NO; \
|
||||
} \
|
||||
break;
|
||||
|
||||
CASE('c', char, charValue)
|
||||
CASE('C', unsigned char, unsignedCharValue)
|
||||
CASE('s', short, shortValue)
|
||||
CASE('S', unsigned short, unsignedShortValue)
|
||||
CASE('i', int, intValue)
|
||||
CASE('I', unsigned int, unsignedIntValue)
|
||||
CASE('l', long, longValue)
|
||||
CASE('L', unsigned long, unsignedLongValue)
|
||||
CASE('q', long long, longLongValue)
|
||||
CASE('Q', unsigned long long, unsignedLongLongValue)
|
||||
CASE('f', float, floatValue)
|
||||
CASE('d', double, doubleValue)
|
||||
CASE('B', BOOL, boolValue)
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (shouldSet) {
|
||||
[invocation setArgument:¶m atIndex:argIdx];
|
||||
[invocation setArgument:¶m atIndex:argIdx];
|
||||
}
|
||||
}];
|
||||
|
||||
|
@ -420,7 +716,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
if (!cbID) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
return ^(NSArray *args) {
|
||||
[self _sendResponseToJavaScriptCallbackID:cbID args:args];
|
||||
};
|
||||
|
@ -433,7 +729,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
dispatch_once(&onceToken, ^{
|
||||
invocations = [NSMutableDictionary dictionary];
|
||||
});
|
||||
|
||||
|
||||
id key = @(argCount);
|
||||
NSInvocation *invocation = invocations[key];
|
||||
if (invocation == nil) {
|
||||
|
@ -442,170 +738,10 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
invocations[key] = invocation;
|
||||
}
|
||||
|
||||
|
||||
return invocation;
|
||||
}
|
||||
|
||||
- (NSArray *)JSMethods
|
||||
{
|
||||
NSMutableArray *methods = [[NSMutableArray alloc] init];
|
||||
|
||||
// Add globally used methods
|
||||
[methods addObjectsFromArray:@[
|
||||
@"Bundler.runApplication",
|
||||
@"RCTEventEmitter.receiveEvent",
|
||||
@"RCTEventEmitter.receiveTouches",
|
||||
]];
|
||||
|
||||
// NOTE: these methods are currently unused in the OSS project
|
||||
// @"Dimensions.set",
|
||||
// @"RCTDeviceEventEmitter.emit",
|
||||
// @"RCTNativeAppEventEmitter.emit",
|
||||
// @"ReactIOS.unmountComponentAtNodeAndRemoveContainer",
|
||||
|
||||
// Register individual methods from modules
|
||||
for (Class cls in RCTNativeModuleClasses().allValues) {
|
||||
if (RCTClassOverridesClassMethod(cls, @selector(JSMethods))) {
|
||||
[methods addObjectsFromArray:[cls JSMethods]];
|
||||
}
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
- (void)doneRegisteringModules
|
||||
{
|
||||
// TODO: everything in this method can actually be determined
|
||||
// statically from just the class, and can therefore be cached
|
||||
// in a dispatch_once instead of being repeated every time we
|
||||
// reload or create a new RootView.
|
||||
|
||||
RCTAssertMainThread();
|
||||
|
||||
/**
|
||||
* This constructs the remote modules configuration data structure,
|
||||
* which represents the native modules and methods that will be called
|
||||
* by JS. A numeric ID is assigned to each module and method, which will
|
||||
* be used to communicate via the bridge. The structure of each
|
||||
* module is as follows:
|
||||
*
|
||||
* "ModuleName1": {
|
||||
* "moduleID": 0,
|
||||
* "methods": {
|
||||
* "methodName1": {
|
||||
* "methodID": 0,
|
||||
* "type": "remote"
|
||||
* },
|
||||
* "methodName2": {
|
||||
* "methodID": 1,
|
||||
* "type": "remote"
|
||||
* },
|
||||
* etc...
|
||||
* },
|
||||
* "constants": {
|
||||
* ...
|
||||
* }
|
||||
* },
|
||||
* etc...
|
||||
*/
|
||||
|
||||
NSUInteger moduleCount = RCTExportedMethodsByModule().count;
|
||||
NSMutableDictionary *remoteModules = [NSMutableDictionary dictionaryWithCapacity:RCTExportedMethodsByModule().count];
|
||||
for (NSUInteger i = 0; i < moduleCount; i++) {
|
||||
NSString *moduleName = RCTExportedModuleNameAtSortedIndex(i);
|
||||
NSArray *rawMethods = RCTExportedMethodsByModule()[moduleName];
|
||||
NSMutableDictionary *methods = [NSMutableDictionary dictionaryWithCapacity:rawMethods.count];
|
||||
[rawMethods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger methodID, BOOL *stop) {
|
||||
methods[method.JSMethodName] = @{
|
||||
@"methodID": @(methodID),
|
||||
@"type": @"remote",
|
||||
};
|
||||
}];
|
||||
|
||||
NSDictionary *module = @{
|
||||
@"moduleID": @(i),
|
||||
@"methods": methods
|
||||
};
|
||||
|
||||
id target = _moduleInstances[moduleName];
|
||||
if (RCTClassOverridesClassMethod([target class], @selector(constantsToExport))) {
|
||||
module = [module mutableCopy];
|
||||
((NSMutableDictionary *)module)[@"constants"] = [[target class] constantsToExport];
|
||||
}
|
||||
remoteModules[moduleName] = module;
|
||||
}
|
||||
|
||||
/**
|
||||
* As above, but for local modules/methods, which represent JS classes
|
||||
* and methods that will be called by the native code via the bridge.
|
||||
* Structure is essentially the same as for remote modules:
|
||||
*
|
||||
* "ModuleName1": {
|
||||
* "moduleID": 0,
|
||||
* "methods": {
|
||||
* "methodName1": {
|
||||
* "methodID": 0,
|
||||
* "type": "local"
|
||||
* },
|
||||
* "methodName2": {
|
||||
* "methodID": 1,
|
||||
* "type": "local"
|
||||
* },
|
||||
* etc...
|
||||
* }
|
||||
* },
|
||||
* etc...
|
||||
*/
|
||||
|
||||
NSMutableDictionary *localModules = [[NSMutableDictionary alloc] init];
|
||||
for (NSString *moduleDotMethod in [self JSMethods]) {
|
||||
|
||||
NSArray *parts = [moduleDotMethod componentsSeparatedByString:@"."];
|
||||
RCTAssert(parts.count == 2, @"'%@' is not a valid JS method definition - expected 'Module.method' format.", moduleDotMethod);
|
||||
|
||||
// Add module if it doesn't already exist
|
||||
NSString *moduleName = parts[0];
|
||||
NSDictionary *module = localModules[moduleName];
|
||||
if (!module) {
|
||||
module = @{
|
||||
@"moduleID": @(localModules.count),
|
||||
@"methods": [[NSMutableDictionary alloc] init]
|
||||
};
|
||||
localModules[moduleName] = module;
|
||||
}
|
||||
|
||||
// Add method if it doesn't already exist
|
||||
NSString *methodName = parts[1];
|
||||
NSMutableDictionary *methods = module[@"methods"];
|
||||
if (!methods[methodName]) {
|
||||
methods[methodName] = @{
|
||||
@"methodID": @(methods.count),
|
||||
@"type": @"local"
|
||||
};
|
||||
}
|
||||
|
||||
// Add module and method lookup
|
||||
_moduleIDLookup[moduleDotMethod] = module[@"moduleID"];
|
||||
_methodIDLookup[moduleDotMethod] = methods[methodName][@"methodID"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject module data into JS context
|
||||
*/
|
||||
NSString *configJSON = RCTJSONStringify(@{
|
||||
@"remoteModuleConfig": remoteModules,
|
||||
@"localModulesConfig": localModules
|
||||
}, NULL);
|
||||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
[_javaScriptExecutor injectJSONText:configJSON asGlobalObjectNamed:@"__fbBatchedBridgeConfig" callback:^(id err) {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)) != 0) {
|
||||
RCTLogMustFix(@"JavaScriptExecutor take too long to inject JSON object");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)registerRootView:(RCTRootView *)rootView
|
||||
{
|
||||
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||
|
@ -628,7 +764,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
return;
|
||||
}
|
||||
NSMutableArray *args = [NSMutableArray arrayWithObject:level];
|
||||
|
||||
|
||||
// TODO (#5906496): Find out and document why we skip the first object
|
||||
for (id ob in [objects subarrayWithRange:(NSRange){1, [objects count] - 1}]) {
|
||||
if ([NSJSONSerialization isValidJSONObject:@[ob]]) {
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class RCTBridge;
|
||||
|
||||
/**
|
||||
* The type of a block that is capable of sending a response to a bridged
|
||||
* operation. Use this for returning callback methods to JS.
|
||||
*/
|
||||
typedef void (^RCTResponseSenderBlock)(NSArray *response);
|
||||
|
||||
/**
|
||||
* Provides minimal interface needed to register a bridge module
|
||||
*/
|
||||
@protocol RCTBridgeModule <NSObject>
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Optional initializer for modules that require access
|
||||
* to bridge features, such as sending events or making JS calls
|
||||
*/
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
/**
|
||||
* The module name exposed to JS. If omitted, this will be inferred
|
||||
* automatically by using the native module's class name.
|
||||
*/
|
||||
+ (NSString *)moduleName;
|
||||
|
||||
/**
|
||||
* Place this macro inside the method body of any method you want to expose
|
||||
* to JS. The optional js_name argument will be used as the JS method name
|
||||
* (the method will be namespaced to the module name, as specified above).
|
||||
* If omitted, the JS method name will match the first part of the Objective-C
|
||||
* method selector name (up to the first colon).
|
||||
*/
|
||||
#define RCT_EXPORT(js_name) __attribute__((used, section("__DATA,RCTExport" \
|
||||
))) static const char *__rct_export_entry__[] = { __func__, #js_name }
|
||||
|
||||
/**
|
||||
* Injects constants into JS. These constants are made accessible via
|
||||
* NativeModules.moduleName.X. Note that this method is not inherited when you
|
||||
* subclass a module, and you should not call [super constantsToExport] when
|
||||
* implementing it.
|
||||
*/
|
||||
+ (NSDictionary *)constantsToExport;
|
||||
|
||||
/**
|
||||
* An array of JavaScript methods that the module will call via the
|
||||
* -[RCTBridge enqueueJSCall:args:] method. Each method should be specified
|
||||
* as a string of the form "JSModuleName.jsMethodName". Attempting to call a
|
||||
* method that has not been registered will result in an error. If a method
|
||||
* has already been regsistered by another module, it is not necessary to
|
||||
* register it again, but it is good pratice. Registering the same method
|
||||
* more than once is silently ignored and will not result in an error.
|
||||
*/
|
||||
+ (NSArray *)JSMethods;
|
||||
|
||||
/**
|
||||
* Notifies the module that a batch of JS method invocations has just completed.
|
||||
*/
|
||||
- (void)batchDidComplete;
|
||||
|
||||
@end
|
|
@ -4,7 +4,13 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "Layout.h"
|
||||
#import "RCTPointerEvents.h"
|
||||
|
||||
/**
|
||||
* This class provides a collection of conversion functions for mapping
|
||||
* JSON objects to native types and classes. These are useful when writing
|
||||
* custom RCTViewManager setter methods.
|
||||
*/
|
||||
@interface RCTConvert : NSObject
|
||||
|
||||
+ (BOOL)BOOL:(id)json;
|
||||
|
@ -57,4 +63,21 @@
|
|||
+ (css_position_type_t)css_position_type_t:(id)json;
|
||||
+ (css_wrap_type_t)css_wrap_type_t:(id)json;
|
||||
|
||||
+ (RCTPointerEvents)RCTPointerEvents:(id)json;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This function will attempt to set a property using a json value by first
|
||||
* inferring the correct type from all available information, and then
|
||||
* applying an appropriate conversion method. If the property does not
|
||||
* exist, or the type cannot be inferred, the function will return NO.
|
||||
*/
|
||||
BOOL RCTSetProperty(id target, NSString *keypath, id json);
|
||||
|
||||
/**
|
||||
* This function attempts to copy a property from the source object to the
|
||||
* destination object using KVC. If the property does not exist, or cannot
|
||||
* be set, it will do nothing and return NO.
|
||||
*/
|
||||
BOOL RCTCopyProperty(id target, id source, NSString *keypath);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#import <objc/message.h>
|
||||
|
||||
#import "RCTLog.h"
|
||||
|
||||
|
@ -41,7 +42,7 @@ RCT_CONVERTER_CUSTOM(type, name, [json getter])
|
|||
return default; \
|
||||
} \
|
||||
if ([json isKindOfClass:[NSNumber class]]) { \
|
||||
if ([[mapping allValues] containsObject:json]) { \
|
||||
if ([[mapping allValues] containsObject:json] || [json getter] == default) { \
|
||||
return [json getter]; \
|
||||
} \
|
||||
RCTLogMustFix(@"Invalid %s '%@'. should be one of: %@", #type, json, [mapping allValues]); \
|
||||
|
@ -651,4 +652,253 @@ RCT_ENUM_CONVERTER(css_wrap_type_t, (@{
|
|||
@"nowrap": @(CSS_NOWRAP)
|
||||
}), CSS_NOWRAP, intValue)
|
||||
|
||||
RCT_ENUM_CONVERTER(RCTPointerEvents, (@{
|
||||
@"none": @(RCTPointerEventsNone),
|
||||
@"boxonly": @(RCTPointerEventsBoxOnly),
|
||||
@"boxnone": @(RCTPointerEventsBoxNone)
|
||||
}), RCTPointerEventsUnspecified, integerValue)
|
||||
|
||||
@end
|
||||
|
||||
static NSString *RCTGuessTypeEncoding(id target, NSString *key, id value, NSString *encoding)
|
||||
{
|
||||
// TODO (#5906496): handle more cases
|
||||
if ([key rangeOfString:@"color" options:NSCaseInsensitiveSearch].location != NSNotFound) {
|
||||
if ([target isKindOfClass:[CALayer class]]) {
|
||||
return @(@encode(CGColorRef));
|
||||
} else {
|
||||
return @"@\"UIColor\"";
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSDictionary *RCTConvertValue(id value, NSString *encoding)
|
||||
{
|
||||
static NSDictionary *converters = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
id (^numberConvert)(id) = ^(id val){
|
||||
return [RCTConvert NSNumber:val];
|
||||
};
|
||||
|
||||
id (^boolConvert)(id) = ^(id val){
|
||||
return @([RCTConvert BOOL:val]);
|
||||
};
|
||||
|
||||
// TODO (#5906496): add the rest of RCTConvert here
|
||||
converters =
|
||||
@{
|
||||
@(@encode(char)): boolConvert,
|
||||
@(@encode(int)): numberConvert,
|
||||
@(@encode(short)): numberConvert,
|
||||
@(@encode(long)): numberConvert,
|
||||
@(@encode(long long)): numberConvert,
|
||||
@(@encode(unsigned char)): numberConvert,
|
||||
@(@encode(unsigned int)): numberConvert,
|
||||
@(@encode(unsigned short)): numberConvert,
|
||||
@(@encode(unsigned long)): numberConvert,
|
||||
@(@encode(unsigned long long)): numberConvert,
|
||||
@(@encode(float)): numberConvert,
|
||||
@(@encode(double)): numberConvert,
|
||||
@(@encode(bool)): boolConvert,
|
||||
@(@encode(UIEdgeInsets)): ^(id val) {
|
||||
return [NSValue valueWithUIEdgeInsets:[RCTConvert UIEdgeInsets:val]];
|
||||
},
|
||||
@(@encode(CGPoint)): ^(id val) {
|
||||
return [NSValue valueWithCGPoint:[RCTConvert CGPoint:val]];
|
||||
},
|
||||
@(@encode(CGSize)): ^(id val) {
|
||||
return [NSValue valueWithCGSize:[RCTConvert CGSize:val]];
|
||||
},
|
||||
@(@encode(CGRect)): ^(id val) {
|
||||
return [NSValue valueWithCGRect:[RCTConvert CGRect:val]];
|
||||
},
|
||||
@(@encode(CGColorRef)): ^(id val) {
|
||||
return (id)[RCTConvert CGColor:val];
|
||||
},
|
||||
@(@encode(CGAffineTransform)): ^(id val) {
|
||||
return [NSValue valueWithCGAffineTransform:[RCTConvert CGAffineTransform:val]];
|
||||
},
|
||||
@(@encode(CATransform3D)): ^(id val) {
|
||||
return [NSValue valueWithCATransform3D:[RCTConvert CATransform3D:val]];
|
||||
},
|
||||
@"@\"NSString\"": ^(id val) {
|
||||
return [RCTConvert NSString:val];
|
||||
},
|
||||
@"@\"NSURL\"": ^(id val) {
|
||||
return [RCTConvert NSURL:val];
|
||||
},
|
||||
@"@\"UIColor\"": ^(id val) {
|
||||
return [RCTConvert UIColor:val];
|
||||
},
|
||||
@"@\"UIImage\"": ^(id val) {
|
||||
return [RCTConvert UIImage:val];
|
||||
},
|
||||
@"@\"NSDate\"": ^(id val) {
|
||||
return [RCTConvert NSDate:val];
|
||||
},
|
||||
@"@\"NSTimeZone\"": ^(id val) {
|
||||
return [RCTConvert NSTimeZone:val];
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Handle null values
|
||||
if (value == [NSNull null] && ![encoding isEqualToString:@"@\"NSNull\""]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Convert value
|
||||
id (^converter)(id) = converters[encoding];
|
||||
return converter ? converter(value) : value;
|
||||
}
|
||||
|
||||
BOOL RCTSetProperty(id target, NSString *keypath, id value)
|
||||
{
|
||||
// Split keypath
|
||||
NSArray *parts = [keypath componentsSeparatedByString:@"."];
|
||||
NSString *key = [parts lastObject];
|
||||
for (NSUInteger i = 0; i < parts.count - 1; i++) {
|
||||
target = [target valueForKey:parts[i]];
|
||||
if (!target) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Check target class for property definition
|
||||
NSString *encoding = nil;
|
||||
objc_property_t property = class_getProperty([target class], [key UTF8String]);
|
||||
if (property) {
|
||||
|
||||
// Get type info
|
||||
char *typeEncoding = property_copyAttributeValue(property, "T");
|
||||
encoding = @(typeEncoding);
|
||||
free(typeEncoding);
|
||||
|
||||
} else {
|
||||
|
||||
// Check if setter exists
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
|
||||
[[key substringToIndex:1] uppercaseString],
|
||||
[key substringFromIndex:1]]);
|
||||
|
||||
if (![target respondsToSelector:setter]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Get type of first method argument
|
||||
Method method = class_getInstanceMethod([target class], setter);
|
||||
char *typeEncoding = method_copyArgumentType(method, 2);
|
||||
if (typeEncoding) {
|
||||
encoding = @(typeEncoding);
|
||||
free(typeEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
if (encoding.length == 0 || [encoding isEqualToString:@(@encode(id))]) {
|
||||
// Not enough info about the type encoding to be useful, so
|
||||
// try to guess the type from the value and property name
|
||||
encoding = RCTGuessTypeEncoding(target, key, value, encoding);
|
||||
}
|
||||
|
||||
// Special case for numeric encodings, which may be enums
|
||||
if ([value isKindOfClass:[NSString class]] &&
|
||||
[@"iIsSlLqQ" containsString:[encoding substringToIndex:1]]) {
|
||||
|
||||
/**
|
||||
* NOTE: the property names below may seem weird, but it's
|
||||
* because they are tested as case-sensitive suffixes, so
|
||||
* "apitalizationType" will match any of the following
|
||||
*
|
||||
* - capitalizationType
|
||||
* - autocapitalizationType
|
||||
* - autoCapitalizationType
|
||||
* - titleCapitalizationType
|
||||
* - etc.
|
||||
*/
|
||||
static NSDictionary *converters = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
converters =
|
||||
@{
|
||||
@"apitalizationType": ^(id val) {
|
||||
return [RCTConvert UITextAutocapitalizationType:val];
|
||||
},
|
||||
@"eyboardType": ^(id val) {
|
||||
return [RCTConvert UIKeyboardType:val];
|
||||
},
|
||||
@"extAlignment": ^(id val) {
|
||||
return [RCTConvert NSTextAlignment:val];
|
||||
},
|
||||
};
|
||||
});
|
||||
for (NSString *subkey in converters) {
|
||||
if ([key hasSuffix:subkey]) {
|
||||
NSInteger (^converter)(NSString *) = converters[subkey];
|
||||
value = @(converter(value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Another nasty special case
|
||||
if ([target isKindOfClass:[UITextField class]]) {
|
||||
static NSDictionary *specialCases = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
specialCases = @{
|
||||
@"autocapitalizationType": ^(UITextField *f, NSInteger v){ f.autocapitalizationType = v; },
|
||||
@"autocorrectionType": ^(UITextField *f, NSInteger v){ f.autocorrectionType = v; },
|
||||
@"spellCheckingType": ^(UITextField *f, NSInteger v){ f.spellCheckingType = v; },
|
||||
@"keyboardType": ^(UITextField *f, NSInteger v){ f.keyboardType = v; },
|
||||
@"keyboardAppearance": ^(UITextField *f, NSInteger v){ f.keyboardAppearance = v; },
|
||||
@"returnKeyType": ^(UITextField *f, NSInteger v){ f.returnKeyType = v; },
|
||||
@"enablesReturnKeyAutomatically": ^(UITextField *f, NSInteger v){ f.enablesReturnKeyAutomatically = !!v; },
|
||||
@"secureTextEntry": ^(UITextField *f, NSInteger v){ f.secureTextEntry = !!v; }};
|
||||
});
|
||||
|
||||
void (^block)(UITextField *f, NSInteger v) = specialCases[key];
|
||||
if (block)
|
||||
{
|
||||
block(target, [value integerValue]);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
// Set converted value
|
||||
[target setValue:RCTConvertValue(value, encoding) forKey:key];
|
||||
return YES;
|
||||
}
|
||||
|
||||
BOOL RCTCopyProperty(id target, id source, NSString *keypath)
|
||||
{
|
||||
// Split keypath
|
||||
NSArray *parts = [keypath componentsSeparatedByString:@"."];
|
||||
NSString *key = [parts lastObject];
|
||||
for (NSUInteger i = 0; i < parts.count - 1; i++) {
|
||||
source = [source valueForKey:parts[i]];
|
||||
target = [target valueForKey:parts[i]];
|
||||
if (!source || !target) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Check class for property definition
|
||||
if (!class_getProperty([source class], [key UTF8String])) {
|
||||
// Check if setter exists
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
|
||||
[[key substringToIndex:1] uppercaseString],
|
||||
[key substringFromIndex:1]]);
|
||||
|
||||
if (![source respondsToSelector:setter]
|
||||
|| ![target respondsToSelector:setter]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
[target setValue:[source valueForKey:key] forKey:key];
|
||||
return YES;
|
||||
}
|
||||
|
|
|
@ -4,13 +4,6 @@
|
|||
|
||||
@class RCTBridge;
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTTouchEventType) {
|
||||
RCTTouchEventTypeStart,
|
||||
RCTTouchEventTypeMove,
|
||||
RCTTouchEventTypeEnd,
|
||||
RCTTouchEventTypeCancel
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTTextEventType) {
|
||||
RCTTextEventTypeFocus,
|
||||
RCTTextEventTypeBlur,
|
||||
|
@ -39,13 +32,6 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) {
|
|||
*/
|
||||
- (void)sendEventWithName:(NSString *)name body:(NSDictionary *)body;
|
||||
|
||||
/**
|
||||
* Send an array of touch events
|
||||
*/
|
||||
- (void)sendTouchEventWithType:(RCTTouchEventType)type
|
||||
touches:(NSArray *)touches
|
||||
changedIndexes:(NSArray *)changedIndexes;
|
||||
|
||||
/**
|
||||
* Send text events
|
||||
*/
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#import "RCTEventDispatcher.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
|
||||
|
@ -27,34 +28,6 @@
|
|||
args:@[body[@"target"], name, body]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs information about touch events to send across the serialized
|
||||
* boundary. This data should be compliant with W3C `Touch` objects. This data
|
||||
* alone isn't sufficient to construct W3C `Event` objects. To construct that,
|
||||
* there must be a simple receiver on the other side of the bridge that
|
||||
* organizes the touch objects into `Event`s.
|
||||
*
|
||||
* We send the data as an array of `Touch`es, the type of action
|
||||
* (start/end/move/cancel) and the indices that represent "changed" `Touch`es
|
||||
* from that array.
|
||||
*/
|
||||
- (void)sendTouchEventWithType:(RCTTouchEventType)type
|
||||
touches:(NSArray *)touches
|
||||
changedIndexes:(NSArray *)changedIndexes
|
||||
{
|
||||
static NSString *events[] = {
|
||||
@"topTouchStart",
|
||||
@"topTouchMove",
|
||||
@"topTouchEnd",
|
||||
@"topTouchCancel",
|
||||
};
|
||||
|
||||
RCTAssert(touches.count, @"No touches in touchEventArgsForOrderedTouches");
|
||||
|
||||
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveTouches"
|
||||
args:@[events[type], touches, changedIndexes]];
|
||||
}
|
||||
|
||||
- (void)sendTextEventWithType:(RCTTextEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
text:(NSString *)text
|
||||
|
|
|
@ -1,226 +0,0 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTLog.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@class RCTSparseArray;
|
||||
@class RCTUIManager;
|
||||
|
||||
typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *viewRegistry);
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
@class RCTShadowView;
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
typedef struct {
|
||||
const char *func;
|
||||
const char *js_name;
|
||||
} RCTExportEntry;
|
||||
|
||||
#define _RCTExportSegmentName "__DATA"
|
||||
#define _RCTExportSectionName "RCTExport"
|
||||
|
||||
extern NSString *RCTExportedModuleNameAtSortedIndex(NSUInteger index);
|
||||
extern NSDictionary *RCTExportedMethodsByModule(void);
|
||||
|
||||
extern BOOL RCTSetProperty(id target, NSString *keypath, id value);
|
||||
extern BOOL RCTCopyProperty(id target, id source, NSString *keypath);
|
||||
extern BOOL RCTCallSetter(id target, SEL setter, id value);
|
||||
/* ------------------------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* The type of a block that is capable of sending a response to a bridged
|
||||
* operation. Use this for returning callback methods to JS.
|
||||
*/
|
||||
typedef void (^RCTResponseSenderBlock)(NSArray *response);
|
||||
|
||||
|
||||
/**
|
||||
* Provides minimal interface needed to register a bridge module
|
||||
*/
|
||||
@protocol RCTNativeModule <NSObject>
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Optional initializer for modules that require access
|
||||
* to bridge features, such as sending events or making JS calls
|
||||
*/
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
/**
|
||||
* Place this macro inside the method body of any method you want
|
||||
* to expose to JS. The optional js_name argument will be used as
|
||||
* the JS function name. If omitted, the JS function name will match
|
||||
* the Objective-C method selector name, up to the first colon.
|
||||
*/
|
||||
#define RCT_EXPORT(js_name) __attribute__((used, section(_RCTExportSegmentName "," \
|
||||
_RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __func__, #js_name }
|
||||
|
||||
/**
|
||||
* The module name exposed to JS. If omitted, this will be inferred
|
||||
* automatically by using the native module's class name.
|
||||
*/
|
||||
+ (NSString *)moduleName;
|
||||
|
||||
/**
|
||||
* Injects constants into JS. These constants are made accessible via
|
||||
* NativeModules.moduleName.X. Note that this method is not inherited when you
|
||||
* subclass a module, and you should not call [super constantsToExport] when
|
||||
* implementing it.
|
||||
*/
|
||||
+ (NSDictionary *)constantsToExport;
|
||||
|
||||
/**
|
||||
* An array of JavaScript methods that the module will call via the
|
||||
* -[RCTBridge enqueueJSCall:args:] method. Each method should be specified
|
||||
* as a string of the form "JSModuleName.jsMethodName". Attempting to call a
|
||||
* method that has not been registered will result in an error. If a method
|
||||
* has already been regsistered by another module, it is not necessary to
|
||||
* register it again, but it is good pratice. Registering the same method
|
||||
* more than once is silently ignored and will not result in an error.
|
||||
*/
|
||||
+ (NSArray *)JSMethods;
|
||||
|
||||
/**
|
||||
* Notifies the module that a batch of JS method invocations has just completed.
|
||||
*/
|
||||
- (void)batchDidComplete;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
* Provides minimal interface needed to register a UIViewManager module
|
||||
*/
|
||||
@protocol RCTNativeViewModule <NSObject>
|
||||
|
||||
/**
|
||||
* Designated initializer for view modules. The event dispatched can either be
|
||||
* used directly by the module, or passed on to instantiated views for event handling.
|
||||
*/
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
||||
|
||||
/**
|
||||
* This method instantiates a native view to be managed by the module. The method
|
||||
* will be called many times, and should return a fresh instance each time. The
|
||||
* view module MUST NOT cache the returned view and return the same instance
|
||||
* for subsequent calls.
|
||||
*/
|
||||
- (UIView *)view;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* The module name exposed to JS. If omitted, this will be inferred
|
||||
* automatically by using the view module's class name.
|
||||
*/
|
||||
+ (NSString *)moduleName;
|
||||
|
||||
/**
|
||||
* This method instantiates a shadow view to be managed by the module. If omitted,
|
||||
* an ordinary RCTShadowView instance will be created. As with the -view method,
|
||||
* the -shadowView method should return a fresh instance each time it is called.
|
||||
*/
|
||||
- (RCTShadowView *)shadowView;
|
||||
|
||||
/**
|
||||
* Informal protocol for setting view and shadowView properties.
|
||||
* Implement methods matching these patterns to set any properties that
|
||||
* require special treatment (e.g. where the type or name cannot be inferred).
|
||||
*
|
||||
* - (void)set_<propertyName>:(id)property
|
||||
* forView:(UIView *)view
|
||||
* withDefaultView:(UIView *)defaultView;
|
||||
*
|
||||
* - (void)set_<propertyName>:(id)property
|
||||
* forShadowView:(RCTShadowView *)view
|
||||
* withDefaultView:(RCTShadowView *)defaultView;
|
||||
*
|
||||
* For simple cases, use the macros below:
|
||||
*/
|
||||
|
||||
/**
|
||||
* This handles the simple case, where JS and native property names match
|
||||
* And the type can be automatically inferred.
|
||||
*/
|
||||
#define RCT_EXPORT_VIEW_PROPERTY(name) \
|
||||
RCT_REMAP_VIEW_PROPERTY(name, name)
|
||||
|
||||
/**
|
||||
* This macro maps a named property on the module to an arbitrary key path
|
||||
* within the view.
|
||||
*/
|
||||
#define RCT_REMAP_VIEW_PROPERTY(name, keypath) \
|
||||
- (void)set_##name:(id)json forView:(id)view withDefaultView:(id)defaultView { \
|
||||
if (json) { \
|
||||
if(!RCTSetProperty(view, @#keypath, json)) { \
|
||||
RCTLogMustFix(@"%@ does not have setter for `%s` property", [view class], #name); \
|
||||
} \
|
||||
} else { \
|
||||
[view setValue:[defaultView valueForKeyPath:@#keypath] forKeyPath:@#keypath]; \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* These are useful in cases where the module's superclass handles a
|
||||
* property, but you wish to "unhandle" it, so it will be ignored.
|
||||
*/
|
||||
#define RCT_IGNORE_VIEW_PROPERTY(name) \
|
||||
- (void)set_##name:(id)value forView:(id)view withDefaultView:(id)defaultView {}
|
||||
|
||||
#define RCT_IGNORE_SHADOW_PROPERTY(name) \
|
||||
- (void)set_##name:(id)value forShadowView:(id)view withDefaultView:(id)defaultView {}
|
||||
|
||||
/**
|
||||
* Returns a dictionary of config data passed to JS that defines eligible events
|
||||
* that can be placed on native views. This should return bubbling
|
||||
* directly-dispatched event types and specify what names should be used to
|
||||
* subscribe to either form (bubbling/capturing).
|
||||
*
|
||||
* Returned dictionary should be of the form: @{
|
||||
* @"onTwirl": {
|
||||
* @"phasedRegistrationNames": @{
|
||||
* @"bubbled": @"onTwirl",
|
||||
* @"captured": @"onTwirlCaptured"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Note that this method is not inherited when you subclass a view module, and
|
||||
* you should not call [super customBubblingEventTypes] when implementing it.
|
||||
*/
|
||||
+ (NSDictionary *)customBubblingEventTypes;
|
||||
|
||||
/**
|
||||
* Returns a dictionary of config data passed to JS that defines eligible events
|
||||
* that can be placed on native views. This should return non-bubbling
|
||||
* directly-dispatched event types.
|
||||
*
|
||||
* Returned dictionary should be of the form: @{
|
||||
* @"onTwirl": {
|
||||
* @"registrationName": @"onTwirl"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Note that this method is not inherited when you subclass a view module, and
|
||||
* you should not call [super customDirectEventTypes] when implementing it.
|
||||
*/
|
||||
+ (NSDictionary *)customDirectEventTypes;
|
||||
|
||||
/**
|
||||
* Injects constants into JS. These constants are made accessible via
|
||||
* NativeModules.moduleName.X. Note that this method is not inherited when you
|
||||
* subclass a view module, and you should not call [super constantsToExport]
|
||||
* when implementing it.
|
||||
*/
|
||||
+ (NSDictionary *)constantsToExport;
|
||||
|
||||
/**
|
||||
* To deprecate, hopefully
|
||||
*/
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry;
|
||||
|
||||
@end
|
|
@ -1,392 +0,0 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTExport.h"
|
||||
|
||||
#import <dlfcn.h>
|
||||
#import <libkern/OSAtomic.h>
|
||||
#import <mach-o/getsect.h>
|
||||
#import <mach-o/dyld.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTModuleMethod.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
static NSDictionary *_methodsByModule;
|
||||
|
||||
@interface _RCTExportLoader : NSObject
|
||||
|
||||
@end
|
||||
|
||||
@implementation _RCTExportLoader
|
||||
|
||||
+ (NSString *)methodNameForSelector:(SEL)selector
|
||||
{
|
||||
NSString *methodName = NSStringFromSelector(selector);
|
||||
NSRange colonRange = [methodName rangeOfString:@":"];
|
||||
if (colonRange.location != NSNotFound) {
|
||||
methodName = [methodName substringToIndex:colonRange.location];
|
||||
}
|
||||
return methodName;
|
||||
}
|
||||
|
||||
+ (NSIndexSet *)blockArgumentIndexesForMethod:(Method)method
|
||||
{
|
||||
unsigned int argumentCount = method_getNumberOfArguments(method);
|
||||
|
||||
NSMutableIndexSet *blockArgumentIndexes = [NSMutableIndexSet indexSet];
|
||||
static const char *blockType = @encode(typeof(^{}));
|
||||
for (unsigned int i = 2; i < argumentCount; i++) {
|
||||
char *type = method_copyArgumentType(method, i);
|
||||
if (!strcmp(type, blockType)) {
|
||||
[blockArgumentIndexes addIndex:i - 2];
|
||||
}
|
||||
free(type);
|
||||
}
|
||||
return [blockArgumentIndexes copy];
|
||||
}
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
static uint32_t _exportsLoaded = 0;
|
||||
if (OSAtomicTestAndSetBarrier(1, &_exportsLoaded)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __LP64__
|
||||
typedef uint64_t RCTExportValue;
|
||||
typedef struct section_64 RCTExportSection;
|
||||
#define RCTGetSectByNameFromHeader getsectbynamefromheader_64
|
||||
#else
|
||||
typedef uint32_t RCTExportValue;
|
||||
typedef struct section RCTExportSection;
|
||||
#define RCTGetSectByNameFromHeader getsectbynamefromheader
|
||||
#endif
|
||||
|
||||
Dl_info info;
|
||||
dladdr(&RCTExportedMethodsByModule, &info);
|
||||
|
||||
const RCTExportValue mach_header = (RCTExportValue)info.dli_fbase;
|
||||
const RCTExportSection *section = RCTGetSectByNameFromHeader((void *)mach_header, _RCTExportSegmentName, _RCTExportSectionName);
|
||||
|
||||
if (section == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableDictionary *methodsByModule = [NSMutableDictionary dictionary];
|
||||
NSCharacterSet *plusMinusCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"+-"];
|
||||
|
||||
for (RCTExportValue addr = section->offset;
|
||||
addr < section->offset + section->size;
|
||||
addr += sizeof(RCTExportEntry)) {
|
||||
|
||||
RCTExportEntry *entry = (RCTExportEntry *)(mach_header + addr);
|
||||
|
||||
NSScanner *scanner = [NSScanner scannerWithString:@(entry->func)];
|
||||
|
||||
NSString *plusMinus;
|
||||
if (![scanner scanCharactersFromSet:plusMinusCharacterSet intoString:&plusMinus]) continue;
|
||||
if (![scanner scanString:@"[" intoString:NULL]) continue;
|
||||
|
||||
NSString *className;
|
||||
if (![scanner scanUpToString:@" " intoString:&className]) continue;
|
||||
[scanner scanString:@" " intoString:NULL];
|
||||
|
||||
NSString *selectorName;
|
||||
if (![scanner scanUpToString:@"]" intoString:&selectorName]) continue;
|
||||
|
||||
Class class = NSClassFromString(className);
|
||||
if (class == Nil) continue;
|
||||
|
||||
SEL selector = NSSelectorFromString(selectorName);
|
||||
Method method = ([plusMinus characterAtIndex:0] == '+' ? class_getClassMethod : class_getInstanceMethod)(class, selector);
|
||||
if (method == nil) continue;
|
||||
|
||||
NSString *JSMethodName = strlen(entry->js_name) ? @(entry->js_name) : [self methodNameForSelector:selector];
|
||||
RCTModuleMethod *moduleMethod =
|
||||
[[RCTModuleMethod alloc] initWithSelector:selector
|
||||
JSMethodName:JSMethodName
|
||||
arity:method_getNumberOfArguments(method) - 2
|
||||
blockArgumentIndexes:[self blockArgumentIndexesForMethod:method]];
|
||||
|
||||
// TODO: store these by class name, not module name, then we don't need to call moduleName here
|
||||
NSString *moduleName = [class respondsToSelector:@selector(moduleName)] ? [class moduleName] : className;
|
||||
NSArray *moduleMap = methodsByModule[moduleName];
|
||||
methodsByModule[moduleName] = (moduleMap != nil) ? [moduleMap arrayByAddingObject:moduleMethod] : @[moduleMethod];
|
||||
}
|
||||
|
||||
_methodsByModule = [methodsByModule copy];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NSDictionary *RCTExportedMethodsByModule(void)
|
||||
{
|
||||
return _methodsByModule;
|
||||
}
|
||||
|
||||
NSString *RCTExportedModuleNameAtSortedIndex(NSUInteger index)
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
static NSArray *sortedModuleNames;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sortedModuleNames = [RCTExportedMethodsByModule().allKeys sortedArrayUsingSelector:@selector(compare:)];
|
||||
});
|
||||
return sortedModuleNames[index];
|
||||
}
|
||||
|
||||
static NSString *RCTGuessTypeEncoding(id target, NSString *key, id value, NSString *encoding)
|
||||
{
|
||||
// TODO (#5906496): handle more cases
|
||||
if ([key rangeOfString:@"color" options:NSCaseInsensitiveSearch].location != NSNotFound) {
|
||||
if ([target isKindOfClass:[CALayer class]]) {
|
||||
return @(@encode(CGColorRef));
|
||||
} else {
|
||||
return @"@\"UIColor\"";
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSDictionary *RCTConvertValue(id value, NSString *encoding)
|
||||
{
|
||||
static NSDictionary *converters = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
id (^numberConvert)(id) = ^(id val){
|
||||
return [RCTConvert NSNumber:val];
|
||||
};
|
||||
|
||||
id (^boolConvert)(id) = ^(id val){
|
||||
return @([RCTConvert BOOL:val]);
|
||||
};
|
||||
|
||||
// TODO (#5906496): add the rest of RCTConvert here
|
||||
converters =
|
||||
@{
|
||||
@(@encode(char)): boolConvert,
|
||||
@(@encode(int)): numberConvert,
|
||||
@(@encode(short)): numberConvert,
|
||||
@(@encode(long)): numberConvert,
|
||||
@(@encode(long long)): numberConvert,
|
||||
@(@encode(unsigned char)): numberConvert,
|
||||
@(@encode(unsigned int)): numberConvert,
|
||||
@(@encode(unsigned short)): numberConvert,
|
||||
@(@encode(unsigned long)): numberConvert,
|
||||
@(@encode(unsigned long long)): numberConvert,
|
||||
@(@encode(float)): numberConvert,
|
||||
@(@encode(double)): numberConvert,
|
||||
@(@encode(bool)): boolConvert,
|
||||
@(@encode(UIEdgeInsets)): ^(id val) {
|
||||
return [NSValue valueWithUIEdgeInsets:[RCTConvert UIEdgeInsets:val]];
|
||||
},
|
||||
@(@encode(CGPoint)): ^(id val) {
|
||||
return [NSValue valueWithCGPoint:[RCTConvert CGPoint:val]];
|
||||
},
|
||||
@(@encode(CGSize)): ^(id val) {
|
||||
return [NSValue valueWithCGSize:[RCTConvert CGSize:val]];
|
||||
},
|
||||
@(@encode(CGRect)): ^(id val) {
|
||||
return [NSValue valueWithCGRect:[RCTConvert CGRect:val]];
|
||||
},
|
||||
@(@encode(CGColorRef)): ^(id val) {
|
||||
return (id)[RCTConvert CGColor:val];
|
||||
},
|
||||
@(@encode(CGAffineTransform)): ^(id val) {
|
||||
return [NSValue valueWithCGAffineTransform:[RCTConvert CGAffineTransform:val]];
|
||||
},
|
||||
@(@encode(CATransform3D)): ^(id val) {
|
||||
return [NSValue valueWithCATransform3D:[RCTConvert CATransform3D:val]];
|
||||
},
|
||||
@"@\"NSString\"": ^(id val) {
|
||||
return [RCTConvert NSString:val];
|
||||
},
|
||||
@"@\"NSURL\"": ^(id val) {
|
||||
return [RCTConvert NSURL:val];
|
||||
},
|
||||
@"@\"UIColor\"": ^(id val) {
|
||||
return [RCTConvert UIColor:val];
|
||||
},
|
||||
@"@\"UIImage\"": ^(id val) {
|
||||
return [RCTConvert UIImage:val];
|
||||
},
|
||||
@"@\"NSDate\"": ^(id val) {
|
||||
return [RCTConvert NSDate:val];
|
||||
},
|
||||
@"@\"NSTimeZone\"": ^(id val) {
|
||||
return [RCTConvert NSTimeZone:val];
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Handle null values
|
||||
if (value == [NSNull null] && ![encoding isEqualToString:@"@\"NSNull\""]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Convert value
|
||||
id (^converter)(id) = converters[encoding];
|
||||
return converter ? converter(value) : value;
|
||||
}
|
||||
|
||||
BOOL RCTSetProperty(id target, NSString *keypath, id value)
|
||||
{
|
||||
// Split keypath
|
||||
NSArray *parts = [keypath componentsSeparatedByString:@"."];
|
||||
NSString *key = [parts lastObject];
|
||||
for (NSUInteger i = 0; i < parts.count - 1; i++) {
|
||||
target = [target valueForKey:parts[i]];
|
||||
if (!target) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Check target class for property definition
|
||||
NSString *encoding = nil;
|
||||
objc_property_t property = class_getProperty([target class], [key UTF8String]);
|
||||
if (property) {
|
||||
|
||||
// Get type info
|
||||
char *typeEncoding = property_copyAttributeValue(property, "T");
|
||||
encoding = @(typeEncoding);
|
||||
free(typeEncoding);
|
||||
|
||||
} else {
|
||||
|
||||
// Check if setter exists
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
|
||||
[[key substringToIndex:1] uppercaseString],
|
||||
[key substringFromIndex:1]]);
|
||||
|
||||
if (![target respondsToSelector:setter]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Get type of first method argument
|
||||
Method method = class_getInstanceMethod([target class], setter);
|
||||
char *typeEncoding = method_copyArgumentType(method, 2);
|
||||
if (typeEncoding) {
|
||||
encoding = @(typeEncoding);
|
||||
free(typeEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
if (encoding.length == 0 || [encoding isEqualToString:@(@encode(id))]) {
|
||||
// Not enough info about the type encoding to be useful, so
|
||||
// try to guess the type from the value and property name
|
||||
encoding = RCTGuessTypeEncoding(target, key, value, encoding);
|
||||
}
|
||||
|
||||
// Special case for numeric encodings, which may be enums
|
||||
if ([value isKindOfClass:[NSString class]] &&
|
||||
[@"iIsSlLqQ" containsString:[encoding substringToIndex:1]]) {
|
||||
|
||||
/**
|
||||
* NOTE: the property names below may seem weird, but it's
|
||||
* because they are tested as case-sensitive suffixes, so
|
||||
* "apitalizationType" will match any of the following
|
||||
*
|
||||
* - capitalizationType
|
||||
* - autocapitalizationType
|
||||
* - autoCapitalizationType
|
||||
* - titleCapitalizationType
|
||||
* - etc.
|
||||
*/
|
||||
static NSDictionary *converters = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
converters =
|
||||
@{
|
||||
@"apitalizationType": ^(id val) {
|
||||
return [RCTConvert UITextAutocapitalizationType:val];
|
||||
},
|
||||
@"eyboardType": ^(id val) {
|
||||
return [RCTConvert UIKeyboardType:val];
|
||||
},
|
||||
@"extAlignment": ^(id val) {
|
||||
return [RCTConvert NSTextAlignment:val];
|
||||
},
|
||||
};
|
||||
});
|
||||
for (NSString *subkey in converters) {
|
||||
if ([key hasSuffix:subkey]) {
|
||||
NSInteger (^converter)(NSString *) = converters[subkey];
|
||||
value = @(converter(value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Another nasty special case
|
||||
if ([target isKindOfClass:[UITextField class]]) {
|
||||
static NSDictionary *specialCases = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
specialCases = @{
|
||||
@"autocapitalizationType": ^(UITextField *f, NSInteger v){ f.autocapitalizationType = v; },
|
||||
@"autocorrectionType": ^(UITextField *f, NSInteger v){ f.autocorrectionType = v; },
|
||||
@"spellCheckingType": ^(UITextField *f, NSInteger v){ f.spellCheckingType = v; },
|
||||
@"keyboardType": ^(UITextField *f, NSInteger v){ f.keyboardType = v; },
|
||||
@"keyboardAppearance": ^(UITextField *f, NSInteger v){ f.keyboardAppearance = v; },
|
||||
@"returnKeyType": ^(UITextField *f, NSInteger v){ f.returnKeyType = v; },
|
||||
@"enablesReturnKeyAutomatically": ^(UITextField *f, NSInteger v){ f.enablesReturnKeyAutomatically = !!v; },
|
||||
@"secureTextEntry": ^(UITextField *f, NSInteger v){ f.secureTextEntry = !!v; }};
|
||||
});
|
||||
|
||||
void (^block)(UITextField *f, NSInteger v) = specialCases[key];
|
||||
if (block)
|
||||
{
|
||||
block(target, [value integerValue]);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
// Set converted value
|
||||
[target setValue:RCTConvertValue(value, encoding) forKey:key];
|
||||
return YES;
|
||||
}
|
||||
|
||||
BOOL RCTCopyProperty(id target, id source, NSString *keypath)
|
||||
{
|
||||
// Split keypath
|
||||
NSArray *parts = [keypath componentsSeparatedByString:@"."];
|
||||
NSString *key = [parts lastObject];
|
||||
for (NSUInteger i = 0; i < parts.count - 1; i++) {
|
||||
source = [source valueForKey:parts[i]];
|
||||
target = [target valueForKey:parts[i]];
|
||||
if (!source || !target) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// Check class for property definition
|
||||
if (!class_getProperty([source class], [key UTF8String])) {
|
||||
// Check if setter exists
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
|
||||
[[key substringToIndex:1] uppercaseString],
|
||||
[key substringFromIndex:1]]);
|
||||
|
||||
if (![source respondsToSelector:setter]
|
||||
|| ![target respondsToSelector:setter]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
[target setValue:[source valueForKey:key] forKey:key];
|
||||
return YES;
|
||||
}
|
||||
|
||||
BOOL RCTCallSetter(id target, SEL setter, id value)
|
||||
{
|
||||
// Get property name
|
||||
NSString *propertyName = NSStringFromSelector(setter);
|
||||
RCTCAssert([propertyName hasPrefix:@"set"] && [propertyName hasSuffix:@":"],
|
||||
@"%@ is not a valid setter name", propertyName);
|
||||
propertyName = [[[propertyName substringWithRange:(NSRange){3,1}] lowercaseString] stringByAppendingString:[propertyName substringWithRange:(NSRange){4,propertyName.length - 5}]];
|
||||
|
||||
// Set property
|
||||
return RCTSetProperty(target, propertyName, value);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTInvalidating.h"
|
||||
#import "RCTJavaScriptExecutor.h"
|
||||
|
||||
@class RCTBridge;
|
||||
|
||||
/**
|
||||
* Class that allows easy embedding, loading, life-cycle management of a
|
||||
* JavaScript application inside of a native application.
|
||||
* TODO: Before loading new application source, publish global notification in
|
||||
* JavaScript so that applications can clean up resources. (launch blocker).
|
||||
* TODO: Incremental module loading. (low pri).
|
||||
*/
|
||||
@interface RCTJavaScriptAppEngine : NSObject
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
- (void)loadBundleAtURL:(NSURL *)moduleURL useCache:(BOOL)useCache onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
+ (void)resetCacheForBundleAtURL:(NSURL *)moduleURL;
|
||||
|
||||
@end
|
|
@ -1,158 +0,0 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTJavaScriptAppEngine.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTInvalidating.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTRedBox.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#define JS_SERVER_NOT_AVAILABLE @"Could not connect to development server. Ensure node server is running - run 'npm start' from ReactKit root"
|
||||
|
||||
#define CACHE_DIR @"RCTJSBundleCache"
|
||||
|
||||
#pragma mark - Application Engine
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* - Add window resize rotation events matching the DOM API.
|
||||
* - Device pixel ration hooks.
|
||||
* - Source maps.
|
||||
*/
|
||||
@implementation RCTJavaScriptAppEngine
|
||||
{
|
||||
NSDictionary *_loadedResource;
|
||||
__weak RCTBridge *_bridge;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
}
|
||||
|
||||
/**
|
||||
* `CADisplayLink` code copied from Ejecta but we've placed the JavaScriptCore
|
||||
* engine in its own dedicated thread.
|
||||
*
|
||||
* TODO: Try adding to the `RCTJavaScriptExecutor`'s thread runloop. Removes one
|
||||
* additional GCD dispatch per frame and likely makes it so that other UIThread
|
||||
* operations don't delay the dispatch (so we can begin working in JS much
|
||||
* faster.) Event handling must still be sent via a GCD dispatch, of course.
|
||||
*
|
||||
* We must add the display link to two runloops in order to get setTimeouts to
|
||||
* fire during scrolling. (`NSDefaultRunLoopMode` and `UITrackingRunLoopMode`)
|
||||
* TODO: We can invent a `requestAnimationFrame` and
|
||||
* `requestAvailableAnimationFrame` to control if callbacks can be fired during
|
||||
* an animation.
|
||||
* http://stackoverflow.com/questions/12622800/why-does-uiscrollview-pause-my-cadisplaylink
|
||||
*
|
||||
*/
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if ((self = [super init])) {
|
||||
_bridge = bridge;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Module and script loading
|
||||
|
||||
+ (void)resetCacheForBundleAtURL:(NSURL *)moduleURL
|
||||
{
|
||||
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
||||
NSString *fileDir = [rootPath stringByAppendingPathComponent:CACHE_DIR];
|
||||
NSString *filePath = [fileDir stringByAppendingPathComponent:RCTMD5Hash(moduleURL.absoluteString)];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:filePath error:NULL];
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: All loading of script via network or disk should be done in a separate
|
||||
* thread, not the JS thread, and not the main UI thread (launch blocker).
|
||||
*/
|
||||
- (void)loadBundleAtURL:(NSURL *)moduleURL useCache:(BOOL)useCache onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
NSString *cachedFilePath;
|
||||
if (useCache) {
|
||||
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
||||
NSString *fileDir = [rootPath stringByAppendingPathComponent:CACHE_DIR];
|
||||
cachedFilePath = [fileDir stringByAppendingPathComponent:RCTMD5Hash(moduleURL.absoluteString)];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:cachedFilePath]) {
|
||||
NSError *error;
|
||||
NSString *rawText = [NSString stringWithContentsOfFile:cachedFilePath encoding:NSUTF8StringEncoding error:&error];
|
||||
if (rawText.length == 0 || error != nil) {
|
||||
if (onComplete) onComplete(error);
|
||||
} else {
|
||||
[self _enqueueLoadBundleResource:rawText url:moduleURL onComplete:onComplete];
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:moduleURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (error != nil) {
|
||||
if ([[error domain] isEqualToString:NSURLErrorDomain]) {
|
||||
NSDictionary *userInfo = @{
|
||||
NSLocalizedDescriptionKey: JS_SERVER_NOT_AVAILABLE,
|
||||
NSLocalizedFailureReasonErrorKey: [error localizedDescription],
|
||||
NSUnderlyingErrorKey: error,
|
||||
};
|
||||
error = [NSError errorWithDomain:@"JSServer"
|
||||
code:error.code
|
||||
userInfo:userInfo];
|
||||
}
|
||||
if (onComplete) onComplete(error);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NSStringEncoding encoding = NSUTF8StringEncoding;
|
||||
if (response.textEncodingName != nil) {
|
||||
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
|
||||
if (cfEncoding != kCFStringEncodingInvalidId) {
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding];
|
||||
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)response statusCode] != 200) {
|
||||
NSDictionary *userInfo;
|
||||
NSDictionary *errorDetails = RCTJSONParse(rawText, nil);
|
||||
if ([errorDetails isKindOfClass:[NSDictionary class]]) {
|
||||
userInfo = @{
|
||||
NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided",
|
||||
@"stack": @[@{
|
||||
@"methodName": errorDetails[@"description"] ?: @"",
|
||||
@"file": errorDetails[@"filename"] ?: @"",
|
||||
@"lineNumber": errorDetails[@"lineNumber"] ?: @0
|
||||
}]
|
||||
};
|
||||
} else {
|
||||
userInfo = @{NSLocalizedDescriptionKey: rawText};
|
||||
}
|
||||
NSError *serverError = [NSError errorWithDomain:@"JSServer"
|
||||
code:[(NSHTTPURLResponse *)response statusCode]
|
||||
userInfo:userInfo];
|
||||
if (onComplete) onComplete(serverError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (useCache) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:cachedFilePath.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:nil error:NULL];
|
||||
[rawText writeToFile:cachedFilePath atomically:YES encoding:encoding error:NULL];
|
||||
}
|
||||
|
||||
[self _enqueueLoadBundleResource:rawText url:moduleURL onComplete:onComplete];
|
||||
}];
|
||||
[task resume];
|
||||
}
|
||||
|
||||
- (void)_enqueueLoadBundleResource:(NSString *)rawText url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
[_bridge enqueueApplicationScript:rawText url:url onComplete:onComplete];
|
||||
}
|
||||
|
||||
@end
|
|
@ -20,28 +20,28 @@
|
|||
// If defined, only log messages that match this regex will fatal
|
||||
#define RCTLOG_FATAL_REGEX nil
|
||||
|
||||
#define _RCTLog(__RCTLog__level, ...) do { \
|
||||
NSString *__RCTLog__levelStr; \
|
||||
switch(__RCTLog__level) { \
|
||||
case RCTLOG_INFO: __RCTLog__levelStr = @"info"; break; \
|
||||
case RCTLOG_WARN: __RCTLog__levelStr = @"warn"; break; \
|
||||
case RCTLOG_ERROR: __RCTLog__levelStr = @"error"; break; \
|
||||
case RCTLOG_MUSTFIX: __RCTLog__levelStr = @"mustfix"; break; \
|
||||
} \
|
||||
NSString *__RCTLog__msg = _RCTLogObjects(RCTLogFormat(__VA_ARGS__), __RCTLog__levelStr); \
|
||||
if (__RCTLog__level >= RCTLOG_FATAL_LEVEL) { \
|
||||
BOOL __RCTLog__fail = YES; \
|
||||
if (RCTLOG_FATAL_REGEX) { \
|
||||
NSError *__RCTLog__e; \
|
||||
#define _RCTLog(__RCTLog__level, ...) do { \
|
||||
NSString *__RCTLog__levelStr; \
|
||||
switch(__RCTLog__level) { \
|
||||
case RCTLOG_INFO: __RCTLog__levelStr = @"info"; break; \
|
||||
case RCTLOG_WARN: __RCTLog__levelStr = @"warn"; break; \
|
||||
case RCTLOG_ERROR: __RCTLog__levelStr = @"error"; break; \
|
||||
case RCTLOG_MUSTFIX: __RCTLog__levelStr = @"mustfix"; break; \
|
||||
} \
|
||||
NSString *__RCTLog__msg = _RCTLogObjects(RCTLogFormat(__VA_ARGS__), __RCTLog__levelStr); \
|
||||
if (__RCTLog__level >= RCTLOG_FATAL_LEVEL) { \
|
||||
BOOL __RCTLog__fail = YES; \
|
||||
if (RCTLOG_FATAL_REGEX) { \
|
||||
NSError *__RCTLog__e; \
|
||||
NSRegularExpression *__RCTLog__regex = [NSRegularExpression regularExpressionWithPattern:RCTLOG_FATAL_REGEX options:0 error:&__RCTLog__e]; \
|
||||
__RCTLog__fail = [__RCTLog__regex numberOfMatchesInString:__RCTLog__msg options:0 range:NSMakeRange(0, [__RCTLog__msg length])] > 0; \
|
||||
} \
|
||||
RCTCAssert(!__RCTLog__fail, @"RCTLOG_FATAL_LEVEL %@: %@", __RCTLog__levelStr, __RCTLog__msg); \
|
||||
} \
|
||||
if (__RCTLog__level >= RCTLOG_REDBOX_LEVEL) { \
|
||||
__RCTLog__fail = [__RCTLog__regex numberOfMatchesInString:__RCTLog__msg options:0 range:NSMakeRange(0, [__RCTLog__msg length])] > 0; \
|
||||
} \
|
||||
RCTCAssert(!__RCTLog__fail, @"RCTLOG_FATAL_LEVEL %@: %@", __RCTLog__levelStr, __RCTLog__msg); \
|
||||
} \
|
||||
if (__RCTLog__level >= RCTLOG_REDBOX_LEVEL) { \
|
||||
RCTRedBox *__RCTLog__redBox = [RCTRedBox sharedInstance]; \
|
||||
[__RCTLog__redBox showErrorMessage:__RCTLog__msg]; \
|
||||
} \
|
||||
[__RCTLog__redBox showErrorMessage:__RCTLog__msg]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RCTLog(...) _RCTLog(RCTLOG_INFO, __VA_ARGS__)
|
||||
|
@ -53,71 +53,13 @@
|
|||
#define RCTLogFormat(...) _RCTLogFormat(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
|
||||
#define RCTLogFormatString(...) _RCTLogFormatString(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
|
||||
|
||||
static inline NSString *_RCTLogPreamble(const char *file, int lineNumber, const char *funcName) {
|
||||
NSString *threadName = [[NSThread currentThread] name];
|
||||
NSString *fileName=[[NSString stringWithUTF8String:file] lastPathComponent];
|
||||
if (!threadName || threadName.length <= 0) {
|
||||
threadName = [NSString stringWithFormat:@"%p", [NSThread currentThread]];
|
||||
}
|
||||
return [NSString stringWithFormat:@"[RCTLog][tid:%@][%@:%d]>", threadName, fileName, lineNumber];
|
||||
}
|
||||
|
||||
// Returns array of objects. First arg is a simple string to print, remaining args are objects to pass through to the debugger so they are
|
||||
// inspectable in the console.
|
||||
static inline NSArray *_RCTLogFormat(const char *file, int lineNumber, const char *funcName, NSString *format, ...) NS_FORMAT_FUNCTION(4,5);
|
||||
static inline NSArray *_RCTLogFormat(const char *file, int lineNumber, const char *funcName, NSString *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString *preamble = _RCTLogPreamble(file, lineNumber, funcName);
|
||||
|
||||
// Pull out NSObjects so we can pass them through as inspectable objects to the js debugger
|
||||
NSArray *formatParts = [format componentsSeparatedByString:@"%"];
|
||||
NSMutableArray *objects = [NSMutableArray arrayWithObject:preamble];
|
||||
BOOL valid = YES;
|
||||
for (int i = 0; i < formatParts.count; i++) {
|
||||
if (i == 0) { // first part is always a string
|
||||
[objects addObject:formatParts[i]];
|
||||
} else {
|
||||
if (valid && [formatParts[i] length] && [formatParts[i] characterAtIndex:0] == '@') {
|
||||
id obj = va_arg(args, id);
|
||||
if (obj) {
|
||||
[objects addObject:obj];
|
||||
} else {
|
||||
[objects addObject:@"null"];
|
||||
}
|
||||
[objects addObject:[formatParts[i] substringFromIndex:1]]; // remove formatting char
|
||||
} else {
|
||||
// We could determine the type (double, int?) of the va_arg by parsing the formatPart, but for now we just bail.
|
||||
valid = NO;
|
||||
[objects addObject:[NSString stringWithFormat:@"unknown object for %%%@", formatParts[i]]];
|
||||
}
|
||||
}
|
||||
}
|
||||
va_end(args);
|
||||
va_start(args, format);
|
||||
NSString *strOut = [preamble stringByAppendingString:[[NSString alloc] initWithFormat:format arguments:args]];
|
||||
va_end(args);
|
||||
NSMutableArray *objectsOut = [NSMutableArray arrayWithObject:strOut];
|
||||
[objectsOut addObjectsFromArray:objects];
|
||||
return objectsOut;
|
||||
}
|
||||
|
||||
static inline NSString *_RCTLogFormatString(const char *, int, const char *, NSString *, ...) NS_FORMAT_FUNCTION(4,5);
|
||||
static inline NSString *_RCTLogFormatString(const char *file, int lineNumber, const char *funcName, NSString *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
NSString *body = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end (args);
|
||||
return [NSString stringWithFormat:@"%@ %@", _RCTLogPreamble(file, lineNumber, funcName), body];
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
NSString *_RCTLogObjects(NSArray *objects, NSString *level);
|
||||
NSArray *_RCTLogFormat(const char *file, int lineNumber, const char *funcName, NSString *format, ...) NS_FORMAT_FUNCTION(4,5);
|
||||
NSString *_RCTLogFormatString(const char *file, int lineNumber, const char *funcName, NSString *format, ...) NS_FORMAT_FUNCTION(4,5);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -10,6 +10,16 @@ void RCTInjectLogFunction(RCTLogFunction func) {
|
|||
injectedLogFunction = func;
|
||||
}
|
||||
|
||||
static inline NSString *_RCTLogPreamble(const char *file, int lineNumber, const char *funcName)
|
||||
{
|
||||
NSString *threadName = [[NSThread currentThread] name];
|
||||
NSString *fileName=[[NSString stringWithUTF8String:file] lastPathComponent];
|
||||
if (!threadName || threadName.length <= 0) {
|
||||
threadName = [NSString stringWithFormat:@"%p", [NSThread currentThread]];
|
||||
}
|
||||
return [NSString stringWithFormat:@"[RCTLog][tid:%@][%@:%d]>", threadName, fileName, lineNumber];
|
||||
}
|
||||
|
||||
// TODO (#5906496): // kinda ugly that this is tied to RCTBridge
|
||||
NSString *_RCTLogObjects(NSArray *objects, NSString *level)
|
||||
{
|
||||
|
@ -31,3 +41,48 @@ NSString *_RCTLogObjects(NSArray *objects, NSString *level)
|
|||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// Returns array of objects. First arg is a simple string to print, remaining args are objects to pass through to the debugger so they are
|
||||
// inspectable in the console.
|
||||
NSArray *_RCTLogFormat(const char *file, int lineNumber, const char *funcName, NSString *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString *preamble = _RCTLogPreamble(file, lineNumber, funcName);
|
||||
|
||||
// Pull out NSObjects so we can pass them through as inspectable objects to the js debugger
|
||||
NSArray *formatParts = [format componentsSeparatedByString:@"%"];
|
||||
NSMutableArray *objects = [NSMutableArray arrayWithObject:preamble];
|
||||
BOOL valid = YES;
|
||||
for (int i = 0; i < formatParts.count; i++) {
|
||||
if (i == 0) { // first part is always a string
|
||||
[objects addObject:formatParts[i]];
|
||||
} else {
|
||||
if (valid && [formatParts[i] length] && [formatParts[i] characterAtIndex:0] == '@') {
|
||||
id obj = va_arg(args, id);
|
||||
[objects addObject:obj ?: @"null"];
|
||||
[objects addObject:[formatParts[i] substringFromIndex:1]]; // remove formatting char
|
||||
} else {
|
||||
// We could determine the type (double, int?) of the va_arg by parsing the formatPart, but for now we just bail.
|
||||
valid = NO;
|
||||
[objects addObject:[NSString stringWithFormat:@"unknown object for %%%@", formatParts[i]]];
|
||||
}
|
||||
}
|
||||
}
|
||||
va_end(args);
|
||||
va_start(args, format);
|
||||
NSString *strOut = [preamble stringByAppendingString:[[NSString alloc] initWithFormat:format arguments:args]];
|
||||
va_end(args);
|
||||
NSMutableArray *objectsOut = [NSMutableArray arrayWithObject:strOut];
|
||||
[objectsOut addObjectsFromArray:objects];
|
||||
return objectsOut;
|
||||
}
|
||||
|
||||
NSString *_RCTLogFormatString(const char *file, int lineNumber, const char *funcName, NSString *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
NSString *body = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end (args);
|
||||
return [NSString stringWithFormat:@"%@ %@", _RCTLogPreamble(file, lineNumber, funcName), body];
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol RCTNativeModule;
|
||||
|
||||
@interface RCTModuleMethod : NSObject
|
||||
|
||||
- (instancetype)initWithSelector:(SEL)selector
|
||||
JSMethodName:(NSString *)JSMethodName
|
||||
arity:(NSUInteger)arity
|
||||
blockArgumentIndexes:(NSIndexSet *)blockArgumentIndexes;
|
||||
|
||||
@property (readonly, nonatomic, assign) SEL selector;
|
||||
@property (readonly, nonatomic, copy) NSString *JSMethodName;
|
||||
@property (readonly, nonatomic, assign) NSUInteger arity;
|
||||
@property (readonly, nonatomic, copy) NSIndexSet *blockArgumentIndexes;
|
||||
|
||||
@end
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTModuleMethod.h"
|
||||
|
||||
@implementation RCTModuleMethod
|
||||
|
||||
- (instancetype)initWithSelector:(SEL)selector
|
||||
JSMethodName:(NSString *)JSMethodName
|
||||
arity:(NSUInteger)arity
|
||||
blockArgumentIndexes:(NSIndexSet *)blockArgumentIndexes
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_selector = selector;
|
||||
_JSMethodName = [JSMethodName copy];
|
||||
_arity = arity;
|
||||
_blockArgumentIndexes = [blockArgumentIndexes copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
NSString *blocks = @"no block args";
|
||||
if (self.blockArgumentIndexes.count > 0) {
|
||||
NSMutableString *indexString = [NSMutableString string];
|
||||
[self.blockArgumentIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
||||
[indexString appendFormat:@", %tu", idx];
|
||||
}];
|
||||
blocks = [NSString stringWithFormat:@"block args at %@", [indexString substringFromIndex:2]];
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat:@"<%@: %p; exports -%@ as %@; %@>", NSStringFromClass(self.class), self, NSStringFromSelector(self.selector), self.JSMethodName, blocks];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTPointerEvents) {
|
||||
RCTPointerEventsUnspecified = 0, // Default
|
||||
RCTPointerEventsNone,
|
||||
RCTPointerEventsBoxNone,
|
||||
RCTPointerEventsBoxOnly,
|
||||
};
|
|
@ -2,14 +2,35 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol RCTJavaScriptExecutor;
|
||||
|
||||
@interface RCTRootView : UIView
|
||||
|
||||
/**
|
||||
* The URL of the bundled application script (required).
|
||||
* Setting this will clear the view contents, and trigger
|
||||
* an asynchronous load/download and execution of the script.
|
||||
*/
|
||||
@property (nonatomic, strong) NSURL *scriptURL;
|
||||
|
||||
/**
|
||||
* The name of the JavaScript module to execute within the
|
||||
* specified scriptURL (required). Setting this will not have
|
||||
* any immediate effect, but it must be done prior to loading
|
||||
* the script.
|
||||
*/
|
||||
@property (nonatomic, copy) NSString *moduleName;
|
||||
|
||||
/**
|
||||
* The default properties to apply to the view when the script bundle
|
||||
* is first loaded. Defaults to nil/empty.
|
||||
*/
|
||||
@property (nonatomic, copy) NSDictionary *initialProperties;
|
||||
@property (nonatomic, strong) id<RCTJavaScriptExecutor> executor;
|
||||
|
||||
/**
|
||||
* The class of the RCTJavaScriptExecutor to use with this view.
|
||||
* If not specified, it will default to using RCTContextExecutor.
|
||||
* Changes will take effect next time the bundle is reloaded.
|
||||
*/
|
||||
@property (nonatomic, strong) Class executorClass;
|
||||
|
||||
/**
|
||||
* Reload this root view, or all root views, respectively.
|
||||
|
|
|
@ -5,25 +5,25 @@
|
|||
#import "RCTBridge.h"
|
||||
#import "RCTContextExecutor.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTJavaScriptAppEngine.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTRedBox.h"
|
||||
#import "RCTTouchHandler.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTViewManager.h"
|
||||
#import "RCTWebViewExecutor.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
|
||||
NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification";
|
||||
|
||||
@implementation RCTRootView
|
||||
{
|
||||
RCTBridge *_bridge;
|
||||
RCTJavaScriptAppEngine *_appEngine;
|
||||
RCTTouchHandler *_touchHandler;
|
||||
id <RCTJavaScriptExecutor> _executor;
|
||||
}
|
||||
|
||||
static BOOL _useWebExec;
|
||||
static Class _globalExecutorClass;
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
|
@ -36,11 +36,12 @@ static BOOL _useWebExec;
|
|||
action:^(UIKeyCommand *command) {
|
||||
[self reloadAll];
|
||||
}];
|
||||
|
||||
// Cmd-D reloads using the web view executor, allows attaching from Safari dev tools.
|
||||
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"d"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:^(UIKeyCommand *command) {
|
||||
_useWebExec = YES;
|
||||
_globalExecutorClass = [RCTWebViewExecutor class];
|
||||
[self reloadAll];
|
||||
}];
|
||||
|
||||
|
@ -50,21 +51,18 @@ static BOOL _useWebExec;
|
|||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (!self) return nil;
|
||||
|
||||
[self setUp];
|
||||
|
||||
if ((self = [super initWithCoder:aDecoder])) {
|
||||
[self setUp];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (!self) return nil;
|
||||
|
||||
[self setUp];
|
||||
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
[self setUp];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -81,7 +79,6 @@ static BOOL _useWebExec;
|
|||
selector:@selector(reload)
|
||||
name:RCTRootViewReloadNotification
|
||||
object:nil];
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
|
@ -104,10 +101,9 @@ static BOOL _useWebExec;
|
|||
|
||||
NSString *moduleName = _moduleName ?: @"";
|
||||
NSDictionary *appParameters = @{
|
||||
@"rootTag": self.reactTag ?: @0,
|
||||
@"rootTag": self.reactTag,
|
||||
@"initialProps": self.initialProperties ?: @{},
|
||||
};
|
||||
|
||||
[_bridge enqueueJSCall:@"Bundler.runApplication"
|
||||
args:@[moduleName, appParameters]];
|
||||
}
|
||||
|
@ -122,28 +118,81 @@ static BOOL _useWebExec;
|
|||
return;
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
RCTJavaScriptCompleteBlock callback = ^(NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf bundleFinishedLoading:error];
|
||||
});
|
||||
};
|
||||
|
||||
// Clean up
|
||||
[self removeGestureRecognizer:_touchHandler];
|
||||
[_executor invalidate];
|
||||
[_bridge invalidate];
|
||||
|
||||
if (!_useWebExec) {
|
||||
_executor = [[RCTContextExecutor alloc] init];
|
||||
} else {
|
||||
_executor = [[RCTWebViewExecutor alloc] init];
|
||||
}
|
||||
|
||||
// Choose local executor if specified, followed by global, followed by default
|
||||
_executor = [[_executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class] alloc] init];
|
||||
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor];
|
||||
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
|
||||
[self addGestureRecognizer:_touchHandler];
|
||||
|
||||
_appEngine = [[RCTJavaScriptAppEngine alloc] initWithBridge:_bridge];
|
||||
_touchHandler = [[RCTTouchHandler alloc] initWithEventDispatcher:_bridge.eventDispatcher rootView:self];
|
||||
// Load the bundle
|
||||
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:_scriptURL completionHandler:
|
||||
^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
|
||||
// Handle general request errors
|
||||
if (error) {
|
||||
if ([[error domain] isEqualToString:NSURLErrorDomain]) {
|
||||
NSDictionary *userInfo = @{
|
||||
NSLocalizedDescriptionKey: @"Could not connect to development server. Ensure node server is running - run 'npm start' from ReactKit root",
|
||||
NSLocalizedFailureReasonErrorKey: [error localizedDescription],
|
||||
NSUnderlyingErrorKey: error,
|
||||
};
|
||||
error = [NSError errorWithDomain:@"JSServer"
|
||||
code:error.code
|
||||
userInfo:userInfo];
|
||||
}
|
||||
[self bundleFinishedLoading:error];
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse response as text
|
||||
NSStringEncoding encoding = NSUTF8StringEncoding;
|
||||
if (response.textEncodingName != nil) {
|
||||
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
|
||||
if (cfEncoding != kCFStringEncodingInvalidId) {
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
|
||||
}
|
||||
}
|
||||
NSString *rawText = [[NSString alloc] initWithData:data encoding:encoding];
|
||||
|
||||
// Handle HTTP errors
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)response statusCode] != 200) {
|
||||
NSDictionary *userInfo;
|
||||
NSDictionary *errorDetails = RCTJSONParse(rawText, nil);
|
||||
if ([errorDetails isKindOfClass:[NSDictionary class]]) {
|
||||
userInfo = @{
|
||||
NSLocalizedDescriptionKey: errorDetails[@"message"] ?: @"No message provided",
|
||||
@"stack": @[@{
|
||||
@"methodName": errorDetails[@"description"] ?: @"",
|
||||
@"file": errorDetails[@"filename"] ?: @"",
|
||||
@"lineNumber": errorDetails[@"lineNumber"] ?: @0
|
||||
}]
|
||||
};
|
||||
} else {
|
||||
userInfo = @{NSLocalizedDescriptionKey: rawText};
|
||||
}
|
||||
error = [NSError errorWithDomain:@"JSServer"
|
||||
code:[(NSHTTPURLResponse *)response statusCode]
|
||||
userInfo:userInfo];
|
||||
|
||||
[self bundleFinishedLoading:error];
|
||||
return;
|
||||
}
|
||||
|
||||
[_appEngine loadBundleAtURL:_scriptURL useCache:NO onComplete:callback];
|
||||
// Success!
|
||||
[_bridge enqueueApplicationScript:rawText url:_scriptURL onComplete:^(NSError *error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self bundleFinishedLoading:error];
|
||||
});
|
||||
}];
|
||||
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
}
|
||||
|
||||
- (void)setScriptURL:(NSURL *)scriptURL
|
||||
|
@ -156,12 +205,6 @@ static BOOL _useWebExec;
|
|||
[self loadBundle];
|
||||
}
|
||||
|
||||
- (void)setExecutor:(id<RCTJavaScriptExecutor>)executor
|
||||
{
|
||||
RCTAssert(!_bridge, @"You may only change the Javascript Executor prior to loading a script bundle.");
|
||||
_executor = executor;
|
||||
}
|
||||
|
||||
- (BOOL)isReactRootView
|
||||
{
|
||||
return YES;
|
||||
|
@ -169,7 +212,6 @@ static BOOL _useWebExec;
|
|||
|
||||
- (void)reload
|
||||
{
|
||||
[RCTJavaScriptAppEngine resetCacheForBundleAtURL:_scriptURL];
|
||||
[self loadBundle];
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
@class RCTBridge;
|
||||
|
||||
@interface RCTTouchHandler : UIGestureRecognizer
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
rootView:(UIView *)rootView;
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
@end
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#import <UIKit/UIGestureRecognizerSubclass.h>
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
|
@ -16,8 +16,7 @@
|
|||
|
||||
@implementation RCTTouchHandler
|
||||
{
|
||||
__weak UIView *_rootView;
|
||||
RCTEventDispatcher *_eventDispatcher;
|
||||
__weak RCTBridge *_bridge;
|
||||
|
||||
/**
|
||||
* Arrays managed in parallel tracking native touch object along with the
|
||||
|
@ -35,16 +34,13 @@
|
|||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
}
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
rootView:(UIView *)rootView
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if ((self = [super initWithTarget:nil action:NULL])) {
|
||||
|
||||
RCTAssert(eventDispatcher != nil, @"Expect an event dispatcher");
|
||||
RCTAssert(rootView != nil, @"Expect a root view");
|
||||
RCTAssert(bridge != nil, @"Expect an event dispatcher");
|
||||
|
||||
_eventDispatcher = eventDispatcher;
|
||||
_rootView = rootView;
|
||||
_bridge = bridge;
|
||||
|
||||
_nativeTouches = [[NSMutableOrderedSet alloc] init];
|
||||
_reactTouches = [[NSMutableArray alloc] init];
|
||||
|
@ -53,11 +49,17 @@
|
|||
// `cancelsTouchesInView` is needed in order to be used as a top level event delegated recognizer. Otherwise, lower
|
||||
// level components not build using RCT, will fail to recognize gestures.
|
||||
self.cancelsTouchesInView = NO;
|
||||
[_rootView addGestureRecognizer:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTTouchEventType) {
|
||||
RCTTouchEventTypeStart,
|
||||
RCTTouchEventTypeMove,
|
||||
RCTTouchEventTypeEnd,
|
||||
RCTTouchEventTypeCancel
|
||||
};
|
||||
|
||||
#pragma mark - Bookkeeping for touch indices
|
||||
|
||||
- (void)_recordNewTouches:(NSSet *)touches
|
||||
|
@ -122,7 +124,7 @@
|
|||
{
|
||||
UITouch *nativeTouch = _nativeTouches[touchIndex];
|
||||
CGPoint windowLocation = [nativeTouch locationInView:nativeTouch.window];
|
||||
CGPoint rootViewLocation = [nativeTouch.window convertPoint:windowLocation toView:_rootView];
|
||||
CGPoint rootViewLocation = [nativeTouch.window convertPoint:windowLocation toView:self.view];
|
||||
|
||||
UIView *touchView = _touchViews[touchIndex];
|
||||
CGPoint touchViewLocation = [nativeTouch.window convertPoint:windowLocation toView:touchView];
|
||||
|
@ -135,15 +137,26 @@
|
|||
reactTouch[@"timestamp"] = @(nativeTouch.timestamp * 1000); // in ms, for JS
|
||||
}
|
||||
|
||||
- (void)_updateAndDispatchTouches:(NSSet *)touches eventType:(RCTTouchEventType)eventType
|
||||
/**
|
||||
* Constructs information about touch events to send across the serialized
|
||||
* boundary. This data should be compliant with W3C `Touch` objects. This data
|
||||
* alone isn't sufficient to construct W3C `Event` objects. To construct that,
|
||||
* there must be a simple receiver on the other side of the bridge that
|
||||
* organizes the touch objects into `Event`s.
|
||||
*
|
||||
* We send the data as an array of `Touch`es, the type of action
|
||||
* (start/end/move/cancel) and the indices that represent "changed" `Touch`es
|
||||
* from that array.
|
||||
*/
|
||||
- (void)_updateAndDispatchTouches:(NSSet *)touches eventName:(NSString *)eventName
|
||||
{
|
||||
// Update touches
|
||||
NSMutableArray *changedIndices = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *changedIndexes = [[NSMutableArray alloc] init];
|
||||
for (UITouch *touch in touches) {
|
||||
NSInteger index = [_nativeTouches indexOfObject:touch];
|
||||
RCTAssert(index != NSNotFound, @"Touch not found. This is a critical bug.");
|
||||
[self _updateReactTouchAtIndex:index];
|
||||
[changedIndices addObject:@(index)];
|
||||
[changedIndexes addObject:@(index)];
|
||||
}
|
||||
|
||||
// Deep copy the touches because they will be accessed from another thread
|
||||
|
@ -154,9 +167,8 @@
|
|||
}
|
||||
|
||||
// Dispatch touch event
|
||||
[_eventDispatcher sendTouchEventWithType:eventType
|
||||
touches:reactTouches
|
||||
changedIndexes:changedIndices];
|
||||
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveTouches"
|
||||
args:@[eventName, reactTouches, changedIndexes]];
|
||||
}
|
||||
|
||||
#pragma mark - Gesture Recognizer Delegate Callbacks
|
||||
|
@ -169,7 +181,7 @@
|
|||
// "start" has to record new touches before extracting the event.
|
||||
// "end"/"cancel" needs to remove the touch *after* extracting the event.
|
||||
[self _recordNewTouches:touches];
|
||||
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeStart];
|
||||
[self _updateAndDispatchTouches:touches eventName:@"topTouchStart"];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
|
@ -178,20 +190,20 @@
|
|||
if (self.state == UIGestureRecognizerStateFailed) {
|
||||
return;
|
||||
}
|
||||
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeMove];
|
||||
[self _updateAndDispatchTouches:touches eventName:@"topTouchMove"];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeEnd];
|
||||
[self _updateAndDispatchTouches:touches eventName:@"topTouchEnd"];
|
||||
[self _recordRemovedTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesCancelled:touches withEvent:event];
|
||||
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeCancel];
|
||||
[self _updateAndDispatchTouches:touches eventName:@"topTouchCancel"];
|
||||
[self _recordRemovedTouches:touches];
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ do { \
|
|||
NSString *RCTJSONStringify(id jsonObject, NSError **error);
|
||||
id RCTJSONParse(NSString *jsonString, NSError **error);
|
||||
|
||||
// Get MD5 hash of a string
|
||||
// Get MD5 hash of a string (TODO: currently unused. Remove?)
|
||||
NSString *RCTMD5Hash(NSString *string);
|
||||
|
||||
// Get screen metrics in a thread-safe way
|
||||
|
|
|
@ -22,8 +22,4 @@
|
|||
// TODO: Deprecate this
|
||||
- (void)reactBridgeDidFinishTransaction;
|
||||
|
||||
// Invoked when react determines that the view will be removed from the view
|
||||
// hierarchy and never replaced.
|
||||
- (void)reactWillDestroy;
|
||||
|
||||
@end
|
||||
|
|
|
@ -13,15 +13,16 @@
|
|||
* environment. And ensure main thread operations are actually added to a queue
|
||||
* instead of being executed immediately if already on the main thread.
|
||||
*/
|
||||
@interface RCTWebViewExecutor : NSObject<RCTJavaScriptExecutor, UIWebViewDelegate>
|
||||
|
||||
@property (nonatomic, readwrite, strong) UIWebView *webView;
|
||||
@interface RCTWebViewExecutor : NSObject<RCTJavaScriptExecutor>
|
||||
|
||||
// Only one callback stored - will only be invoked for the latest issued
|
||||
// application script request.
|
||||
@property (nonatomic, readwrite, copy) RCTJavaScriptCompleteBlock onApplicationScriptLoaded;
|
||||
@property (nonatomic, copy) RCTJavaScriptCompleteBlock onApplicationScriptLoaded;
|
||||
|
||||
- (instancetype)initWithWebView:(UIWebView *)webView;
|
||||
/**
|
||||
* Instantiate with a specific webview instance
|
||||
*/
|
||||
- (instancetype)initWithWebView:(UIWebView *)webView NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Invoke this to reclaim the web view for reuse. This is necessary in order to
|
||||
|
|
|
@ -23,8 +23,13 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
@interface RCTWebViewExecutor () <UIWebViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTWebViewExecutor
|
||||
{
|
||||
UIWebView *_webView;
|
||||
NSMutableDictionary *_objectsToInject;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
|
||||
@interface RCTAlertManager : NSObject <RCTNativeModule>
|
||||
@interface RCTAlertManager : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
|
||||
@interface RCTDataManager : NSObject <RCTNativeModule>
|
||||
@interface RCTDataManager : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
|
||||
@interface RCTExceptionsManager : NSObject <RCTNativeModule>
|
||||
@interface RCTExceptionsManager : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
|
|
|
@ -2,13 +2,9 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
@class RCTBridge;
|
||||
|
||||
@interface RCTTiming : NSObject <RCTNativeModule, RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
@interface RCTTiming : NSObject <RCTBridgeModule, RCTInvalidating>
|
||||
|
||||
@end
|
||||
|
|
|
@ -2,19 +2,16 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@class RCTRootView;
|
||||
@class RCTShadowView;
|
||||
@class RCTSparseArray;
|
||||
|
||||
@protocol RCTScrollableProtocol;
|
||||
@protocol RCTViewNodeProtocol;
|
||||
|
||||
@interface RCTUIManager : NSObject <RCTNativeModule, RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
@interface RCTUIManager : NSObject <RCTBridgeModule, RCTInvalidating>
|
||||
|
||||
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry;
|
||||
@property (nonatomic, strong) RCTSparseArray *viewRegistry;
|
||||
|
@ -26,9 +23,6 @@
|
|||
*/
|
||||
@property (nonatomic, readwrite, weak) id<UIScrollViewDelegate> nativeMainScrollDelegate;
|
||||
|
||||
+ (UIView <RCTViewNodeProtocol> *)closestReactAncestor:(UIView *)view;
|
||||
+ (UIView <RCTViewNodeProtocol> *)closestReactAncestorThatRespondsToTouch:(UITouch *)touch;
|
||||
|
||||
- (void)registerRootView:(RCTRootView *)rootView;
|
||||
|
||||
+ (UIView *)JSResponder;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#import "RCTUtils.h"
|
||||
#import "RCTView.h"
|
||||
#import "RCTViewNodeProtocol.h"
|
||||
#import "RCTUIViewManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
|
||||
@class RCTAnimationConfig;
|
||||
|
@ -52,17 +52,20 @@ static NSDictionary *RCTViewModuleClasses(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (![cls conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
||||
// Not an RCTNativeModule
|
||||
if (![cls isSubclassOfClass:[RCTViewManager class]]) {
|
||||
// Not a view module
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get module name
|
||||
NSString *moduleName = [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
|
||||
NSString *moduleName = [cls moduleName];
|
||||
|
||||
// Check module name is unique
|
||||
id existingClass = modules[moduleName];
|
||||
RCTCAssert(existingClass == Nil, @"Attempted to register RCTNativeViewModule class %@ for the name '%@', but name was already registered by class %@", cls, moduleName, existingClass);
|
||||
RCTCAssert(existingClass == Nil, @"Attempted to register view module class %@ "
|
||||
"for the name '%@', but name was already registered by class %@", cls, moduleName, existingClass);
|
||||
|
||||
// Add to module list
|
||||
modules[moduleName] = cls;
|
||||
}
|
||||
|
||||
|
@ -90,14 +93,13 @@ static NSDictionary *RCTViewModuleClasses(void)
|
|||
__weak RCTBridge *_bridge;
|
||||
}
|
||||
|
||||
- (id <RCTNativeViewModule>)_managerInstanceForViewWithModuleName:(NSString *)moduleName
|
||||
- (RCTViewManager *)_managerInstanceForViewWithModuleName:(NSString *)moduleName
|
||||
{
|
||||
id <RCTNativeViewModule> managerInstance = _viewManagers[moduleName];
|
||||
RCTViewManager *managerInstance = _viewManagers[moduleName];
|
||||
if (managerInstance == nil) {
|
||||
RCTLogWarn(@"No manager class found for view with module name \"%@\"", moduleName);
|
||||
managerInstance = [[RCTUIViewManager alloc] init];
|
||||
managerInstance = [[RCTViewManager alloc] init];
|
||||
}
|
||||
|
||||
return managerInstance;
|
||||
}
|
||||
|
||||
|
@ -183,27 +185,6 @@ static NSDictionary *RCTViewModuleClasses(void)
|
|||
});
|
||||
}
|
||||
|
||||
+ (UIView *)closestReactAncestor:(UIView *)view
|
||||
{
|
||||
UIView *currentUIView = view;
|
||||
while (currentUIView && !currentUIView.reactTag) {
|
||||
currentUIView = currentUIView.superview;
|
||||
}
|
||||
return (UIView *)currentUIView;
|
||||
}
|
||||
|
||||
+ (UIView *)closestReactAncestorThatRespondsToTouch:(UITouch *)touch
|
||||
{
|
||||
UIView *currentUIView = [RCTUIManager closestReactAncestor:touch.view];
|
||||
while (currentUIView != nil) {
|
||||
if ([currentUIView isUserInteractionEnabled]) { // TODO: implement respondsToTouch:touch mechanism
|
||||
return currentUIView;
|
||||
}
|
||||
currentUIView = [RCTUIManager closestReactAncestor:currentUIView.superview];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters views from registries
|
||||
*/
|
||||
|
@ -212,8 +193,8 @@ static NSDictionary *RCTViewModuleClasses(void)
|
|||
for (id<RCTViewNodeProtocol> child in children) {
|
||||
RCTTraverseViewNodes(registry[child.reactTag], ^(id<RCTViewNodeProtocol> subview) {
|
||||
RCTAssert(![subview isReactRootView], @"Host views should not be unregistered");
|
||||
if ([subview respondsToSelector:@selector(reactWillDestroy)]) {
|
||||
[subview reactWillDestroy];
|
||||
if ([subview conformsToProtocol:@protocol(RCTInvalidating)]) {
|
||||
[(id<RCTInvalidating>)subview invalidate];
|
||||
}
|
||||
registry[subview.reactTag] = nil;
|
||||
});
|
||||
|
@ -332,17 +313,10 @@ static NSDictionary *RCTViewModuleClasses(void)
|
|||
{
|
||||
NSMutableSet *applierBlocks = [NSMutableSet setWithCapacity:1];
|
||||
[topView collectUpdatedProperties:applierBlocks parentProperties:@{}];
|
||||
NSMutableArray *propsAppliers = [NSMutableArray arrayWithCapacity:applierBlocks.count];
|
||||
|
||||
for (RCTApplierBlock propsApplier in applierBlocks) {
|
||||
[propsAppliers addObject:propsApplier];
|
||||
}
|
||||
|
||||
NSArray *immutablePropsAppliers = propsAppliers;
|
||||
|
||||
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
||||
for (NSUInteger f = 0; f < immutablePropsAppliers.count; f++) {
|
||||
RCTApplierBlock applier = [immutablePropsAppliers objectAtIndex: f];
|
||||
applier(viewRegistry);
|
||||
for (RCTApplierBlock block in applierBlocks) {
|
||||
block(viewRegistry);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
@ -515,7 +489,7 @@ static NSDictionary *RCTViewModuleClasses(void)
|
|||
}
|
||||
}
|
||||
|
||||
static BOOL RCTCallPropertySetter(SEL setter, id value, id view, id defaultView, id <RCTNativeViewModule>manager)
|
||||
static BOOL RCTCallPropertySetter(SEL setter, id value, id view, id defaultView, RCTViewManager *manager)
|
||||
{
|
||||
// TODO: cache respondsToSelector tests
|
||||
if ([manager respondsToSelector:setter]) {
|
||||
|
@ -531,7 +505,7 @@ static BOOL RCTCallPropertySetter(SEL setter, id value, id view, id defaultView,
|
|||
}
|
||||
|
||||
static void RCTSetViewProps(NSDictionary *props, UIView *view,
|
||||
UIView *defaultView, id <RCTNativeViewModule>manager)
|
||||
UIView *defaultView, RCTViewManager *manager)
|
||||
{
|
||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||
|
||||
|
@ -544,7 +518,7 @@ static void RCTSetViewProps(NSDictionary *props, UIView *view,
|
|||
}
|
||||
|
||||
static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView,
|
||||
RCTShadowView *defaultView, id <RCTNativeViewModule>manager)
|
||||
RCTShadowView *defaultView, RCTViewManager *manager)
|
||||
{
|
||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||
|
||||
|
@ -575,14 +549,14 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
|||
{
|
||||
RCT_EXPORT(createView);
|
||||
|
||||
id <RCTNativeViewModule>manager = [self _managerInstanceForViewWithModuleName:moduleName];
|
||||
RCTViewManager *manager = [self _managerInstanceForViewWithModuleName:moduleName];
|
||||
|
||||
// Generate default view, used for resetting default props
|
||||
if (!_defaultShadowViews[moduleName]) {
|
||||
_defaultShadowViews[moduleName] = ([manager respondsToSelector:@selector(shadowView)] ? [manager shadowView] : nil) ?: [[RCTShadowView alloc] init];
|
||||
_defaultShadowViews[moduleName] = [manager shadowView];
|
||||
}
|
||||
|
||||
RCTShadowView *shadowView = ([manager respondsToSelector:@selector(shadowView)] ? [manager shadowView] : nil) ?: [[RCTShadowView alloc] init];
|
||||
RCTShadowView *shadowView = [manager shadowView];
|
||||
shadowView.moduleName = moduleName;
|
||||
shadowView.reactTag = reactTag;
|
||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[moduleName], manager);
|
||||
|
@ -619,7 +593,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
|||
RCT_EXPORT();
|
||||
|
||||
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
||||
id <RCTNativeViewModule>manager = [self _managerInstanceForViewWithModuleName:moduleName];
|
||||
RCTViewManager *manager = [self _managerInstanceForViewWithModuleName:moduleName];
|
||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[moduleName], manager);
|
||||
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
|
@ -657,8 +631,8 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
|||
// pending blocks to a new array. This guards against mutation while
|
||||
// processing the pending blocks in another thread.
|
||||
|
||||
for (id <RCTNativeViewModule>viewManager in _viewManagers.allValues) {
|
||||
RCTViewManagerUIBlock uiBlock = [viewManager respondsToSelector:@selector(uiBlockToAmendWithShadowViewRegistry:)] ? [viewManager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry] : nil;
|
||||
for (RCTViewManager *manager in _viewManagers.allValues) {
|
||||
RCTViewManagerUIBlock uiBlock = [manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
|
||||
if (uiBlock != nil) {
|
||||
[self addUIBlock:uiBlock];
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
134FCB371A6D4ED700051CC8 /* RCTRawTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137029581A6C197000575408 /* RCTRawTextManager.m */; };
|
||||
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; };
|
||||
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */; };
|
||||
137029331A69659C00575408 /* RCTExport.m in Sources */ = {isa = PBXBuildFile; fileRef = 830213F41A65574D00B993E6 /* RCTExport.m */; };
|
||||
137029491A698FF000575408 /* RCTNetworkImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137029401A698FF000575408 /* RCTNetworkImageViewManager.m */; };
|
||||
137029501A6990A100575408 /* RCTNetworkImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1370294F1A6990A100575408 /* RCTNetworkImageView.m */; };
|
||||
137029531A69923600575408 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 137029521A69923600575408 /* RCTImageDownloader.m */; };
|
||||
|
@ -37,9 +36,8 @@
|
|||
13B080291A694C4900A75B9A /* RCTDataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080281A694C4900A75B9A /* RCTDataManager.m */; };
|
||||
13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067491A70F434002CDEE1 /* RCTUIManager.m */; };
|
||||
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */; };
|
||||
13E067561A70F44B002CDEE1 /* RCTUIViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674E1A70F44B002CDEE1 /* RCTUIViewManager.m */; };
|
||||
13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */; };
|
||||
13E067571A70F44B002CDEE1 /* RCTView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067501A70F44B002CDEE1 /* RCTView.m */; };
|
||||
13E067581A70F44B002CDEE1 /* RCTViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067521A70F44B002CDEE1 /* RCTViewManager.m */; };
|
||||
13E067591A70F44B002CDEE1 /* UIView+ReactKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */; };
|
||||
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 830A229D1A66C68A008503DA /* RCTRootView.m */; };
|
||||
832348161A77A5AA00B55238 /* Layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FC71A68125100A75B9A /* Layout.c */; };
|
||||
|
@ -49,10 +47,8 @@
|
|||
83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA591A601E9000E9B192 /* RCTRedBox.m */; };
|
||||
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */; };
|
||||
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */; };
|
||||
83CBBA871A60202500E9B192 /* RCTJavaScriptAppEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA861A60202500E9B192 /* RCTJavaScriptAppEngine.m */; };
|
||||
83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */; };
|
||||
83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBACB1A6023D300E9B192 /* RCTConvert.m */; };
|
||||
83EEC2EE1A604AB200C39218 /* RCTModuleMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EEC2ED1A604AB200C39218 /* RCTModuleMethod.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -128,16 +124,14 @@
|
|||
13E067491A70F434002CDEE1 /* RCTUIManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManager.m; sourceTree = "<group>"; };
|
||||
13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowView.h; sourceTree = "<group>"; };
|
||||
13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowView.m; sourceTree = "<group>"; };
|
||||
13E0674D1A70F44B002CDEE1 /* RCTUIViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUIViewManager.h; sourceTree = "<group>"; };
|
||||
13E0674E1A70F44B002CDEE1 /* RCTUIViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIViewManager.m; sourceTree = "<group>"; };
|
||||
13E0674D1A70F44B002CDEE1 /* RCTViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewManager.h; sourceTree = "<group>"; };
|
||||
13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTViewManager.m; sourceTree = "<group>"; };
|
||||
13E0674F1A70F44B002CDEE1 /* RCTView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTView.h; sourceTree = "<group>"; };
|
||||
13E067501A70F44B002CDEE1 /* RCTView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTView.m; sourceTree = "<group>"; };
|
||||
13E067511A70F44B002CDEE1 /* RCTViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewManager.h; sourceTree = "<group>"; };
|
||||
13E067521A70F44B002CDEE1 /* RCTViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTViewManager.m; sourceTree = "<group>"; };
|
||||
13E067531A70F44B002CDEE1 /* UIView+ReactKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+ReactKit.h"; sourceTree = "<group>"; };
|
||||
13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+ReactKit.m"; sourceTree = "<group>"; };
|
||||
830213F31A654E0800B993E6 /* RCTExport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTExport.h; sourceTree = "<group>"; };
|
||||
830213F41A65574D00B993E6 /* RCTExport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTExport.m; sourceTree = "<group>"; };
|
||||
13ED13891A80C9D40050A8F9 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = "<group>"; };
|
||||
830213F31A654E0800B993E6 /* RCTBridgeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBridgeModule.h; sourceTree = "<group>"; };
|
||||
830A229C1A66C68A008503DA /* RCTRootView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootView.h; sourceTree = "<group>"; };
|
||||
830A229D1A66C68A008503DA /* RCTRootView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootView.m; sourceTree = "<group>"; };
|
||||
83BEE46C1A6D19BC00B5863B /* RCTSparseArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSparseArray.h; sourceTree = "<group>"; };
|
||||
|
@ -159,14 +153,10 @@
|
|||
83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTJavaScriptExecutor.h; sourceTree = "<group>"; };
|
||||
83CBBA651A601EF300E9B192 /* RCTEventDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTEventDispatcher.h; sourceTree = "<group>"; };
|
||||
83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTEventDispatcher.m; sourceTree = "<group>"; };
|
||||
83CBBA851A60202500E9B192 /* RCTJavaScriptAppEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJavaScriptAppEngine.h; sourceTree = "<group>"; };
|
||||
83CBBA861A60202500E9B192 /* RCTJavaScriptAppEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJavaScriptAppEngine.m; sourceTree = "<group>"; };
|
||||
83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTouchHandler.h; sourceTree = "<group>"; };
|
||||
83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTouchHandler.m; sourceTree = "<group>"; };
|
||||
83CBBACA1A6023D300E9B192 /* RCTConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTConvert.h; sourceTree = "<group>"; };
|
||||
83CBBACB1A6023D300E9B192 /* RCTConvert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert.m; sourceTree = "<group>"; };
|
||||
83EEC2EC1A604AB200C39218 /* RCTModuleMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleMethod.h; sourceTree = "<group>"; };
|
||||
83EEC2ED1A604AB200C39218 /* RCTModuleMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethod.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -224,12 +214,10 @@
|
|||
13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */,
|
||||
13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */,
|
||||
13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */,
|
||||
13E0674D1A70F44B002CDEE1 /* RCTUIViewManager.h */,
|
||||
13E0674E1A70F44B002CDEE1 /* RCTUIViewManager.m */,
|
||||
13E0674F1A70F44B002CDEE1 /* RCTView.h */,
|
||||
13E067501A70F44B002CDEE1 /* RCTView.m */,
|
||||
13E067511A70F44B002CDEE1 /* RCTViewManager.h */,
|
||||
13E067521A70F44B002CDEE1 /* RCTViewManager.m */,
|
||||
13E0674D1A70F44B002CDEE1 /* RCTViewManager.h */,
|
||||
13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */,
|
||||
13B07FF61A6947C200A75B9A /* RCTScrollView.h */,
|
||||
13B07FF71A6947C200A75B9A /* RCTScrollView.m */,
|
||||
13B07FF81A6947C200A75B9A /* RCTScrollViewManager.h */,
|
||||
|
@ -308,17 +296,12 @@
|
|||
83CBBA611A601EB200E9B192 /* RCTAutoInsetsProtocol.h */,
|
||||
83CBBA5E1A601EAA00E9B192 /* RCTBridge.h */,
|
||||
83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */,
|
||||
83EEC2EC1A604AB200C39218 /* RCTModuleMethod.h */,
|
||||
83EEC2ED1A604AB200C39218 /* RCTModuleMethod.m */,
|
||||
83CBBACA1A6023D300E9B192 /* RCTConvert.h */,
|
||||
83CBBACB1A6023D300E9B192 /* RCTConvert.m */,
|
||||
830213F31A654E0800B993E6 /* RCTExport.h */,
|
||||
830213F41A65574D00B993E6 /* RCTExport.m */,
|
||||
830213F31A654E0800B993E6 /* RCTBridgeModule.h */,
|
||||
830A229C1A66C68A008503DA /* RCTRootView.h */,
|
||||
830A229D1A66C68A008503DA /* RCTRootView.m */,
|
||||
83CBBA4C1A601E3B00E9B192 /* RCTInvalidating.h */,
|
||||
83CBBA851A60202500E9B192 /* RCTJavaScriptAppEngine.h */,
|
||||
83CBBA861A60202500E9B192 /* RCTJavaScriptAppEngine.m */,
|
||||
83CBBA651A601EF300E9B192 /* RCTEventDispatcher.h */,
|
||||
83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */,
|
||||
83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */,
|
||||
|
@ -336,6 +319,7 @@
|
|||
137029511A69923600575408 /* RCTImageDownloader.h */,
|
||||
137029521A69923600575408 /* RCTImageDownloader.m */,
|
||||
13B07FCD1A683B5F00A75B9A /* RCTScrollableProtocol.h */,
|
||||
13ED13891A80C9D40050A8F9 /* RCTPointerEvents.h */,
|
||||
);
|
||||
path = Base;
|
||||
sourceTree = "<group>";
|
||||
|
@ -424,7 +408,7 @@
|
|||
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,
|
||||
832348161A77A5AA00B55238 /* Layout.c in Sources */,
|
||||
13B080201A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m in Sources */,
|
||||
13E067561A70F44B002CDEE1 /* RCTUIViewManager.m in Sources */,
|
||||
13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */,
|
||||
13B080061A6947C200A75B9A /* RCTScrollViewManager.m in Sources */,
|
||||
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */,
|
||||
13B080051A6947C200A75B9A /* RCTScrollView.m in Sources */,
|
||||
|
@ -442,17 +426,13 @@
|
|||
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */,
|
||||
137029491A698FF000575408 /* RCTNetworkImageViewManager.m in Sources */,
|
||||
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */,
|
||||
13E067581A70F44B002CDEE1 /* RCTViewManager.m in Sources */,
|
||||
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */,
|
||||
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */,
|
||||
13B080081A6947C200A75B9A /* RCTShadowText.m in Sources */,
|
||||
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */,
|
||||
13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */,
|
||||
83CBBA871A60202500E9B192 /* RCTJavaScriptAppEngine.m in Sources */,
|
||||
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */,
|
||||
83EEC2EE1A604AB200C39218 /* RCTModuleMethod.m in Sources */,
|
||||
13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */,
|
||||
137029331A69659C00575408 /* RCTExport.m in Sources */,
|
||||
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */,
|
||||
134FCB371A6D4ED700051CC8 /* RCTRawTextManager.m in Sources */,
|
||||
13B0800B1A6947C200A75B9A /* RCTTextManager.m in Sources */,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTNavItemManager : RCTUIViewManager
|
||||
@interface RCTNavItemManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
|
||||
@interface RCTNavigator : UIView <UINavigationControllerDelegate>
|
||||
@interface RCTNavigator : UIView <RCTInvalidating>
|
||||
|
||||
@property (nonatomic, strong) UIView *reactNavSuperviewLink;
|
||||
@property (nonatomic, assign) NSInteger requestedTopOfStack;
|
||||
|
|
|
@ -200,7 +200,7 @@ NSInteger kNeverProgressed = -10000;
|
|||
@end
|
||||
|
||||
|
||||
@interface RCTNavigator() <RCTWrapperViewControllerNavigationListener>
|
||||
@interface RCTNavigator() <RCTWrapperViewControllerNavigationListener, UINavigationControllerDelegate>
|
||||
{
|
||||
RCTEventDispatcher *_eventDispatcher;
|
||||
NSInteger _numberOfViewControllerMovesToIgnore;
|
||||
|
@ -417,9 +417,14 @@ NSInteger kNeverProgressed = -10000;
|
|||
return _currentViews;
|
||||
}
|
||||
|
||||
- (void)reactWillDestroy
|
||||
- (BOOL)isValid
|
||||
{
|
||||
// Removes run loop's references to `displayLink`.
|
||||
return _displayLink != nil;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
// Prevent displayLink from retaining the navigator indefinitely
|
||||
[_displayLink invalidate];
|
||||
_displayLink = nil;
|
||||
_runTimer = nil;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTNavigatorManager : RCTUIViewManager
|
||||
@interface RCTNavigatorManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTNetworkImageViewManager : RCTUIViewManager
|
||||
@interface RCTNetworkImageViewManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTRawTextManager : RCTUIViewManager
|
||||
@interface RCTRawTextManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
|
|
@ -242,11 +242,6 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
|||
return frame;
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(NSObject<RCTViewNodeProtocol> *)subview atIndex:(NSInteger)atIndex
|
||||
{
|
||||
[super insertReactSubview:subview atIndex:atIndex];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTScrollView
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTScrollViewManager : RCTUIViewManager
|
||||
@interface RCTScrollViewManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTStaticImageManager : RCTUIViewManager
|
||||
@interface RCTStaticImageManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
|
|
@ -14,12 +14,13 @@
|
|||
return [[RCTStaticImage alloc] init];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(capInsets)
|
||||
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode)
|
||||
|
||||
- (void)set_src:(id)json forView:(RCTStaticImage *)view withDefaultView:(RCTStaticImage *)defaultView
|
||||
{
|
||||
if (json) {
|
||||
if ([json isKindOfClass:[NSString class]] && [[json pathExtension] caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
|
||||
if ([[[json description] pathExtension] caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
|
||||
[view.layer addAnimation:[RCTConvert GIF:json] forKey:@"contents"];
|
||||
} else {
|
||||
view.image = [RCTConvert UIImage:json];
|
||||
|
@ -29,11 +30,6 @@ RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode)
|
|||
}
|
||||
}
|
||||
|
||||
- (void)set_capInsets:(id)json forView:(RCTStaticImage *)view withDefaultView:(RCTStaticImage *)defaultView
|
||||
{
|
||||
view.capInsets = json ? [RCTConvert UIEdgeInsets:json] : defaultView.capInsets;
|
||||
}
|
||||
|
||||
- (void)set_tintColor:(id)json forView:(RCTStaticImage *)view withDefaultView:(RCTStaticImage *)defaultView
|
||||
{
|
||||
if (json) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTTextFieldManager : RCTUIViewManager
|
||||
@interface RCTTextFieldManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTTextManager : RCTUIViewManager
|
||||
@interface RCTTextManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -67,6 +67,13 @@ RCT_REMAP_VIEW_PROPERTY(textAlign, textAlignment);
|
|||
shadowView.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
||||
}
|
||||
|
||||
// TODO: the purpose of this block is effectively just to copy properties from the shadow views
|
||||
// to their equivalent UIViews. In this case, the property being copied is the attributed text,
|
||||
// but the same principle could be used to copy any property. The implementation is really ugly tho
|
||||
// because the RCTViewManager doesn't retain a reference to the views that it manages, so it basically
|
||||
// has to search the entire view hierarchy for relevant views. Not awesome. This seems like something
|
||||
// where we could introduce a generic solution - perhaps a method on RCTShadowView that is called after
|
||||
// layout to copy its properties across?
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry
|
||||
{
|
||||
NSMutableArray *shadowBlocks = [NSMutableArray new];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@interface RCTUIActivityIndicatorViewManager : RCTUIViewManager
|
||||
@interface RCTUIActivityIndicatorViewManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
|
||||
@interface RCTUIViewManager : NSObject <RCTNativeViewModule>
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
|
||||
|
||||
@end
|
|
@ -1,143 +0,0 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTShadowView.h"
|
||||
#import "RCTView.h"
|
||||
|
||||
@implementation RCTUIViewManager
|
||||
{
|
||||
__weak RCTEventDispatcher *_eventDispatcher;
|
||||
}
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_eventDispatcher = eventDispatcher;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (NSString *)moduleName
|
||||
{
|
||||
// Default implementation, works in most cases
|
||||
NSString *name = NSStringFromClass(self);
|
||||
if ([name hasPrefix:@"RCTUI"]) {
|
||||
name = [name substringFromIndex:@"RCT".length];
|
||||
}
|
||||
if ([name hasSuffix:@"Manager"]) {
|
||||
name = [name substringToIndex:name.length - @"Manager".length];
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[UIView alloc] init];
|
||||
}
|
||||
|
||||
// View properties
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel)
|
||||
RCT_EXPORT_VIEW_PROPERTY(hidden)
|
||||
RCT_EXPORT_VIEW_PROPERTY(backgroundColor)
|
||||
RCT_REMAP_VIEW_PROPERTY(accessible, isAccessibilityElement)
|
||||
RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier)
|
||||
RCT_REMAP_VIEW_PROPERTY(opacity, alpha)
|
||||
RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor);
|
||||
RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset);
|
||||
RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity)
|
||||
RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius)
|
||||
RCT_REMAP_VIEW_PROPERTY(borderColor, layer.borderColor);
|
||||
RCT_REMAP_VIEW_PROPERTY(borderRadius, layer.cornerRadius)
|
||||
RCT_REMAP_VIEW_PROPERTY(borderWidth, layer.borderWidth)
|
||||
RCT_REMAP_VIEW_PROPERTY(transformMatrix, layer.transform)
|
||||
|
||||
- (void)set_overflow:(id)json
|
||||
forView:(UIView *)view
|
||||
withDefaultView:(UIView *)defaultView
|
||||
{
|
||||
view.clipsToBounds = json ? ![RCTConvert css_overflow:json] : defaultView.clipsToBounds;
|
||||
}
|
||||
|
||||
- (void)set_pointerEvents:(id)json
|
||||
forView:(UIView *)view
|
||||
withDefaultView:(UIView *)defaultView
|
||||
{
|
||||
if (!json) {
|
||||
view.userInteractionEnabled = defaultView.userInteractionEnabled;
|
||||
return;
|
||||
}
|
||||
|
||||
switch ([RCTConvert NSInteger:json]) {
|
||||
case RCTPointerEventsUnspecified:
|
||||
// Pointer events "unspecified" acts as if a stylesheet had not specified,
|
||||
// which is different than "auto" in CSS (which cannot and will not be
|
||||
// supported in `ReactKit`. "auto" may override a parent's "none".
|
||||
// Unspecified values do not.
|
||||
// This wouldn't override a container view's `userInteractionEnabled = NO`
|
||||
view.userInteractionEnabled = YES;
|
||||
case RCTPointerEventsNone:
|
||||
view.userInteractionEnabled = NO;
|
||||
break;
|
||||
default:
|
||||
RCTLogError(@"UIView base class does not support pointerEvent value: %@", json);
|
||||
}
|
||||
}
|
||||
|
||||
// ShadowView properties
|
||||
|
||||
- (void)set_backgroundColor:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor;
|
||||
shadowView.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
||||
}
|
||||
|
||||
- (void)set_flexDirection:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.flexDirection = json? [RCTConvert css_flex_direction_t:json] : defaultView.flexDirection;
|
||||
}
|
||||
|
||||
- (void)set_flexWrap:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.flexWrap = json ? [RCTConvert css_wrap_type_t:json] : defaultView.flexWrap;
|
||||
}
|
||||
|
||||
- (void)set_justifyContent:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.justifyContent = json ? [RCTConvert css_justify_t:json] : defaultView.justifyContent;
|
||||
}
|
||||
|
||||
- (void)set_alignItems:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.alignItems = json ? [RCTConvert css_align_t:json] : defaultView.alignItems;
|
||||
}
|
||||
|
||||
- (void)set_alignSelf:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.alignSelf = json ? [RCTConvert css_align_t:json] : defaultView.alignSelf;
|
||||
}
|
||||
|
||||
- (void)set_position:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.positionType = json ? [RCTConvert css_position_type_t:json] : defaultView.positionType;
|
||||
}
|
||||
|
||||
@end
|
|
@ -4,19 +4,13 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
// TODO: rehome this
|
||||
typedef NS_ENUM(NSInteger, RCTPointerEventsValue) {
|
||||
RCTPointerEventsUnspecified = 0, // Default
|
||||
RCTPointerEventsNone,
|
||||
RCTPointerEventsBoxNone,
|
||||
RCTPointerEventsBoxOnly,
|
||||
};
|
||||
#import "RCTPointerEvents.h"
|
||||
|
||||
@protocol RCTAutoInsetsProtocol;
|
||||
|
||||
@interface RCTView : UIView
|
||||
|
||||
@property (nonatomic, assign) RCTPointerEventsValue pointerEvents;
|
||||
@property (nonatomic, assign) RCTPointerEvents pointerEvents;
|
||||
@property (nonatomic, copy) NSString *overrideAccessibilityLabel;
|
||||
|
||||
+ (void)autoAdjustInsetsForView:(UIView<RCTAutoInsetsProtocol> *)parentView
|
||||
|
|
|
@ -40,7 +40,7 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
return RCTRecursiveAccessibilityLabel(self);
|
||||
}
|
||||
|
||||
- (void)setPointerEvents:(RCTPointerEventsValue)pointerEvents
|
||||
- (void)setPointerEvents:(RCTPointerEvents)pointerEvents
|
||||
{
|
||||
_pointerEvents = pointerEvents;
|
||||
self.userInteractionEnabled = (pointerEvents != RCTPointerEventsNone);
|
||||
|
|
|
@ -1,7 +1,148 @@
|
|||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTUIViewManager.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface RCTViewManager : RCTUIViewManager
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
@class RCTShadowView;
|
||||
@class RCTSparseArray;
|
||||
@class RCTUIManager;
|
||||
|
||||
typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *viewRegistry);
|
||||
|
||||
@interface RCTViewManager : NSObject
|
||||
|
||||
/**
|
||||
* Designated initializer for view modules. Override this when subclassing.
|
||||
*/
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* The event dispatcher is used to send events back to the JavaScript application.
|
||||
* It can either be used directly by the module, or passed on to instantiated
|
||||
* view subclasses so that they can handle their own events.
|
||||
*/
|
||||
@property (nonatomic, readonly, weak) RCTEventDispatcher *eventDispatcher;
|
||||
|
||||
/**
|
||||
* The module name exposed to React JS. If omitted, this will be inferred
|
||||
* automatically by using the view module's class name. It is better to not
|
||||
* override this, and just follow standard naming conventions for your view
|
||||
* module subclasses.
|
||||
*/
|
||||
+ (NSString *)moduleName;
|
||||
|
||||
/**
|
||||
* This method instantiates a native view to be managed by the module. Override
|
||||
* this to return a custom view instance, which may be preconfigured with default
|
||||
* properties, subviews, etc. This method will be called many times, and should
|
||||
* return a fresh instance each time. The view module MUST NOT cache the returned
|
||||
* view and return the same instance for subsequent calls.
|
||||
*/
|
||||
- (UIView *)view;
|
||||
|
||||
/**
|
||||
* This method instantiates a shadow view to be managed by the module. If omitted,
|
||||
* an ordinary RCTShadowView instance will be created, which is typically fine for
|
||||
* most view types. As with the -view method, the -shadowView method should return
|
||||
* a fresh instance each time it is called.
|
||||
*/
|
||||
- (RCTShadowView *)shadowView;
|
||||
|
||||
/**
|
||||
* Returns a dictionary of config data passed to JS that defines eligible events
|
||||
* that can be placed on native views. This should return bubbling
|
||||
* directly-dispatched event types and specify what names should be used to
|
||||
* subscribe to either form (bubbling/capturing).
|
||||
*
|
||||
* Returned dictionary should be of the form: @{
|
||||
* @"onTwirl": {
|
||||
* @"phasedRegistrationNames": @{
|
||||
* @"bubbled": @"onTwirl",
|
||||
* @"captured": @"onTwirlCaptured"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Note that this method is not inherited when you subclass a view module, and
|
||||
* you should not call [super customBubblingEventTypes] when overriding it.
|
||||
*/
|
||||
+ (NSDictionary *)customBubblingEventTypes;
|
||||
|
||||
/**
|
||||
* Returns a dictionary of config data passed to JS that defines eligible events
|
||||
* that can be placed on native views. This should return non-bubbling
|
||||
* directly-dispatched event types.
|
||||
*
|
||||
* Returned dictionary should be of the form: @{
|
||||
* @"onTwirl": {
|
||||
* @"registrationName": @"onTwirl"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Note that this method is not inherited when you subclass a view module, and
|
||||
* you should not call [super customDirectEventTypes] when overriding it.
|
||||
*/
|
||||
+ (NSDictionary *)customDirectEventTypes;
|
||||
|
||||
/**
|
||||
* Injects constants into JS. These constants are made accessible via
|
||||
* NativeModules.moduleName.X. Note that this method is not inherited when you
|
||||
* subclass a view module, and you should not call [super constantsToExport]
|
||||
* when overriding it.
|
||||
*/
|
||||
+ (NSDictionary *)constantsToExport;
|
||||
|
||||
/**
|
||||
* To deprecate, hopefully
|
||||
*/
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry;
|
||||
|
||||
/**
|
||||
* Informal protocol for setting view and shadowView properties.
|
||||
* Implement methods matching these patterns to set any properties that
|
||||
* require special treatment (e.g. where the type or name cannot be inferred).
|
||||
*
|
||||
* - (void)set_<propertyName>:(id)property
|
||||
* forView:(UIView *)view
|
||||
* withDefaultView:(UIView *)defaultView;
|
||||
*
|
||||
* - (void)set_<propertyName>:(id)property
|
||||
* forShadowView:(RCTShadowView *)view
|
||||
* withDefaultView:(RCTShadowView *)defaultView;
|
||||
*
|
||||
* For simple cases, use the macros below:
|
||||
*/
|
||||
|
||||
/**
|
||||
* This handles the simple case, where JS and native property names match
|
||||
* And the type can be automatically inferred.
|
||||
*/
|
||||
#define RCT_EXPORT_VIEW_PROPERTY(name) \
|
||||
RCT_REMAP_VIEW_PROPERTY(name, name)
|
||||
|
||||
/**
|
||||
* This macro maps a named property on the module to an arbitrary key path
|
||||
* within the view.
|
||||
*/
|
||||
#define RCT_REMAP_VIEW_PROPERTY(name, keypath) \
|
||||
- (void)set_##name:(id)json forView:(id)view withDefaultView:(id)defaultView { \
|
||||
if ((json && !RCTSetProperty(view, @#keypath, json)) || \
|
||||
(!json && !RCTCopyProperty(view, defaultView, @#keypath))) { \
|
||||
RCTLogMustFix(@"%@ does not have setter for `%s` property", [view class], #name); \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* These are useful in cases where the module's superclass handles a
|
||||
* property, but you wish to "unhandle" it, so it will be ignored.
|
||||
*/
|
||||
#define RCT_IGNORE_VIEW_PROPERTY(name) \
|
||||
- (void)set_##name:(id)value forView:(id)view withDefaultView:(id)defaultView {}
|
||||
|
||||
#define RCT_IGNORE_SHADOW_PROPERTY(name) \
|
||||
- (void)set_##name:(id)value forShadowView:(id)view withDefaultView:(id)defaultView {}
|
||||
|
||||
@end
|
||||
|
|
|
@ -2,15 +2,169 @@
|
|||
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTShadowView.h"
|
||||
#import "RCTView.h"
|
||||
|
||||
@implementation RCTViewManager
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_eventDispatcher = eventDispatcher;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (NSString *)moduleName
|
||||
{
|
||||
// Default implementation, works in most cases
|
||||
NSString *name = NSStringFromClass(self);
|
||||
if ([name hasPrefix:@"RCTUI"]) {
|
||||
name = [name substringFromIndex:@"RCT".length];
|
||||
}
|
||||
if ([name hasSuffix:@"Manager"]) {
|
||||
name = [name substringToIndex:name.length - @"Manager".length];
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTView alloc] init];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(pointerEvents)
|
||||
- (RCTShadowView *)shadowView
|
||||
{
|
||||
return [[RCTShadowView alloc] init];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)customBubblingEventTypes
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)customDirectEventTypes
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)constantsToExport
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// View properties
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel)
|
||||
RCT_EXPORT_VIEW_PROPERTY(hidden)
|
||||
RCT_EXPORT_VIEW_PROPERTY(backgroundColor)
|
||||
RCT_REMAP_VIEW_PROPERTY(accessible, isAccessibilityElement)
|
||||
RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier)
|
||||
RCT_REMAP_VIEW_PROPERTY(opacity, alpha)
|
||||
RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor);
|
||||
RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset);
|
||||
RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity)
|
||||
RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius)
|
||||
RCT_REMAP_VIEW_PROPERTY(borderColor, layer.borderColor);
|
||||
RCT_REMAP_VIEW_PROPERTY(borderRadius, layer.cornerRadius)
|
||||
RCT_REMAP_VIEW_PROPERTY(borderWidth, layer.borderWidth)
|
||||
RCT_REMAP_VIEW_PROPERTY(transformMatrix, view.layer.transform)
|
||||
|
||||
- (void)set_overflow:(id)json
|
||||
forView:(UIView *)view
|
||||
withDefaultView:(UIView *)defaultView
|
||||
{
|
||||
view.clipsToBounds = json ? ![RCTConvert css_overflow:json] : defaultView.clipsToBounds;
|
||||
}
|
||||
|
||||
- (void)set_pointerEvents:(id)json
|
||||
forView:(UIView *)view
|
||||
withDefaultView:(UIView *)defaultView
|
||||
{
|
||||
if ([view respondsToSelector:@selector(setPointerEvents:)]) {
|
||||
[(id)view setPointerEvents:json ? [RCTConvert RCTPointerEvents:json] : [(id)defaultView pointerEvents]];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json) {
|
||||
view.userInteractionEnabled = defaultView.userInteractionEnabled;
|
||||
return;
|
||||
}
|
||||
|
||||
switch ([RCTConvert NSInteger:json]) {
|
||||
case RCTPointerEventsUnspecified:
|
||||
// Pointer events "unspecified" acts as if a stylesheet had not specified,
|
||||
// which is different than "auto" in CSS (which cannot and will not be
|
||||
// supported in `ReactKit`. "auto" may override a parent's "none".
|
||||
// Unspecified values do not.
|
||||
// This wouldn't override a container view's `userInteractionEnabled = NO`
|
||||
view.userInteractionEnabled = YES;
|
||||
case RCTPointerEventsNone:
|
||||
view.userInteractionEnabled = NO;
|
||||
break;
|
||||
default:
|
||||
RCTLogError(@"UIView base class does not support pointerEvent value: %@", json);
|
||||
}
|
||||
}
|
||||
|
||||
// ShadowView properties
|
||||
|
||||
- (void)set_backgroundColor:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor;
|
||||
shadowView.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
||||
}
|
||||
|
||||
- (void)set_flexDirection:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.flexDirection = json? [RCTConvert css_flex_direction_t:json] : defaultView.flexDirection;
|
||||
}
|
||||
|
||||
- (void)set_flexWrap:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.flexWrap = json ? [RCTConvert css_wrap_type_t:json] : defaultView.flexWrap;
|
||||
}
|
||||
|
||||
- (void)set_justifyContent:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.justifyContent = json ? [RCTConvert css_justify_t:json] : defaultView.justifyContent;
|
||||
}
|
||||
|
||||
- (void)set_alignItems:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.alignItems = json ? [RCTConvert css_align_t:json] : defaultView.alignItems;
|
||||
}
|
||||
|
||||
- (void)set_alignSelf:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.alignSelf = json ? [RCTConvert css_align_t:json] : defaultView.alignSelf;
|
||||
}
|
||||
|
||||
- (void)set_position:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
{
|
||||
shadowView.positionType = json ? [RCTConvert css_position_type_t:json] : defaultView.positionType;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue