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:
Родитель
6153fffb30
Коммит
fd8b7dee77
|
@ -16,7 +16,7 @@
|
||||||
* @providesModule ResponderEventPlugin
|
* @providesModule ResponderEventPlugin
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
var EventConstants = require('EventConstants');
|
var EventConstants = require('EventConstants');
|
||||||
var EventPluginUtils = require('EventPluginUtils');
|
var EventPluginUtils = require('EventPluginUtils');
|
||||||
|
@ -27,6 +27,7 @@ var ResponderSyntheticEvent = require('ResponderSyntheticEvent');
|
||||||
var ResponderTouchHistoryStore = require('ResponderTouchHistoryStore');
|
var ResponderTouchHistoryStore = require('ResponderTouchHistoryStore');
|
||||||
|
|
||||||
var accumulate = require('accumulate');
|
var accumulate = require('accumulate');
|
||||||
|
var invariant = require('invariant');
|
||||||
var keyOf = require('keyOf');
|
var keyOf = require('keyOf');
|
||||||
|
|
||||||
var isStartish = EventPluginUtils.isStartish;
|
var isStartish = EventPluginUtils.isStartish;
|
||||||
|
@ -43,6 +44,12 @@ var executeDispatchesInOrderStopAtTrue =
|
||||||
*/
|
*/
|
||||||
var responderID = null;
|
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.
|
* Last reported number of active touches.
|
||||||
*/
|
*/
|
||||||
|
@ -426,7 +433,8 @@ function setResponderAndExtractTransfer(
|
||||||
function canTriggerTransfer(topLevelType, topLevelTargetID) {
|
function canTriggerTransfer(topLevelType, topLevelTargetID) {
|
||||||
return topLevelTargetID && (
|
return topLevelTargetID && (
|
||||||
topLevelType === EventConstants.topLevelTypes.topScroll ||
|
topLevelType === EventConstants.topLevelTypes.topScroll ||
|
||||||
topLevelType === EventConstants.topLevelTypes.topSelectionChange ||
|
(trackedTouchCount > 0 &&
|
||||||
|
topLevelType === EventConstants.topLevelTypes.topSelectionChange) ||
|
||||||
isStartish(topLevelType) ||
|
isStartish(topLevelType) ||
|
||||||
isMoveish(topLevelType)
|
isMoveish(topLevelType)
|
||||||
);
|
);
|
||||||
|
@ -489,6 +497,15 @@ var ResponderEventPlugin = {
|
||||||
topLevelTargetID,
|
topLevelTargetID,
|
||||||
nativeEvent) {
|
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);
|
ResponderTouchHistoryStore.recordTouchTrack(topLevelType, nativeEvent);
|
||||||
|
|
||||||
|
@ -507,12 +524,10 @@ var ResponderEventPlugin = {
|
||||||
// (`onResponderRelease/onResponderTerminate`).
|
// (`onResponderRelease/onResponderTerminate`).
|
||||||
var isResponderTouchStart = responderID && isStartish(topLevelType);
|
var isResponderTouchStart = responderID && isStartish(topLevelType);
|
||||||
var isResponderTouchMove = responderID && isMoveish(topLevelType);
|
var isResponderTouchMove = responderID && isMoveish(topLevelType);
|
||||||
var isResponderTouchTerminate = responderID && topLevelType === EventConstants.topLevelTypes.topTouchCancel;
|
|
||||||
var isResponderTouchEnd = responderID && isEndish(topLevelType);
|
var isResponderTouchEnd = responderID && isEndish(topLevelType);
|
||||||
var incrementalTouch =
|
var incrementalTouch =
|
||||||
isResponderTouchStart ? eventTypes.responderStart :
|
isResponderTouchStart ? eventTypes.responderStart :
|
||||||
isResponderTouchMove ? eventTypes.responderMove :
|
isResponderTouchMove ? eventTypes.responderMove :
|
||||||
isResponderTouchTerminate ? eventTypes.responderTerminate :
|
|
||||||
isResponderTouchEnd ? eventTypes.responderEnd :
|
isResponderTouchEnd ? eventTypes.responderEnd :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
|
@ -524,17 +539,24 @@ var ResponderEventPlugin = {
|
||||||
extracted = accumulate(extracted, gesture);
|
extracted = accumulate(extracted, gesture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isResponderTerminate =
|
||||||
|
responderID &&
|
||||||
|
topLevelType === EventConstants.topLevelTypes.topTouchCancel;
|
||||||
var isResponderRelease =
|
var isResponderRelease =
|
||||||
responderID && isEndish(topLevelType) && noResponderTouches(nativeEvent);
|
responderID &&
|
||||||
if (isResponderRelease) {
|
!isResponderTerminate &&
|
||||||
var release = ResponderSyntheticEvent.getPooled(
|
isEndish(topLevelType) &&
|
||||||
eventTypes.responderRelease,
|
noResponderTouches(nativeEvent);
|
||||||
responderID,
|
var finalTouch =
|
||||||
nativeEvent
|
isResponderTerminate ? eventTypes.responderTerminate :
|
||||||
);
|
isResponderRelease ? eventTypes.responderRelease :
|
||||||
release.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
null;
|
||||||
EventPropagators.accumulateDirectDispatches(release);
|
if (finalTouch) {
|
||||||
extracted = accumulate(extracted, release);
|
var finalEvent =
|
||||||
|
ResponderSyntheticEvent.getPooled(finalTouch, responderID, nativeEvent);
|
||||||
|
finalEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||||
|
EventPropagators.accumulateDirectDispatches(finalEvent);
|
||||||
|
extracted = accumulate(extracted, finalEvent);
|
||||||
changeResponder(null);
|
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
|
* @providesModule Touchable
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
var BoundingDimensions = require('BoundingDimensions');
|
var BoundingDimensions = require('BoundingDimensions');
|
||||||
var Position = require('Position');
|
var Position = require('Position');
|
||||||
var TouchEventUtils = require('TouchEventUtils');
|
var TouchEventUtils = require('TouchEventUtils');
|
||||||
|
|
||||||
var invariant = require('invariant');
|
|
||||||
var keyMirror = require('keyMirror');
|
var keyMirror = require('keyMirror');
|
||||||
var queryLayoutByID = require('queryLayoutByID');
|
var queryLayoutByID = require('queryLayoutByID');
|
||||||
|
|
||||||
|
@ -277,20 +276,20 @@ var LONG_PRESS_ALLOWED_MOVEMENT = 10;
|
||||||
* +
|
* +
|
||||||
* | RESPONDER_GRANT (HitRect)
|
* | RESPONDER_GRANT (HitRect)
|
||||||
* v
|
* v
|
||||||
* +---------------------------+ DELAY +-------------------------+ T - DELAY +------------------------------+
|
* +---------------------------+ DELAY +-------------------------+ T - DELAY +------------------------------+
|
||||||
* |RESPONDER_INACTIVE_PRESS_IN|+-------->|RESPONDER_ACTIVE_PRESS_IN| +------------> |RESPONDER_ACTIVE_LONG_PRESS_IN|
|
* |RESPONDER_INACTIVE_PRESS_IN|+-------->|RESPONDER_ACTIVE_PRESS_IN| +------------> |RESPONDER_ACTIVE_LONG_PRESS_IN|
|
||||||
* +---------------------------+ +-------------------------+ +------------------------------+
|
* +---------------------------+ +-------------------------+ +------------------------------+
|
||||||
* + ^ + ^ + ^
|
* + ^ + ^ + ^
|
||||||
* |LEAVE_ |ENTER_ |LEAVE_ |ENTER_ |LEAVE_ |ENTER_
|
* |LEAVE_ |ENTER_ |LEAVE_ |ENTER_ |LEAVE_ |ENTER_
|
||||||
* |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT
|
* |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT |PRESS_RECT
|
||||||
* | | | | | |
|
* | | | | | |
|
||||||
* v + v + v +
|
* v + v + v +
|
||||||
* +----------------------------+ DELAY +--------------------------+ +-------------------------------+
|
* +----------------------------+ DELAY +--------------------------+ +-------------------------------+
|
||||||
* |RESPONDER_INACTIVE_PRESS_OUT|+------->|RESPONDER_ACTIVE_PRESS_OUT| |RESPONDER_ACTIVE_LONG_PRESS_OUT|
|
* |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
|
* Not drawn are the side effects of each transition. The most important side
|
||||||
* effect is the `touchableHandlePress` abstract method invocation that occurs
|
* effect is the `touchableHandlePress` abstract method invocation that occurs
|
||||||
* when a responder is released while in either of the "Press" states.
|
* when a responder is released while in either of the "Press" states.
|
||||||
|
@ -344,7 +343,7 @@ var TouchableMixin = {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
touchableHandleResponderGrant: function(e, dispatchID) {
|
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
|
// (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.
|
// event to make sure it doesn't get reused in the event object pool.
|
||||||
e.persist();
|
e.persist();
|
||||||
|
@ -420,9 +419,9 @@ var TouchableMixin = {
|
||||||
var movedDistance = this._getDistanceBetweenPoints(pageX, pageY, this.pressInLocation.pageX, this.pressInLocation.pageY);
|
var movedDistance = this._getDistanceBetweenPoints(pageX, pageY, this.pressInLocation.pageX, this.pressInLocation.pageY);
|
||||||
if (movedDistance > LONG_PRESS_ALLOWED_MOVEMENT) {
|
if (movedDistance > LONG_PRESS_ALLOWED_MOVEMENT) {
|
||||||
this._cancelLongPressDelayTimeout();
|
this._cancelLongPressDelayTimeout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isTouchWithinActive =
|
var isTouchWithinActive =
|
||||||
pageX > positionOnActivate.left - pressExpandLeft &&
|
pageX > positionOnActivate.left - pressExpandLeft &&
|
||||||
pageY > positionOnActivate.top - pressExpandTop &&
|
pageY > positionOnActivate.top - pressExpandTop &&
|
||||||
|
@ -556,18 +555,19 @@ var TouchableMixin = {
|
||||||
*/
|
*/
|
||||||
_receiveSignal: function(signal, e) {
|
_receiveSignal: function(signal, e) {
|
||||||
var curState = this.state.touchable.touchState;
|
var curState = this.state.touchable.touchState;
|
||||||
invariant(
|
if (!(Transitions[curState] && Transitions[curState][signal])) {
|
||||||
Transitions[curState] && Transitions[curState][signal],
|
throw new Error(
|
||||||
'You have supplied either an unrecognized signal or current state %s',
|
'Unrecognized signal `' + signal + '` or state `' + curState +
|
||||||
curState
|
'` for Touchable responder `' + this.state.touchable.responderID + '`'
|
||||||
);
|
);
|
||||||
|
}
|
||||||
var nextState = Transitions[curState][signal];
|
var nextState = Transitions[curState][signal];
|
||||||
invariant(
|
if (nextState === States.ERROR) {
|
||||||
nextState !== States.ERROR,
|
throw new Error(
|
||||||
'Some assumptions about the state machine were violated. This is the ' +
|
'Touchable cannot transition from `' + curState + '` to `' + signal +
|
||||||
'fault of Touchable.js. This case has been modeled and caught as an ' +
|
'` for responder `' + this.state.touchable.responderID + '`'
|
||||||
'error transition.'
|
);
|
||||||
);
|
}
|
||||||
if (curState !== nextState) {
|
if (curState !== nextState) {
|
||||||
this._performSideEffectsForTransition(curState, nextState, signal, e);
|
this._performSideEffectsForTransition(curState, nextState, signal, e);
|
||||||
this.state.touchable.touchState = nextState;
|
this.state.touchable.touchState = nextState;
|
||||||
|
@ -580,7 +580,7 @@ var TouchableMixin = {
|
||||||
},
|
},
|
||||||
|
|
||||||
_isHighlight: function (state) {
|
_isHighlight: function (state) {
|
||||||
return state === States.RESPONDER_ACTIVE_PRESS_IN ||
|
return state === States.RESPONDER_ACTIVE_PRESS_IN ||
|
||||||
state === States.RESPONDER_ACTIVE_LONG_PRESS_IN;
|
state === States.RESPONDER_ACTIVE_LONG_PRESS_IN;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -626,15 +626,15 @@ var TouchableMixin = {
|
||||||
|
|
||||||
if (IsPressingIn[curState] && signal === Signals.LONG_PRESS_DETECTED) {
|
if (IsPressingIn[curState] && signal === Signals.LONG_PRESS_DETECTED) {
|
||||||
this.touchableHandleLongPress && this.touchableHandleLongPress();
|
this.touchableHandleLongPress && this.touchableHandleLongPress();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newIsHighlight && !curIsHighlight) {
|
if (newIsHighlight && !curIsHighlight) {
|
||||||
this._savePressInLocation(e);
|
this._savePressInLocation(e);
|
||||||
this.touchableHandleActivePressIn && this.touchableHandleActivePressIn();
|
this.touchableHandleActivePressIn && this.touchableHandleActivePressIn();
|
||||||
} else if (!newIsHighlight && curIsHighlight) {
|
} else if (!newIsHighlight && curIsHighlight) {
|
||||||
this.touchableHandleActivePressOut && this.touchableHandleActivePressOut();
|
this.touchableHandleActivePressOut && this.touchableHandleActivePressOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPressingIn[curState] && signal === Signals.RESPONDER_RELEASE) {
|
if (IsPressingIn[curState] && signal === Signals.RESPONDER_RELEASE) {
|
||||||
var hasLongPressHandler = !!this.touchableHandleLongPress;
|
var hasLongPressHandler = !!this.touchableHandleLongPress;
|
||||||
var pressIsLongButStillCallOnPress =
|
var pressIsLongButStillCallOnPress =
|
||||||
|
@ -660,4 +660,3 @@ var Touchable = {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = 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
|
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
|
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
|
features. Some things may even be well-documented in JS, but missing from the
|
||||||
|
@ -67,11 +67,14 @@ the same library.**
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
Xcode will break if you have two examples open at the same time.
|
+ Xcode will break if you have two examples open at the same time.
|
||||||
|
+ If `npm start` fails with log spew like:
|
||||||
Jest testing does not yet work on node versions after 0.10.x.
|
```
|
||||||
|
2015-02-02 10:56 node[24294] (FSEvents.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-21)
|
||||||
You can verify the packager is working by loading the [bundle](http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle) in your browser and
|
```
|
||||||
|
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.
|
inspecting the contents.
|
||||||
|
|
||||||
Please report any other issues you encounter so we can fix them ASAP.
|
Please report any other issues you encounter so we can fix them ASAP.
|
||||||
|
@ -92,27 +95,27 @@ the responder system.
|
||||||
|
|
||||||
# FAQ
|
# 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
|
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.
|
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.
|
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
|
We are looking forward to the community helping us target other platforms as
|
||||||
well :)
|
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
|
A. Copy the entire `Examples/TicTacToe` folder, rename stuff in Xcode, and
|
||||||
replace the `TicTacToeApp.js` with your own. Then, in `AppDelegate.m`, update
|
replace the `TicTacToeApp.js` with your own. Then, in `AppDelegate.m`, update
|
||||||
`moduleName` to match your call to
|
`moduleName` to match your call to
|
||||||
`Bundler.registerComponent(<moduleName>, <componentName>)` at the bottom of your
|
`Bundler.registerComponent(<moduleName>, <componentName>)` at the bottom of your
|
||||||
JS file, and update `jsCodeLocation` to match your JS file name and location.
|
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
|
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.
|
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
|
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
|
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
|
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
|
`AppDelegate.m` to point to that file and deploy to your device like you would
|
||||||
any other app.
|
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
|
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
|
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.
|
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
|
A. No, we just use the JavaScriptCore public API that is part of iOS 7 and
|
||||||
later.
|
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
|
A. React Native is designed to be extensible - come talk to us, we would love to
|
||||||
work with you.
|
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
|
A. Yes, React Native is designed to be extensible and allow integration of all
|
||||||
sorts of native components, such as `UINavigationController` (available as
|
sorts of native components, such as `UINavigationController` (available as
|
||||||
`<NavigatorIOS>`), `MKMapView` (not available yet), or your own custom
|
`<NavigatorIOS>`), `MKMapView` (not available yet), or your own custom
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
#import "RCTExport.h"
|
#import "RCTBridgeModule.h"
|
||||||
#import "RCTInvalidating.h"
|
#import "RCTInvalidating.h"
|
||||||
#import "RCTJavaScriptExecutor.h"
|
#import "RCTJavaScriptExecutor.h"
|
||||||
|
|
||||||
@protocol RCTNativeModule;
|
|
||||||
|
|
||||||
@class RCTEventDispatcher;
|
@class RCTEventDispatcher;
|
||||||
@class RCTRootView;
|
@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>
|
@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;
|
- (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;
|
- (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;
|
@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;
|
@property (nonatomic, readonly) dispatch_queue_t shadowQueue;
|
||||||
|
|
||||||
// For use in implementing delegates, which may need to queue responses.
|
// For use in implementing delegates, which may need to queue responses.
|
||||||
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)callbackID;
|
- (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;
|
- (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
|
* NOTE: Use via RCTLog* macros defined in RCTLog.h
|
||||||
* TODO (#5906496): should log function be exposed here, or could it be a module?
|
* TODO (#5906496): should log function be exposed here, or could it be a module?
|
||||||
*/
|
*/
|
||||||
+ (void)log:(NSArray *)objects level:(NSString *)level;
|
+ (void)log:(NSArray *)objects level:(NSString *)level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to check that a valid executor exists with which to log
|
||||||
|
*/
|
||||||
+ (BOOL)hasValidJSExecutor;
|
+ (BOOL)hasValidJSExecutor;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -2,27 +2,18 @@
|
||||||
|
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
|
|
||||||
|
#import <dlfcn.h>
|
||||||
|
#import <mach-o/getsect.h>
|
||||||
|
#import <mach-o/dyld.h>
|
||||||
#import <objc/message.h>
|
#import <objc/message.h>
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
#import "RCTModuleMethod.h"
|
#import "RCTConvert.h"
|
||||||
#import "RCTInvalidating.h"
|
#import "RCTInvalidating.h"
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
#import "RCTUtils.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`.
|
* Must be kept in sync with `MessageQueue.js`.
|
||||||
*/
|
*/
|
||||||
|
@ -35,13 +26,148 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
||||||
RCTBridgeFieldFlushDateMillis
|
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 NSMutableDictionary *modules;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
modules = [NSMutableDictionary dictionary];
|
modules = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
unsigned int classCount;
|
unsigned int classCount;
|
||||||
Class *classes = objc_copyClassList(&classCount);
|
Class *classes = objc_copyClassList(&classCount);
|
||||||
for (unsigned int i = 0; i < classCount; i++) {
|
for (unsigned int i = 0; i < classCount; i++) {
|
||||||
|
@ -53,17 +179,17 @@ static NSDictionary *RCTNativeModuleClasses(void)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![cls conformsToProtocol:@protocol(RCTNativeModule)]) {
|
if (![cls conformsToProtocol:@protocol(RCTBridgeModule)]) {
|
||||||
// Not an RCTNativeModule
|
// Not an RCTBridgeModule
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get module name
|
// Get module name
|
||||||
NSString *moduleName = [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
|
NSString *moduleName = [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
|
||||||
|
|
||||||
// Check module name is unique
|
// Check module name is unique
|
||||||
id existingClass = modules[moduleName];
|
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;
|
modules[moduleName] = cls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,11 +199,166 @@ static NSDictionary *RCTNativeModuleClasses(void)
|
||||||
return modules;
|
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
|
@implementation RCTBridge
|
||||||
{
|
{
|
||||||
NSMutableDictionary *_moduleInstances;
|
NSMutableDictionary *_moduleInstances;
|
||||||
NSMutableDictionary *_moduleIDLookup;
|
|
||||||
NSMutableDictionary *_methodIDLookup;
|
|
||||||
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +371,10 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
_latestJSExecutor = _javaScriptExecutor;
|
_latestJSExecutor = _javaScriptExecutor;
|
||||||
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||||
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
|
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
|
||||||
|
|
||||||
// Instantiate modules
|
// Instantiate modules
|
||||||
_moduleInstances = [[NSMutableDictionary alloc] init];
|
_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 (_moduleInstances[moduleName] == nil) {
|
||||||
if ([moduleClass instancesRespondToSelector:@selector(initWithBridge:)]) {
|
if ([moduleClass instancesRespondToSelector:@selector(initWithBridge:)]) {
|
||||||
_moduleInstances[moduleName] = [[moduleClass alloc] initWithBridge:self];
|
_moduleInstances[moduleName] = [[moduleClass alloc] initWithBridge:self];
|
||||||
|
@ -103,17 +384,27 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
_moduleIDLookup = [[NSMutableDictionary alloc] init];
|
// Inject module data into JS context
|
||||||
_methodIDLookup = [[NSMutableDictionary alloc] init];
|
NSString *configJSON = RCTJSONStringify(@{
|
||||||
[self doneRegisteringModules];
|
@"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;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (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
|
#pragma mark - RCTInvalidating
|
||||||
|
@ -129,7 +420,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
_latestJSExecutor = nil;
|
_latestJSExecutor = nil;
|
||||||
}
|
}
|
||||||
_javaScriptExecutor = nil;
|
_javaScriptExecutor = nil;
|
||||||
|
|
||||||
dispatch_sync(_shadowQueue, ^{
|
dispatch_sync(_shadowQueue, ^{
|
||||||
// Make sure all dispatchers have been executed before continuing
|
// Make sure all dispatchers have been executed before continuing
|
||||||
// TODO: is this still needed?
|
// TODO: is this still needed?
|
||||||
|
@ -157,11 +448,11 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
*/
|
*/
|
||||||
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
||||||
{
|
{
|
||||||
NSNumber *moduleID = _moduleIDLookup[moduleDotMethod];
|
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
|
||||||
RCTAssert(moduleID, @"Module '%@' not registered.",
|
RCTAssert(moduleID, @"Module '%@' not registered.",
|
||||||
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
|
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
|
||||||
|
|
||||||
NSNumber *methodID = _methodIDLookup[moduleDotMethod];
|
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
|
||||||
RCTAssert(methodID, @"Method '%@' not registered.", moduleDotMethod);
|
RCTAssert(methodID, @"Method '%@' not registered.", moduleDotMethod);
|
||||||
|
|
||||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||||
|
@ -177,7 +468,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
onComplete(scriptLoadError);
|
onComplete(scriptLoadError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[_javaScriptExecutor executeJSCall:@"BatchedBridge"
|
[_javaScriptExecutor executeJSCall:@"BatchedBridge"
|
||||||
method:@"flushedQueue"
|
method:@"flushedQueue"
|
||||||
arguments:@[]
|
arguments:@[]
|
||||||
|
@ -193,19 +484,19 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
|
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
|
||||||
{
|
{
|
||||||
NSTimeInterval startJS = RCTTGetAbsoluteTime();
|
NSTimeInterval startJS = RCTTGetAbsoluteTime();
|
||||||
|
|
||||||
RCTJavaScriptCallback processResponse = ^(id objcValue, NSError *error) {
|
RCTJavaScriptCallback processResponse = ^(id objcValue, NSError *error) {
|
||||||
NSTimeInterval startNative = RCTTGetAbsoluteTime();
|
NSTimeInterval startNative = RCTTGetAbsoluteTime();
|
||||||
[self _handleBuffer:objcValue];
|
[self _handleBuffer:objcValue];
|
||||||
|
|
||||||
NSTimeInterval end = RCTTGetAbsoluteTime();
|
NSTimeInterval end = RCTTGetAbsoluteTime();
|
||||||
NSTimeInterval timeJS = startNative - startJS;
|
NSTimeInterval timeJS = startNative - startJS;
|
||||||
NSTimeInterval timeNative = end - startNative;
|
NSTimeInterval timeNative = end - startNative;
|
||||||
|
|
||||||
// TODO: surface this performance information somewhere
|
// TODO: surface this performance information somewhere
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"PERF" object:nil userInfo:@{@"JS": @(timeJS * 1000000), @"Native": @(timeNative * 1000000)}];
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"PERF" object:nil userInfo:@{@"JS": @(timeJS * 1000000), @"Native": @(timeNative * 1000000)}];
|
||||||
};
|
};
|
||||||
|
|
||||||
[_javaScriptExecutor executeJSCall:module
|
[_javaScriptExecutor executeJSCall:module
|
||||||
method:method
|
method:method
|
||||||
arguments:args
|
arguments:args
|
||||||
|
@ -229,12 +520,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
if (buffer == nil || buffer == (id)kCFNull) {
|
if (buffer == nil || buffer == (id)kCFNull) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![buffer isKindOfClass:[NSArray class]]) {
|
if (![buffer isKindOfClass:[NSArray class]]) {
|
||||||
RCTLogMustFix(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class]));
|
RCTLogMustFix(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSArray *requestsArray = (NSArray *)buffer;
|
NSArray *requestsArray = (NSArray *)buffer;
|
||||||
NSUInteger bufferRowCount = [requestsArray count];
|
NSUInteger bufferRowCount = [requestsArray count];
|
||||||
NSUInteger expectedFieldsCount = RCTBridgeFieldResponseReturnValues + 1;
|
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);
|
RCTLogMustFix(@"Must pass all fields to buffer - expected %zd, saw %zd", expectedFieldsCount, bufferRowCount);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (NSUInteger fieldIndex = RCTBridgeFieldRequestModuleIDs; fieldIndex <= RCTBridgeFieldParamss; fieldIndex++) {
|
for (NSUInteger fieldIndex = RCTBridgeFieldRequestModuleIDs; fieldIndex <= RCTBridgeFieldParamss; fieldIndex++) {
|
||||||
id field = [requestsArray objectAtIndex:fieldIndex];
|
id field = [requestsArray objectAtIndex:fieldIndex];
|
||||||
if (![field isKindOfClass:[NSArray class]]) {
|
if (![field isKindOfClass:[NSArray class]]) {
|
||||||
|
@ -250,27 +541,27 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs];
|
NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs];
|
||||||
NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs];
|
NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs];
|
||||||
NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss];
|
NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss];
|
||||||
|
|
||||||
NSUInteger numRequests = [moduleIDs count];
|
NSUInteger numRequests = [moduleIDs count];
|
||||||
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count];
|
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count];
|
||||||
if (!allSame) {
|
if (!allSame) {
|
||||||
RCTLogMustFix(@"Invalid data message - all must be length: %zd", numRequests);
|
RCTLogMustFix(@"Invalid data message - all must be length: %zd", numRequests);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (NSUInteger i = 0; i < numRequests; i++) {
|
for (NSUInteger i = 0; i < numRequests; i++) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
[self _handleRequestNumber:i
|
[self _handleRequestNumber:i
|
||||||
moduleID:[moduleIDs[i] integerValue]
|
moduleID:moduleIDs[i]
|
||||||
methodID:[methodIDs[i] integerValue]
|
methodID:[methodIDs[i] integerValue]
|
||||||
params:paramsArrays[i]];
|
params:paramsArrays[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||||
dispatch_async(_shadowQueue, ^{
|
dispatch_async(_shadowQueue, ^{
|
||||||
for (id target in _moduleInstances.objectEnumerator) {
|
for (id target in _moduleInstances.objectEnumerator) {
|
||||||
|
@ -282,7 +573,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_handleRequestNumber:(NSUInteger)i
|
- (BOOL)_handleRequestNumber:(NSUInteger)i
|
||||||
moduleID:(NSInteger)moduleID
|
moduleID:(NSNumber *)moduleID
|
||||||
methodID:(NSInteger)methodID
|
methodID:(NSInteger)methodID
|
||||||
params:(NSArray *)params
|
params:(NSArray *)params
|
||||||
{
|
{
|
||||||
|
@ -291,13 +582,15 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moduleID < 0 || moduleID >= RCTExportedMethodsByModule().count) {
|
NSString *moduleName = RCTRemoteModulesByID[moduleID];
|
||||||
|
if (!moduleName) {
|
||||||
|
RCTLogMustFix(@"Unknown moduleID: %@", moduleID);
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *moduleName = RCTExportedModuleNameAtSortedIndex(moduleID);
|
|
||||||
NSArray *methods = RCTExportedMethodsByModule()[moduleName];
|
NSArray *methods = RCTExportedMethodsByModule()[moduleName];
|
||||||
if (methodID < 0 || methodID >= methods.count) {
|
if (methodID >= methods.count) {
|
||||||
|
RCTLogMustFix(@"Unknown methodID: %zd for module: %@", methodID, moduleName);
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,13 +614,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
// invocation should not continue.
|
// invocation should not continue.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we should just store module instances by index, since that's how we look them up anyway
|
// TODO: we should just store module instances by index, since that's how we look them up anyway
|
||||||
id target = strongSelf->_moduleInstances[moduleName];
|
id target = strongSelf->_moduleInstances[moduleName];
|
||||||
RCTAssert(target != nil, @"No module found for name '%@'", moduleName);
|
RCTAssert(target != nil, @"No module found for name '%@'", moduleName);
|
||||||
|
|
||||||
SEL selector = method.selector;
|
SEL selector = method.selector;
|
||||||
|
|
||||||
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
|
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
|
||||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||||
[invocation setArgument:&target atIndex:0];
|
[invocation setArgument:&target atIndex:0];
|
||||||
|
@ -335,7 +627,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
|
|
||||||
// Retain used blocks until after invocation completes.
|
// Retain used blocks until after invocation completes.
|
||||||
NS_VALID_UNTIL_END_OF_SCOPE NSMutableArray *blocks = [NSMutableArray array];
|
NS_VALID_UNTIL_END_OF_SCOPE NSMutableArray *blocks = [NSMutableArray array];
|
||||||
|
|
||||||
[params enumerateObjectsUsingBlock:^(id param, NSUInteger idx, BOOL *stop) {
|
[params enumerateObjectsUsingBlock:^(id param, NSUInteger idx, BOOL *stop) {
|
||||||
if ([param isEqual:[NSNull null]]) {
|
if ([param isEqual:[NSNull null]]) {
|
||||||
param = nil;
|
param = nil;
|
||||||
|
@ -344,9 +636,11 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
[blocks addObject:block];
|
[blocks addObject:block];
|
||||||
param = block;
|
param = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSUInteger argIdx = idx + 2;
|
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;
|
BOOL shouldSet = YES;
|
||||||
const char *argumentType = [methodSignature getArgumentTypeAtIndex:argIdx];
|
const char *argumentType = [methodSignature getArgumentTypeAtIndex:argIdx];
|
||||||
switch (argumentType[0]) {
|
switch (argumentType[0]) {
|
||||||
|
@ -357,7 +651,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
shouldSet = NO;
|
shouldSet = NO;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '*':
|
case '*':
|
||||||
if ([param isKindOfClass:[NSString class]]) {
|
if ([param isKindOfClass:[NSString class]]) {
|
||||||
const char *string = [param UTF8String];
|
const char *string = [param UTF8String];
|
||||||
|
@ -365,36 +659,38 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
shouldSet = NO;
|
shouldSet = NO;
|
||||||
}
|
}
|
||||||
break;
|
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) \
|
#define CASE(_value, _type, _selector) \
|
||||||
case _value: \
|
case _value: \
|
||||||
if ([param respondsToSelector:@selector(_selector)]) { \
|
if ([param respondsToSelector:@selector(_selector)]) { \
|
||||||
_type value = [param _selector]; \
|
_type value = [param _selector]; \
|
||||||
[invocation setArgument:&value atIndex:argIdx]; \
|
[invocation setArgument:&value atIndex:argIdx]; \
|
||||||
shouldSet = NO; \
|
shouldSet = NO; \
|
||||||
} \
|
} \
|
||||||
break;
|
break;
|
||||||
|
|
||||||
CASE('c', char, charValue)
|
CASE('c', char, charValue)
|
||||||
CASE('C', unsigned char, unsignedCharValue)
|
CASE('C', unsigned char, unsignedCharValue)
|
||||||
CASE('s', short, shortValue)
|
CASE('s', short, shortValue)
|
||||||
CASE('S', unsigned short, unsignedShortValue)
|
CASE('S', unsigned short, unsignedShortValue)
|
||||||
CASE('i', int, intValue)
|
CASE('i', int, intValue)
|
||||||
CASE('I', unsigned int, unsignedIntValue)
|
CASE('I', unsigned int, unsignedIntValue)
|
||||||
CASE('l', long, longValue)
|
CASE('l', long, longValue)
|
||||||
CASE('L', unsigned long, unsignedLongValue)
|
CASE('L', unsigned long, unsignedLongValue)
|
||||||
CASE('q', long long, longLongValue)
|
CASE('q', long long, longLongValue)
|
||||||
CASE('Q', unsigned long long, unsignedLongLongValue)
|
CASE('Q', unsigned long long, unsignedLongLongValue)
|
||||||
CASE('f', float, floatValue)
|
CASE('f', float, floatValue)
|
||||||
CASE('d', double, doubleValue)
|
CASE('d', double, doubleValue)
|
||||||
CASE('B', BOOL, boolValue)
|
CASE('B', BOOL, boolValue)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldSet) {
|
if (shouldSet) {
|
||||||
[invocation setArgument:¶m atIndex:argIdx];
|
[invocation setArgument:¶m atIndex:argIdx];
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -420,7 +716,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
if (!cbID) {
|
if (!cbID) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ^(NSArray *args) {
|
return ^(NSArray *args) {
|
||||||
[self _sendResponseToJavaScriptCallbackID:cbID args:args];
|
[self _sendResponseToJavaScriptCallbackID:cbID args:args];
|
||||||
};
|
};
|
||||||
|
@ -433,7 +729,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
invocations = [NSMutableDictionary dictionary];
|
invocations = [NSMutableDictionary dictionary];
|
||||||
});
|
});
|
||||||
|
|
||||||
id key = @(argCount);
|
id key = @(argCount);
|
||||||
NSInvocation *invocation = invocations[key];
|
NSInvocation *invocation = invocations[key];
|
||||||
if (invocation == nil) {
|
if (invocation == nil) {
|
||||||
|
@ -442,170 +738,10 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||||
invocations[key] = invocation;
|
invocations[key] = invocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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
|
- (void)registerRootView:(RCTRootView *)rootView
|
||||||
{
|
{
|
||||||
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||||
|
@ -628,7 +764,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NSMutableArray *args = [NSMutableArray arrayWithObject:level];
|
NSMutableArray *args = [NSMutableArray arrayWithObject:level];
|
||||||
|
|
||||||
// TODO (#5906496): Find out and document why we skip the first object
|
// TODO (#5906496): Find out and document why we skip the first object
|
||||||
for (id ob in [objects subarrayWithRange:(NSRange){1, [objects count] - 1}]) {
|
for (id ob in [objects subarrayWithRange:(NSRange){1, [objects count] - 1}]) {
|
||||||
if ([NSJSONSerialization isValidJSONObject:@[ob]]) {
|
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 <UIKit/UIKit.h>
|
||||||
|
|
||||||
#import "Layout.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
|
@interface RCTConvert : NSObject
|
||||||
|
|
||||||
+ (BOOL)BOOL:(id)json;
|
+ (BOOL)BOOL:(id)json;
|
||||||
|
@ -57,4 +63,21 @@
|
||||||
+ (css_position_type_t)css_position_type_t:(id)json;
|
+ (css_position_type_t)css_position_type_t:(id)json;
|
||||||
+ (css_wrap_type_t)css_wrap_type_t:(id)json;
|
+ (css_wrap_type_t)css_wrap_type_t:(id)json;
|
||||||
|
|
||||||
|
+ (RCTPointerEvents)RCTPointerEvents:(id)json;
|
||||||
|
|
||||||
@end
|
@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 <ImageIO/ImageIO.h>
|
||||||
#import <MobileCoreServices/MobileCoreServices.h>
|
#import <MobileCoreServices/MobileCoreServices.h>
|
||||||
|
#import <objc/message.h>
|
||||||
|
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
|
|
||||||
|
@ -41,7 +42,7 @@ RCT_CONVERTER_CUSTOM(type, name, [json getter])
|
||||||
return default; \
|
return default; \
|
||||||
} \
|
} \
|
||||||
if ([json isKindOfClass:[NSNumber class]]) { \
|
if ([json isKindOfClass:[NSNumber class]]) { \
|
||||||
if ([[mapping allValues] containsObject:json]) { \
|
if ([[mapping allValues] containsObject:json] || [json getter] == default) { \
|
||||||
return [json getter]; \
|
return [json getter]; \
|
||||||
} \
|
} \
|
||||||
RCTLogMustFix(@"Invalid %s '%@'. should be one of: %@", #type, json, [mapping allValues]); \
|
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)
|
@"nowrap": @(CSS_NOWRAP)
|
||||||
}), CSS_NOWRAP, intValue)
|
}), CSS_NOWRAP, intValue)
|
||||||
|
|
||||||
|
RCT_ENUM_CONVERTER(RCTPointerEvents, (@{
|
||||||
|
@"none": @(RCTPointerEventsNone),
|
||||||
|
@"boxonly": @(RCTPointerEventsBoxOnly),
|
||||||
|
@"boxnone": @(RCTPointerEventsBoxNone)
|
||||||
|
}), RCTPointerEventsUnspecified, integerValue)
|
||||||
|
|
||||||
@end
|
@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;
|
@class RCTBridge;
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, RCTTouchEventType) {
|
|
||||||
RCTTouchEventTypeStart,
|
|
||||||
RCTTouchEventTypeMove,
|
|
||||||
RCTTouchEventTypeEnd,
|
|
||||||
RCTTouchEventTypeCancel
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, RCTTextEventType) {
|
typedef NS_ENUM(NSInteger, RCTTextEventType) {
|
||||||
RCTTextEventTypeFocus,
|
RCTTextEventTypeFocus,
|
||||||
RCTTextEventTypeBlur,
|
RCTTextEventTypeBlur,
|
||||||
|
@ -39,13 +32,6 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) {
|
||||||
*/
|
*/
|
||||||
- (void)sendEventWithName:(NSString *)name body:(NSDictionary *)body;
|
- (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
|
* Send text events
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
|
|
||||||
|
#import "RCTAssert.h"
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "UIView+ReactKit.h"
|
#import "UIView+ReactKit.h"
|
||||||
|
|
||||||
|
@ -27,34 +28,6 @@
|
||||||
args:@[body[@"target"], name, body]];
|
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
|
- (void)sendTextEventWithType:(RCTTextEventType)type
|
||||||
reactTag:(NSNumber *)reactTag
|
reactTag:(NSNumber *)reactTag
|
||||||
text:(NSString *)text
|
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
|
// If defined, only log messages that match this regex will fatal
|
||||||
#define RCTLOG_FATAL_REGEX nil
|
#define RCTLOG_FATAL_REGEX nil
|
||||||
|
|
||||||
#define _RCTLog(__RCTLog__level, ...) do { \
|
#define _RCTLog(__RCTLog__level, ...) do { \
|
||||||
NSString *__RCTLog__levelStr; \
|
NSString *__RCTLog__levelStr; \
|
||||||
switch(__RCTLog__level) { \
|
switch(__RCTLog__level) { \
|
||||||
case RCTLOG_INFO: __RCTLog__levelStr = @"info"; break; \
|
case RCTLOG_INFO: __RCTLog__levelStr = @"info"; break; \
|
||||||
case RCTLOG_WARN: __RCTLog__levelStr = @"warn"; break; \
|
case RCTLOG_WARN: __RCTLog__levelStr = @"warn"; break; \
|
||||||
case RCTLOG_ERROR: __RCTLog__levelStr = @"error"; break; \
|
case RCTLOG_ERROR: __RCTLog__levelStr = @"error"; break; \
|
||||||
case RCTLOG_MUSTFIX: __RCTLog__levelStr = @"mustfix"; break; \
|
case RCTLOG_MUSTFIX: __RCTLog__levelStr = @"mustfix"; break; \
|
||||||
} \
|
} \
|
||||||
NSString *__RCTLog__msg = _RCTLogObjects(RCTLogFormat(__VA_ARGS__), __RCTLog__levelStr); \
|
NSString *__RCTLog__msg = _RCTLogObjects(RCTLogFormat(__VA_ARGS__), __RCTLog__levelStr); \
|
||||||
if (__RCTLog__level >= RCTLOG_FATAL_LEVEL) { \
|
if (__RCTLog__level >= RCTLOG_FATAL_LEVEL) { \
|
||||||
BOOL __RCTLog__fail = YES; \
|
BOOL __RCTLog__fail = YES; \
|
||||||
if (RCTLOG_FATAL_REGEX) { \
|
if (RCTLOG_FATAL_REGEX) { \
|
||||||
NSError *__RCTLog__e; \
|
NSError *__RCTLog__e; \
|
||||||
NSRegularExpression *__RCTLog__regex = [NSRegularExpression regularExpressionWithPattern:RCTLOG_FATAL_REGEX options:0 error:&__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; \
|
__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); \
|
RCTCAssert(!__RCTLog__fail, @"RCTLOG_FATAL_LEVEL %@: %@", __RCTLog__levelStr, __RCTLog__msg); \
|
||||||
} \
|
} \
|
||||||
if (__RCTLog__level >= RCTLOG_REDBOX_LEVEL) { \
|
if (__RCTLog__level >= RCTLOG_REDBOX_LEVEL) { \
|
||||||
RCTRedBox *__RCTLog__redBox = [RCTRedBox sharedInstance]; \
|
RCTRedBox *__RCTLog__redBox = [RCTRedBox sharedInstance]; \
|
||||||
[__RCTLog__redBox showErrorMessage:__RCTLog__msg]; \
|
[__RCTLog__redBox showErrorMessage:__RCTLog__msg]; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define RCTLog(...) _RCTLog(RCTLOG_INFO, __VA_ARGS__)
|
#define RCTLog(...) _RCTLog(RCTLOG_INFO, __VA_ARGS__)
|
||||||
|
@ -53,71 +53,13 @@
|
||||||
#define RCTLogFormat(...) _RCTLogFormat(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
|
#define RCTLogFormat(...) _RCTLogFormat(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
|
||||||
#define RCTLogFormatString(...) _RCTLogFormatString(__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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NSString *_RCTLogObjects(NSArray *objects, NSString *level);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,16 @@ void RCTInjectLogFunction(RCTLogFunction func) {
|
||||||
injectedLogFunction = 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
|
// TODO (#5906496): // kinda ugly that this is tied to RCTBridge
|
||||||
NSString *_RCTLogObjects(NSArray *objects, NSString *level)
|
NSString *_RCTLogObjects(NSArray *objects, NSString *level)
|
||||||
{
|
{
|
||||||
|
@ -31,3 +41,48 @@ NSString *_RCTLogObjects(NSArray *objects, NSString *level)
|
||||||
}
|
}
|
||||||
return str;
|
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>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@protocol RCTJavaScriptExecutor;
|
|
||||||
|
|
||||||
@interface RCTRootView : UIView
|
@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;
|
@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;
|
@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, 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.
|
* Reload this root view, or all root views, respectively.
|
||||||
|
|
|
@ -5,25 +5,25 @@
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTContextExecutor.h"
|
#import "RCTContextExecutor.h"
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTJavaScriptAppEngine.h"
|
#import "RCTKeyCommands.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
|
#import "RCTRedBox.h"
|
||||||
#import "RCTTouchHandler.h"
|
#import "RCTTouchHandler.h"
|
||||||
#import "RCTUIManager.h"
|
#import "RCTUIManager.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
#import "RCTViewManager.h"
|
|
||||||
#import "RCTWebViewExecutor.h"
|
#import "RCTWebViewExecutor.h"
|
||||||
#import "UIView+ReactKit.h"
|
#import "UIView+ReactKit.h"
|
||||||
#import "RCTKeyCommands.h"
|
|
||||||
|
|
||||||
NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification";
|
NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification";
|
||||||
|
|
||||||
@implementation RCTRootView
|
@implementation RCTRootView
|
||||||
{
|
{
|
||||||
RCTBridge *_bridge;
|
RCTBridge *_bridge;
|
||||||
RCTJavaScriptAppEngine *_appEngine;
|
|
||||||
RCTTouchHandler *_touchHandler;
|
RCTTouchHandler *_touchHandler;
|
||||||
|
id <RCTJavaScriptExecutor> _executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL _useWebExec;
|
static Class _globalExecutorClass;
|
||||||
|
|
||||||
+ (void)initialize
|
+ (void)initialize
|
||||||
{
|
{
|
||||||
|
@ -36,11 +36,12 @@ static BOOL _useWebExec;
|
||||||
action:^(UIKeyCommand *command) {
|
action:^(UIKeyCommand *command) {
|
||||||
[self reloadAll];
|
[self reloadAll];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// Cmd-D reloads using the web view executor, allows attaching from Safari dev tools.
|
// Cmd-D reloads using the web view executor, allows attaching from Safari dev tools.
|
||||||
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"d"
|
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"d"
|
||||||
modifierFlags:UIKeyModifierCommand
|
modifierFlags:UIKeyModifierCommand
|
||||||
action:^(UIKeyCommand *command) {
|
action:^(UIKeyCommand *command) {
|
||||||
_useWebExec = YES;
|
_globalExecutorClass = [RCTWebViewExecutor class];
|
||||||
[self reloadAll];
|
[self reloadAll];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
@ -50,21 +51,18 @@ static BOOL _useWebExec;
|
||||||
|
|
||||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||||
{
|
{
|
||||||
self = [super initWithCoder:aDecoder];
|
if ((self = [super initWithCoder:aDecoder])) {
|
||||||
if (!self) return nil;
|
[self setUp];
|
||||||
|
}
|
||||||
[self setUp];
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithFrame:(CGRect)frame
|
- (instancetype)initWithFrame:(CGRect)frame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:frame];
|
if ((self = [super initWithFrame:frame])) {
|
||||||
if (!self) return nil;
|
self.backgroundColor = [UIColor whiteColor];
|
||||||
|
[self setUp];
|
||||||
[self setUp];
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +79,6 @@ static BOOL _useWebExec;
|
||||||
selector:@selector(reload)
|
selector:@selector(reload)
|
||||||
name:RCTRootViewReloadNotification
|
name:RCTRootViewReloadNotification
|
||||||
object:nil];
|
object:nil];
|
||||||
self.backgroundColor = [UIColor whiteColor];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
|
@ -104,10 +101,9 @@ static BOOL _useWebExec;
|
||||||
|
|
||||||
NSString *moduleName = _moduleName ?: @"";
|
NSString *moduleName = _moduleName ?: @"";
|
||||||
NSDictionary *appParameters = @{
|
NSDictionary *appParameters = @{
|
||||||
@"rootTag": self.reactTag ?: @0,
|
@"rootTag": self.reactTag,
|
||||||
@"initialProps": self.initialProperties ?: @{},
|
@"initialProps": self.initialProperties ?: @{},
|
||||||
};
|
};
|
||||||
|
|
||||||
[_bridge enqueueJSCall:@"Bundler.runApplication"
|
[_bridge enqueueJSCall:@"Bundler.runApplication"
|
||||||
args:@[moduleName, appParameters]];
|
args:@[moduleName, appParameters]];
|
||||||
}
|
}
|
||||||
|
@ -122,28 +118,81 @@ static BOOL _useWebExec;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
__weak typeof(self) weakSelf = self;
|
// Clean up
|
||||||
RCTJavaScriptCompleteBlock callback = ^(NSError *error) {
|
[self removeGestureRecognizer:_touchHandler];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[weakSelf bundleFinishedLoading:error];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
[_executor invalidate];
|
[_executor invalidate];
|
||||||
[_bridge invalidate];
|
[_bridge invalidate];
|
||||||
|
|
||||||
if (!_useWebExec) {
|
// Choose local executor if specified, followed by global, followed by default
|
||||||
_executor = [[RCTContextExecutor alloc] init];
|
_executor = [[_executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class] alloc] init];
|
||||||
} else {
|
|
||||||
_executor = [[RCTWebViewExecutor alloc] init];
|
|
||||||
}
|
|
||||||
|
|
||||||
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor];
|
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor];
|
||||||
|
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
|
||||||
|
[self addGestureRecognizer:_touchHandler];
|
||||||
|
|
||||||
_appEngine = [[RCTJavaScriptAppEngine alloc] initWithBridge:_bridge];
|
// Load the bundle
|
||||||
_touchHandler = [[RCTTouchHandler alloc] initWithEventDispatcher:_bridge.eventDispatcher rootView:self];
|
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
|
- (void)setScriptURL:(NSURL *)scriptURL
|
||||||
|
@ -156,12 +205,6 @@ static BOOL _useWebExec;
|
||||||
[self loadBundle];
|
[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
|
- (BOOL)isReactRootView
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
|
@ -169,7 +212,6 @@ static BOOL _useWebExec;
|
||||||
|
|
||||||
- (void)reload
|
- (void)reload
|
||||||
{
|
{
|
||||||
[RCTJavaScriptAppEngine resetCacheForBundleAtURL:_scriptURL];
|
|
||||||
[self loadBundle];
|
[self loadBundle];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,10 @@
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@class RCTEventDispatcher;
|
@class RCTBridge;
|
||||||
|
|
||||||
@interface RCTTouchHandler : UIGestureRecognizer
|
@interface RCTTouchHandler : UIGestureRecognizer
|
||||||
|
|
||||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||||
rootView:(UIView *)rootView;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#import <UIKit/UIGestureRecognizerSubclass.h>
|
#import <UIKit/UIGestureRecognizerSubclass.h>
|
||||||
|
|
||||||
#import "RCTAssert.h"
|
#import "RCTAssert.h"
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
#import "RCTUIManager.h"
|
#import "RCTUIManager.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
@ -16,8 +16,7 @@
|
||||||
|
|
||||||
@implementation RCTTouchHandler
|
@implementation RCTTouchHandler
|
||||||
{
|
{
|
||||||
__weak UIView *_rootView;
|
__weak RCTBridge *_bridge;
|
||||||
RCTEventDispatcher *_eventDispatcher;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arrays managed in parallel tracking native touch object along with the
|
* Arrays managed in parallel tracking native touch object along with the
|
||||||
|
@ -35,16 +34,13 @@
|
||||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||||
rootView:(UIView *)rootView
|
|
||||||
{
|
{
|
||||||
if ((self = [super initWithTarget:nil action:NULL])) {
|
if ((self = [super initWithTarget:nil action:NULL])) {
|
||||||
|
|
||||||
RCTAssert(eventDispatcher != nil, @"Expect an event dispatcher");
|
RCTAssert(bridge != nil, @"Expect an event dispatcher");
|
||||||
RCTAssert(rootView != nil, @"Expect a root view");
|
|
||||||
|
|
||||||
_eventDispatcher = eventDispatcher;
|
_bridge = bridge;
|
||||||
_rootView = rootView;
|
|
||||||
|
|
||||||
_nativeTouches = [[NSMutableOrderedSet alloc] init];
|
_nativeTouches = [[NSMutableOrderedSet alloc] init];
|
||||||
_reactTouches = [[NSMutableArray 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
|
// `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.
|
// level components not build using RCT, will fail to recognize gestures.
|
||||||
self.cancelsTouchesInView = NO;
|
self.cancelsTouchesInView = NO;
|
||||||
[_rootView addGestureRecognizer:self];
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, RCTTouchEventType) {
|
||||||
|
RCTTouchEventTypeStart,
|
||||||
|
RCTTouchEventTypeMove,
|
||||||
|
RCTTouchEventTypeEnd,
|
||||||
|
RCTTouchEventTypeCancel
|
||||||
|
};
|
||||||
|
|
||||||
#pragma mark - Bookkeeping for touch indices
|
#pragma mark - Bookkeeping for touch indices
|
||||||
|
|
||||||
- (void)_recordNewTouches:(NSSet *)touches
|
- (void)_recordNewTouches:(NSSet *)touches
|
||||||
|
@ -122,7 +124,7 @@
|
||||||
{
|
{
|
||||||
UITouch *nativeTouch = _nativeTouches[touchIndex];
|
UITouch *nativeTouch = _nativeTouches[touchIndex];
|
||||||
CGPoint windowLocation = [nativeTouch locationInView:nativeTouch.window];
|
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];
|
UIView *touchView = _touchViews[touchIndex];
|
||||||
CGPoint touchViewLocation = [nativeTouch.window convertPoint:windowLocation toView:touchView];
|
CGPoint touchViewLocation = [nativeTouch.window convertPoint:windowLocation toView:touchView];
|
||||||
|
@ -135,15 +137,26 @@
|
||||||
reactTouch[@"timestamp"] = @(nativeTouch.timestamp * 1000); // in ms, for JS
|
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
|
// Update touches
|
||||||
NSMutableArray *changedIndices = [[NSMutableArray alloc] init];
|
NSMutableArray *changedIndexes = [[NSMutableArray alloc] init];
|
||||||
for (UITouch *touch in touches) {
|
for (UITouch *touch in touches) {
|
||||||
NSInteger index = [_nativeTouches indexOfObject:touch];
|
NSInteger index = [_nativeTouches indexOfObject:touch];
|
||||||
RCTAssert(index != NSNotFound, @"Touch not found. This is a critical bug.");
|
RCTAssert(index != NSNotFound, @"Touch not found. This is a critical bug.");
|
||||||
[self _updateReactTouchAtIndex:index];
|
[self _updateReactTouchAtIndex:index];
|
||||||
[changedIndices addObject:@(index)];
|
[changedIndexes addObject:@(index)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deep copy the touches because they will be accessed from another thread
|
// Deep copy the touches because they will be accessed from another thread
|
||||||
|
@ -154,9 +167,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch touch event
|
// Dispatch touch event
|
||||||
[_eventDispatcher sendTouchEventWithType:eventType
|
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveTouches"
|
||||||
touches:reactTouches
|
args:@[eventName, reactTouches, changedIndexes]];
|
||||||
changedIndexes:changedIndices];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Gesture Recognizer Delegate Callbacks
|
#pragma mark - Gesture Recognizer Delegate Callbacks
|
||||||
|
@ -169,7 +181,7 @@
|
||||||
// "start" has to record new touches before extracting the event.
|
// "start" has to record new touches before extracting the event.
|
||||||
// "end"/"cancel" needs to remove the touch *after* extracting the event.
|
// "end"/"cancel" needs to remove the touch *after* extracting the event.
|
||||||
[self _recordNewTouches:touches];
|
[self _recordNewTouches:touches];
|
||||||
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeStart];
|
[self _updateAndDispatchTouches:touches eventName:@"topTouchStart"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
||||||
|
@ -178,20 +190,20 @@
|
||||||
if (self.state == UIGestureRecognizerStateFailed) {
|
if (self.state == UIGestureRecognizerStateFailed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeMove];
|
[self _updateAndDispatchTouches:touches eventName:@"topTouchMove"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||||
{
|
{
|
||||||
[super touchesEnded:touches withEvent:event];
|
[super touchesEnded:touches withEvent:event];
|
||||||
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeEnd];
|
[self _updateAndDispatchTouches:touches eventName:@"topTouchEnd"];
|
||||||
[self _recordRemovedTouches:touches];
|
[self _recordRemovedTouches:touches];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||||
{
|
{
|
||||||
[super touchesCancelled:touches withEvent:event];
|
[super touchesCancelled:touches withEvent:event];
|
||||||
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeCancel];
|
[self _updateAndDispatchTouches:touches eventName:@"topTouchCancel"];
|
||||||
[self _recordRemovedTouches:touches];
|
[self _recordRemovedTouches:touches];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ do { \
|
||||||
NSString *RCTJSONStringify(id jsonObject, NSError **error);
|
NSString *RCTJSONStringify(id jsonObject, NSError **error);
|
||||||
id RCTJSONParse(NSString *jsonString, 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);
|
NSString *RCTMD5Hash(NSString *string);
|
||||||
|
|
||||||
// Get screen metrics in a thread-safe way
|
// Get screen metrics in a thread-safe way
|
||||||
|
|
|
@ -22,8 +22,4 @@
|
||||||
// TODO: Deprecate this
|
// TODO: Deprecate this
|
||||||
- (void)reactBridgeDidFinishTransaction;
|
- (void)reactBridgeDidFinishTransaction;
|
||||||
|
|
||||||
// Invoked when react determines that the view will be removed from the view
|
|
||||||
// hierarchy and never replaced.
|
|
||||||
- (void)reactWillDestroy;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -13,15 +13,16 @@
|
||||||
* environment. And ensure main thread operations are actually added to a queue
|
* environment. And ensure main thread operations are actually added to a queue
|
||||||
* instead of being executed immediately if already on the main thread.
|
* instead of being executed immediately if already on the main thread.
|
||||||
*/
|
*/
|
||||||
@interface RCTWebViewExecutor : NSObject<RCTJavaScriptExecutor, UIWebViewDelegate>
|
@interface RCTWebViewExecutor : NSObject<RCTJavaScriptExecutor>
|
||||||
|
|
||||||
@property (nonatomic, readwrite, strong) UIWebView *webView;
|
|
||||||
|
|
||||||
// Only one callback stored - will only be invoked for the latest issued
|
// Only one callback stored - will only be invoked for the latest issued
|
||||||
// application script request.
|
// 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
|
* 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);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@interface RCTWebViewExecutor () <UIWebViewDelegate>
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation RCTWebViewExecutor
|
@implementation RCTWebViewExecutor
|
||||||
{
|
{
|
||||||
|
UIWebView *_webView;
|
||||||
NSMutableDictionary *_objectsToInject;
|
NSMutableDictionary *_objectsToInject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
#import "RCTExport.h"
|
#import "RCTBridgeModule.h"
|
||||||
|
|
||||||
@interface RCTAlertManager : NSObject <RCTNativeModule>
|
@interface RCTAlertManager : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
#import "RCTExport.h"
|
#import "RCTBridgeModule.h"
|
||||||
|
|
||||||
@interface RCTDataManager : NSObject <RCTNativeModule>
|
@interface RCTDataManager : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
#import "RCTExport.h"
|
#import "RCTBridgeModule.h"
|
||||||
|
|
||||||
@interface RCTExceptionsManager : NSObject <RCTNativeModule>
|
@interface RCTExceptionsManager : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -2,13 +2,9 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
#import "RCTExport.h"
|
#import "RCTBridgeModule.h"
|
||||||
#import "RCTInvalidating.h"
|
#import "RCTInvalidating.h"
|
||||||
|
|
||||||
@class RCTBridge;
|
@interface RCTTiming : NSObject <RCTBridgeModule, RCTInvalidating>
|
||||||
|
|
||||||
@interface RCTTiming : NSObject <RCTNativeModule, RCTInvalidating>
|
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -2,19 +2,16 @@
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
#import "RCTExport.h"
|
#import "RCTBridgeModule.h"
|
||||||
#import "RCTInvalidating.h"
|
#import "RCTInvalidating.h"
|
||||||
|
|
||||||
@class RCTBridge;
|
|
||||||
@class RCTRootView;
|
@class RCTRootView;
|
||||||
@class RCTShadowView;
|
@class RCTShadowView;
|
||||||
|
@class RCTSparseArray;
|
||||||
|
|
||||||
@protocol RCTScrollableProtocol;
|
@protocol RCTScrollableProtocol;
|
||||||
@protocol RCTViewNodeProtocol;
|
|
||||||
|
|
||||||
@interface RCTUIManager : NSObject <RCTNativeModule, RCTInvalidating>
|
@interface RCTUIManager : NSObject <RCTBridgeModule, RCTInvalidating>
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
|
||||||
|
|
||||||
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry;
|
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry;
|
||||||
@property (nonatomic, strong) RCTSparseArray *viewRegistry;
|
@property (nonatomic, strong) RCTSparseArray *viewRegistry;
|
||||||
|
@ -26,9 +23,6 @@
|
||||||
*/
|
*/
|
||||||
@property (nonatomic, readwrite, weak) id<UIScrollViewDelegate> nativeMainScrollDelegate;
|
@property (nonatomic, readwrite, weak) id<UIScrollViewDelegate> nativeMainScrollDelegate;
|
||||||
|
|
||||||
+ (UIView <RCTViewNodeProtocol> *)closestReactAncestor:(UIView *)view;
|
|
||||||
+ (UIView <RCTViewNodeProtocol> *)closestReactAncestorThatRespondsToTouch:(UITouch *)touch;
|
|
||||||
|
|
||||||
- (void)registerRootView:(RCTRootView *)rootView;
|
- (void)registerRootView:(RCTRootView *)rootView;
|
||||||
|
|
||||||
+ (UIView *)JSResponder;
|
+ (UIView *)JSResponder;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
#import "RCTView.h"
|
#import "RCTView.h"
|
||||||
#import "RCTViewNodeProtocol.h"
|
#import "RCTViewNodeProtocol.h"
|
||||||
#import "RCTUIViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
#import "UIView+ReactKit.h"
|
#import "UIView+ReactKit.h"
|
||||||
|
|
||||||
@class RCTAnimationConfig;
|
@class RCTAnimationConfig;
|
||||||
|
@ -52,17 +52,20 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![cls conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
if (![cls isSubclassOfClass:[RCTViewManager class]]) {
|
||||||
// Not an RCTNativeModule
|
// Not a view module
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get module name
|
// Get module name
|
||||||
NSString *moduleName = [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
|
NSString *moduleName = [cls moduleName];
|
||||||
|
|
||||||
// Check module name is unique
|
// Check module name is unique
|
||||||
id existingClass = modules[moduleName];
|
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;
|
modules[moduleName] = cls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,14 +93,13 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
__weak RCTBridge *_bridge;
|
__weak RCTBridge *_bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id <RCTNativeViewModule>)_managerInstanceForViewWithModuleName:(NSString *)moduleName
|
- (RCTViewManager *)_managerInstanceForViewWithModuleName:(NSString *)moduleName
|
||||||
{
|
{
|
||||||
id <RCTNativeViewModule> managerInstance = _viewManagers[moduleName];
|
RCTViewManager *managerInstance = _viewManagers[moduleName];
|
||||||
if (managerInstance == nil) {
|
if (managerInstance == nil) {
|
||||||
RCTLogWarn(@"No manager class found for view with module name \"%@\"", moduleName);
|
RCTLogWarn(@"No manager class found for view with module name \"%@\"", moduleName);
|
||||||
managerInstance = [[RCTUIViewManager alloc] init];
|
managerInstance = [[RCTViewManager alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
return managerInstance;
|
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
|
* Unregisters views from registries
|
||||||
*/
|
*/
|
||||||
|
@ -212,8 +193,8 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
for (id<RCTViewNodeProtocol> child in children) {
|
for (id<RCTViewNodeProtocol> child in children) {
|
||||||
RCTTraverseViewNodes(registry[child.reactTag], ^(id<RCTViewNodeProtocol> subview) {
|
RCTTraverseViewNodes(registry[child.reactTag], ^(id<RCTViewNodeProtocol> subview) {
|
||||||
RCTAssert(![subview isReactRootView], @"Host views should not be unregistered");
|
RCTAssert(![subview isReactRootView], @"Host views should not be unregistered");
|
||||||
if ([subview respondsToSelector:@selector(reactWillDestroy)]) {
|
if ([subview conformsToProtocol:@protocol(RCTInvalidating)]) {
|
||||||
[subview reactWillDestroy];
|
[(id<RCTInvalidating>)subview invalidate];
|
||||||
}
|
}
|
||||||
registry[subview.reactTag] = nil;
|
registry[subview.reactTag] = nil;
|
||||||
});
|
});
|
||||||
|
@ -332,17 +313,10 @@ static NSDictionary *RCTViewModuleClasses(void)
|
||||||
{
|
{
|
||||||
NSMutableSet *applierBlocks = [NSMutableSet setWithCapacity:1];
|
NSMutableSet *applierBlocks = [NSMutableSet setWithCapacity:1];
|
||||||
[topView collectUpdatedProperties:applierBlocks parentProperties:@{}];
|
[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) {
|
[self addUIBlock:^(RCTUIManager *viewManager, RCTSparseArray *viewRegistry) {
|
||||||
for (NSUInteger f = 0; f < immutablePropsAppliers.count; f++) {
|
for (RCTApplierBlock block in applierBlocks) {
|
||||||
RCTApplierBlock applier = [immutablePropsAppliers objectAtIndex: f];
|
block(viewRegistry);
|
||||||
applier(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
|
// TODO: cache respondsToSelector tests
|
||||||
if ([manager respondsToSelector:setter]) {
|
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,
|
static void RCTSetViewProps(NSDictionary *props, UIView *view,
|
||||||
UIView *defaultView, id <RCTNativeViewModule>manager)
|
UIView *defaultView, RCTViewManager *manager)
|
||||||
{
|
{
|
||||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
[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,
|
static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView,
|
||||||
RCTShadowView *defaultView, id <RCTNativeViewModule>manager)
|
RCTShadowView *defaultView, RCTViewManager *manager)
|
||||||
{
|
{
|
||||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||||
|
|
||||||
|
@ -575,14 +549,14 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
{
|
{
|
||||||
RCT_EXPORT(createView);
|
RCT_EXPORT(createView);
|
||||||
|
|
||||||
id <RCTNativeViewModule>manager = [self _managerInstanceForViewWithModuleName:moduleName];
|
RCTViewManager *manager = [self _managerInstanceForViewWithModuleName:moduleName];
|
||||||
|
|
||||||
// Generate default view, used for resetting default props
|
// Generate default view, used for resetting default props
|
||||||
if (!_defaultShadowViews[moduleName]) {
|
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.moduleName = moduleName;
|
||||||
shadowView.reactTag = reactTag;
|
shadowView.reactTag = reactTag;
|
||||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[moduleName], manager);
|
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[moduleName], manager);
|
||||||
|
@ -619,7 +593,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||||
RCT_EXPORT();
|
RCT_EXPORT();
|
||||||
|
|
||||||
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
||||||
id <RCTNativeViewModule>manager = [self _managerInstanceForViewWithModuleName:moduleName];
|
RCTViewManager *manager = [self _managerInstanceForViewWithModuleName:moduleName];
|
||||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[moduleName], manager);
|
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[moduleName], manager);
|
||||||
|
|
||||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
[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
|
// pending blocks to a new array. This guards against mutation while
|
||||||
// processing the pending blocks in another thread.
|
// processing the pending blocks in another thread.
|
||||||
|
|
||||||
for (id <RCTNativeViewModule>viewManager in _viewManagers.allValues) {
|
for (RCTViewManager *manager in _viewManagers.allValues) {
|
||||||
RCTViewManagerUIBlock uiBlock = [viewManager respondsToSelector:@selector(uiBlockToAmendWithShadowViewRegistry:)] ? [viewManager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry] : nil;
|
RCTViewManagerUIBlock uiBlock = [manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
|
||||||
if (uiBlock != nil) {
|
if (uiBlock != nil) {
|
||||||
[self addUIBlock:uiBlock];
|
[self addUIBlock:uiBlock];
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
134FCB371A6D4ED700051CC8 /* RCTRawTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137029581A6C197000575408 /* RCTRawTextManager.m */; };
|
134FCB371A6D4ED700051CC8 /* RCTRawTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137029581A6C197000575408 /* RCTRawTextManager.m */; };
|
||||||
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; };
|
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; };
|
||||||
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.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 */; };
|
137029491A698FF000575408 /* RCTNetworkImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137029401A698FF000575408 /* RCTNetworkImageViewManager.m */; };
|
||||||
137029501A6990A100575408 /* RCTNetworkImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1370294F1A6990A100575408 /* RCTNetworkImageView.m */; };
|
137029501A6990A100575408 /* RCTNetworkImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1370294F1A6990A100575408 /* RCTNetworkImageView.m */; };
|
||||||
137029531A69923600575408 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 137029521A69923600575408 /* RCTImageDownloader.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 */; };
|
13B080291A694C4900A75B9A /* RCTDataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B080281A694C4900A75B9A /* RCTDataManager.m */; };
|
||||||
13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067491A70F434002CDEE1 /* RCTUIManager.m */; };
|
13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067491A70F434002CDEE1 /* RCTUIManager.m */; };
|
||||||
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674C1A70F44B002CDEE1 /* RCTShadowView.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 */; };
|
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 */; };
|
13E067591A70F44B002CDEE1 /* UIView+ReactKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */; };
|
||||||
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 830A229D1A66C68A008503DA /* RCTRootView.m */; };
|
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 830A229D1A66C68A008503DA /* RCTRootView.m */; };
|
||||||
832348161A77A5AA00B55238 /* Layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FC71A68125100A75B9A /* Layout.c */; };
|
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 */; };
|
83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA591A601E9000E9B192 /* RCTRedBox.m */; };
|
||||||
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */; };
|
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */; };
|
||||||
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA661A601EF300E9B192 /* RCTEventDispatcher.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 */; };
|
83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */; };
|
||||||
83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBACB1A6023D300E9B192 /* RCTConvert.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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
@ -128,16 +124,14 @@
|
||||||
13E067491A70F434002CDEE1 /* RCTUIManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManager.m; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
13E0674D1A70F44B002CDEE1 /* RCTViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewManager.h; sourceTree = "<group>"; };
|
||||||
13E0674E1A70F44B002CDEE1 /* RCTUIViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIViewManager.m; 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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
13ED13891A80C9D40050A8F9 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = "<group>"; };
|
||||||
830213F41A65574D00B993E6 /* RCTExport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTExport.m; 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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -224,12 +214,10 @@
|
||||||
13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */,
|
13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */,
|
||||||
13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */,
|
13E0674B1A70F44B002CDEE1 /* RCTShadowView.h */,
|
||||||
13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */,
|
13E0674C1A70F44B002CDEE1 /* RCTShadowView.m */,
|
||||||
13E0674D1A70F44B002CDEE1 /* RCTUIViewManager.h */,
|
|
||||||
13E0674E1A70F44B002CDEE1 /* RCTUIViewManager.m */,
|
|
||||||
13E0674F1A70F44B002CDEE1 /* RCTView.h */,
|
13E0674F1A70F44B002CDEE1 /* RCTView.h */,
|
||||||
13E067501A70F44B002CDEE1 /* RCTView.m */,
|
13E067501A70F44B002CDEE1 /* RCTView.m */,
|
||||||
13E067511A70F44B002CDEE1 /* RCTViewManager.h */,
|
13E0674D1A70F44B002CDEE1 /* RCTViewManager.h */,
|
||||||
13E067521A70F44B002CDEE1 /* RCTViewManager.m */,
|
13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */,
|
||||||
13B07FF61A6947C200A75B9A /* RCTScrollView.h */,
|
13B07FF61A6947C200A75B9A /* RCTScrollView.h */,
|
||||||
13B07FF71A6947C200A75B9A /* RCTScrollView.m */,
|
13B07FF71A6947C200A75B9A /* RCTScrollView.m */,
|
||||||
13B07FF81A6947C200A75B9A /* RCTScrollViewManager.h */,
|
13B07FF81A6947C200A75B9A /* RCTScrollViewManager.h */,
|
||||||
|
@ -308,17 +296,12 @@
|
||||||
83CBBA611A601EB200E9B192 /* RCTAutoInsetsProtocol.h */,
|
83CBBA611A601EB200E9B192 /* RCTAutoInsetsProtocol.h */,
|
||||||
83CBBA5E1A601EAA00E9B192 /* RCTBridge.h */,
|
83CBBA5E1A601EAA00E9B192 /* RCTBridge.h */,
|
||||||
83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */,
|
83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */,
|
||||||
83EEC2EC1A604AB200C39218 /* RCTModuleMethod.h */,
|
|
||||||
83EEC2ED1A604AB200C39218 /* RCTModuleMethod.m */,
|
|
||||||
83CBBACA1A6023D300E9B192 /* RCTConvert.h */,
|
83CBBACA1A6023D300E9B192 /* RCTConvert.h */,
|
||||||
83CBBACB1A6023D300E9B192 /* RCTConvert.m */,
|
83CBBACB1A6023D300E9B192 /* RCTConvert.m */,
|
||||||
830213F31A654E0800B993E6 /* RCTExport.h */,
|
830213F31A654E0800B993E6 /* RCTBridgeModule.h */,
|
||||||
830213F41A65574D00B993E6 /* RCTExport.m */,
|
|
||||||
830A229C1A66C68A008503DA /* RCTRootView.h */,
|
830A229C1A66C68A008503DA /* RCTRootView.h */,
|
||||||
830A229D1A66C68A008503DA /* RCTRootView.m */,
|
830A229D1A66C68A008503DA /* RCTRootView.m */,
|
||||||
83CBBA4C1A601E3B00E9B192 /* RCTInvalidating.h */,
|
83CBBA4C1A601E3B00E9B192 /* RCTInvalidating.h */,
|
||||||
83CBBA851A60202500E9B192 /* RCTJavaScriptAppEngine.h */,
|
|
||||||
83CBBA861A60202500E9B192 /* RCTJavaScriptAppEngine.m */,
|
|
||||||
83CBBA651A601EF300E9B192 /* RCTEventDispatcher.h */,
|
83CBBA651A601EF300E9B192 /* RCTEventDispatcher.h */,
|
||||||
83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */,
|
83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */,
|
||||||
83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */,
|
83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */,
|
||||||
|
@ -336,6 +319,7 @@
|
||||||
137029511A69923600575408 /* RCTImageDownloader.h */,
|
137029511A69923600575408 /* RCTImageDownloader.h */,
|
||||||
137029521A69923600575408 /* RCTImageDownloader.m */,
|
137029521A69923600575408 /* RCTImageDownloader.m */,
|
||||||
13B07FCD1A683B5F00A75B9A /* RCTScrollableProtocol.h */,
|
13B07FCD1A683B5F00A75B9A /* RCTScrollableProtocol.h */,
|
||||||
|
13ED13891A80C9D40050A8F9 /* RCTPointerEvents.h */,
|
||||||
);
|
);
|
||||||
path = Base;
|
path = Base;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -424,7 +408,7 @@
|
||||||
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,
|
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,
|
||||||
832348161A77A5AA00B55238 /* Layout.c in Sources */,
|
832348161A77A5AA00B55238 /* Layout.c in Sources */,
|
||||||
13B080201A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m in Sources */,
|
13B080201A69489C00A75B9A /* RCTUIActivityIndicatorViewManager.m in Sources */,
|
||||||
13E067561A70F44B002CDEE1 /* RCTUIViewManager.m in Sources */,
|
13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */,
|
||||||
13B080061A6947C200A75B9A /* RCTScrollViewManager.m in Sources */,
|
13B080061A6947C200A75B9A /* RCTScrollViewManager.m in Sources */,
|
||||||
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */,
|
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */,
|
||||||
13B080051A6947C200A75B9A /* RCTScrollView.m in Sources */,
|
13B080051A6947C200A75B9A /* RCTScrollView.m in Sources */,
|
||||||
|
@ -442,17 +426,13 @@
|
||||||
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */,
|
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */,
|
||||||
137029491A698FF000575408 /* RCTNetworkImageViewManager.m in Sources */,
|
137029491A698FF000575408 /* RCTNetworkImageViewManager.m in Sources */,
|
||||||
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */,
|
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */,
|
||||||
13E067581A70F44B002CDEE1 /* RCTViewManager.m in Sources */,
|
|
||||||
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */,
|
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */,
|
||||||
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */,
|
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */,
|
||||||
13B080081A6947C200A75B9A /* RCTShadowText.m in Sources */,
|
13B080081A6947C200A75B9A /* RCTShadowText.m in Sources */,
|
||||||
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */,
|
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */,
|
||||||
13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */,
|
13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */,
|
||||||
83CBBA871A60202500E9B192 /* RCTJavaScriptAppEngine.m in Sources */,
|
|
||||||
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */,
|
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */,
|
||||||
83EEC2EE1A604AB200C39218 /* RCTModuleMethod.m in Sources */,
|
|
||||||
13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */,
|
13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */,
|
||||||
137029331A69659C00575408 /* RCTExport.m in Sources */,
|
|
||||||
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */,
|
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */,
|
||||||
134FCB371A6D4ED700051CC8 /* RCTRawTextManager.m in Sources */,
|
134FCB371A6D4ED700051CC8 /* RCTRawTextManager.m in Sources */,
|
||||||
13B0800B1A6947C200A75B9A /* RCTTextManager.m in Sources */,
|
13B0800B1A6947C200A75B9A /* RCTTextManager.m in Sources */,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
#import "RCTUIViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
@interface RCTNavItemManager : RCTUIViewManager
|
@interface RCTNavItemManager : RCTViewManager
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import "RCTInvalidating.h"
|
||||||
|
|
||||||
@class RCTEventDispatcher;
|
@class RCTEventDispatcher;
|
||||||
|
|
||||||
@interface RCTNavigator : UIView <UINavigationControllerDelegate>
|
@interface RCTNavigator : UIView <RCTInvalidating>
|
||||||
|
|
||||||
@property (nonatomic, strong) UIView *reactNavSuperviewLink;
|
@property (nonatomic, strong) UIView *reactNavSuperviewLink;
|
||||||
@property (nonatomic, assign) NSInteger requestedTopOfStack;
|
@property (nonatomic, assign) NSInteger requestedTopOfStack;
|
||||||
|
|
|
@ -200,7 +200,7 @@ NSInteger kNeverProgressed = -10000;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@interface RCTNavigator() <RCTWrapperViewControllerNavigationListener>
|
@interface RCTNavigator() <RCTWrapperViewControllerNavigationListener, UINavigationControllerDelegate>
|
||||||
{
|
{
|
||||||
RCTEventDispatcher *_eventDispatcher;
|
RCTEventDispatcher *_eventDispatcher;
|
||||||
NSInteger _numberOfViewControllerMovesToIgnore;
|
NSInteger _numberOfViewControllerMovesToIgnore;
|
||||||
|
@ -417,9 +417,14 @@ NSInteger kNeverProgressed = -10000;
|
||||||
return _currentViews;
|
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 invalidate];
|
||||||
_displayLink = nil;
|
_displayLink = nil;
|
||||||
_runTimer = nil;
|
_runTimer = nil;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
#import "RCTUIViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
@interface RCTNavigatorManager : RCTUIViewManager
|
@interface RCTNavigatorManager : RCTViewManager
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
#import "RCTUIViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
@interface RCTNetworkImageViewManager : RCTUIViewManager
|
@interface RCTNetworkImageViewManager : RCTViewManager
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
#import "RCTUIViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
@interface RCTRawTextManager : RCTUIViewManager
|
@interface RCTRawTextManager : RCTViewManager
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -242,11 +242,6 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)insertReactSubview:(NSObject<RCTViewNodeProtocol> *)subview atIndex:(NSInteger)atIndex
|
|
||||||
{
|
|
||||||
[super insertReactSubview:subview atIndex:atIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation RCTScrollView
|
@implementation RCTScrollView
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
#import "RCTUIViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
@interface RCTScrollViewManager : RCTUIViewManager
|
@interface RCTScrollViewManager : RCTViewManager
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
#import "RCTUIViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
@interface RCTStaticImageManager : RCTUIViewManager
|
@interface RCTStaticImageManager : RCTViewManager
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -14,12 +14,13 @@
|
||||||
return [[RCTStaticImage alloc] init];
|
return [[RCTStaticImage alloc] init];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(capInsets)
|
||||||
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode)
|
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode)
|
||||||
|
|
||||||
- (void)set_src:(id)json forView:(RCTStaticImage *)view withDefaultView:(RCTStaticImage *)defaultView
|
- (void)set_src:(id)json forView:(RCTStaticImage *)view withDefaultView:(RCTStaticImage *)defaultView
|
||||||
{
|
{
|
||||||
if (json) {
|
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"];
|
[view.layer addAnimation:[RCTConvert GIF:json] forKey:@"contents"];
|
||||||
} else {
|
} else {
|
||||||
view.image = [RCTConvert UIImage:json];
|
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
|
- (void)set_tintColor:(id)json forView:(RCTStaticImage *)view withDefaultView:(RCTStaticImage *)defaultView
|
||||||
{
|
{
|
||||||
if (json) {
|
if (json) {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
#import "RCTUIViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
@interface RCTTextFieldManager : RCTUIViewManager
|
@interface RCTTextFieldManager : RCTViewManager
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
#import "RCTUIViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
@interface RCTTextManager : RCTUIViewManager
|
@interface RCTTextManager : RCTViewManager
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,13 @@ RCT_REMAP_VIEW_PROPERTY(textAlign, textAlignment);
|
||||||
shadowView.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
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
|
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry
|
||||||
{
|
{
|
||||||
NSMutableArray *shadowBlocks = [NSMutableArray new];
|
NSMutableArray *shadowBlocks = [NSMutableArray new];
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
#import "RCTUIViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
@interface RCTUIActivityIndicatorViewManager : RCTUIViewManager
|
@interface RCTUIActivityIndicatorViewManager : RCTViewManager
|
||||||
|
|
||||||
@end
|
@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>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
// TODO: rehome this
|
#import "RCTPointerEvents.h"
|
||||||
typedef NS_ENUM(NSInteger, RCTPointerEventsValue) {
|
|
||||||
RCTPointerEventsUnspecified = 0, // Default
|
|
||||||
RCTPointerEventsNone,
|
|
||||||
RCTPointerEventsBoxNone,
|
|
||||||
RCTPointerEventsBoxOnly,
|
|
||||||
};
|
|
||||||
|
|
||||||
@protocol RCTAutoInsetsProtocol;
|
@protocol RCTAutoInsetsProtocol;
|
||||||
|
|
||||||
@interface RCTView : UIView
|
@interface RCTView : UIView
|
||||||
|
|
||||||
@property (nonatomic, assign) RCTPointerEventsValue pointerEvents;
|
@property (nonatomic, assign) RCTPointerEvents pointerEvents;
|
||||||
@property (nonatomic, copy) NSString *overrideAccessibilityLabel;
|
@property (nonatomic, copy) NSString *overrideAccessibilityLabel;
|
||||||
|
|
||||||
+ (void)autoAdjustInsetsForView:(UIView<RCTAutoInsetsProtocol> *)parentView
|
+ (void)autoAdjustInsetsForView:(UIView<RCTAutoInsetsProtocol> *)parentView
|
||||||
|
|
|
@ -40,7 +40,7 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||||
return RCTRecursiveAccessibilityLabel(self);
|
return RCTRecursiveAccessibilityLabel(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setPointerEvents:(RCTPointerEventsValue)pointerEvents
|
- (void)setPointerEvents:(RCTPointerEvents)pointerEvents
|
||||||
{
|
{
|
||||||
_pointerEvents = pointerEvents;
|
_pointerEvents = pointerEvents;
|
||||||
self.userInteractionEnabled = (pointerEvents != RCTPointerEventsNone);
|
self.userInteractionEnabled = (pointerEvents != RCTPointerEventsNone);
|
||||||
|
|
|
@ -1,7 +1,148 @@
|
||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
// 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
|
@end
|
||||||
|
|
|
@ -2,15 +2,169 @@
|
||||||
|
|
||||||
#import "RCTViewManager.h"
|
#import "RCTViewManager.h"
|
||||||
|
|
||||||
|
#import "RCTConvert.h"
|
||||||
|
#import "RCTEventDispatcher.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
|
#import "RCTShadowView.h"
|
||||||
#import "RCTView.h"
|
#import "RCTView.h"
|
||||||
|
|
||||||
@implementation RCTViewManager
|
@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
|
- (UIView *)view
|
||||||
{
|
{
|
||||||
return [[RCTView alloc] init];
|
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
|
@end
|
||||||
|
|
Загрузка…
Ссылка в новой задаче