Invoke any native code directly from Javascript in React Native (without wrapping it first with a native manager). Gives you full access from JS to all native API of iOS and Android.
The story behind this library and why it might be useful:
https://medium.com/@talkol/invoke-any-native-api-directly-from-pure-javascript-in-react-native-1fb6afcdf57d
####Both Platforms
- In your project root,
npm install rn-invoke --save
or add it in yourpackage.json
:
"dependencies": {
...
"rn-invoke": "^0.2.0"
}
-
In your project root,
npm install rn-invoke --save
-
In Xcode, in Project Navigator (left pane), right-click on the
Libraries
>Add files to [project name]
Add./node_modules/rn-invoke/ios/RNInvoke.xcodeproj
-
In Xcode, in Project Navigator (left pane), click on your project (top) and select the
Build Phases
tab (right pane)
In theLink Binary With Libraries
section addlibRNInvoke.a
-
In Xcode, in Project Navigator (left pane), click on your project (top) and select the
Build Settings
tab (right pane)
In theHeader Search Paths
section add$(SRCROOT)/../node_modules/rn-invoke/ios
Make sure on the right to mark this new pathrecursive
-
Add
rn-invoke
fromnode_modules
to yoursettings.gradle
:include ':app' include ':rn-invoke' project(':rn-invoke').projectDir = new File(rootProject.projectDir, '../node_modules/rn-invoke/android/invoke')
-
In
app/build.gradle
addrn-invoke
as a dependencydependencies { ... compile project(':rn-invoke') }
- In `YourApplication.java` register `InvokeReactPackage` in your pacakges:
```java
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
...
new InvokeReactPackage()
);
}
```
<br><br>
## Executing calls to native
> Notice that this is regular Javascript code. It has full access to all native API in iOS and there's no RN native manager involved wrapping each individual API call.
```js
import Invoke from 'rn-invoke';
// execute a single call
const _getContentOffset = Invoke.call(_scrollView, 'contentOffset');
const {x, y} = await Invoke.execute(_getContentOffset);
Invoke.execute returns a promise. The native code doesn't actually execute until Invoke.execute runs.
// execute multiple calls
const _getScrollView = Invoke.call(_scrollParent, 'scrollView');
const _getContentOffset = Invoke.call(_getScrollView, 'contentOffset');
const {x, y} = await Invoke.execute(_getContentOffset);
Only simple serializable objects can pass between native and JS. Since many methods take a complex object as argument, we support making multiple calls in one execution so the result of one call can be passed to the next one without going through JS.
###iOS
CGPoint offset = [componentView.scrollView contentOffset];
const _getScrollView = Invoke.call(_componentView, 'scrollView');
const _getOffset = Invoke.call(_getScrollView, 'contentOffset');
const {x, y} = await Invoke.execute(_getOffset);
###### 2. from Objective-C
CGRect frame = componentView.frame;
const _getFrame = Invoke.call(_componentView, 'frame');
let {x, y, width, height} = await Invoke.execute(_getFrame);
###### 3. from Objective-C
[componentView setFrame:frame];
const _setFrame = Invoke.call(_componentView, 'setFrame:', Invoke.IOS.CGRect({x, y, width, height}));
await Invoke.execute(_setFrame);
###### 4. from Objective-C
id textView = [componentView valueForKey:@'_textView'];
CGRect pos = [textView caretRectForPosition:textView.selectedTextRange.start];
const _getTextView = Invoke.call(_componentView, 'valueForKey:', '_textView');
const _getSelectedTextRange = Invoke.call(_getTextView, 'selectedTextRange');
const _getStartPosition = Invoke.call(_getSelectedTextRange, 'start');
const _getCaretRect = Invoke.call(_getTextView, 'caretRectForPosition:', _getStartPosition);
const {x, y, width, height} = await Invoke.execute(_getCaretRect);
###Android
reactSwipeRefreshLayout.setRefreshing(false);
const swipeRefreshLayout = Invoke.React.view(this.refs['refresh']);
const setRefreshing = Invoke.call(swipeRefreshLayout, 'setRefreshing', {type: "Boolean", value: false});
await Invoke.execute(setRefreshing);
scrollView.getScrollY()
const scrollView = Invoke.React.view(this.refs['scroll']);
const getScrollY = Invoke.call(scrollView, 'getScrollY');
const y = await Invoke.execute(getScrollY);
textView.getSelectionEnd()
const textView = Invoke.React.view(this.refs['input']);
const getSelectionEnd = Invoke.call(textView, 'getSelectionEnd');
const selectionEnd = await Invoke.execute(getSelectionEnd);
## Full example project Available [here](example)
####iOS
cd example
npm install
react-native run-ios
or open ios/example.xcodeproj
to open in xcode.
####Android
cd example
npm install
react-native run-android
####javascript
- Example of getting the scroll offset of a ScrollView
- Example of getting, changing and setting the frame of RefreshControl
- Example of getting the cursor pos from a TextInput
Send the entire invocation to native and execute it. Code runs in native only when we reach this command. Returns a promise with the final return value (make sure it's serializable).
Prepare a call for later execution.
Returns (in later execution) the native view backing the React component ref.
Example:
<ScrollView refreshControl={<RefreshControl refreshing={true} ref='myRefreshControl'/>} />
const _componentView = Invoke.React.view(this.refs['myRefreshControl']);
##### > `Invoke.IOS.CGPoint({x, y})`
Returns (in later execution) an iOS point.
Returns (in later execution) an iOS rect.
-
The final return value from native arrives as the promise result of
Invoke.execute
. It has to be serializable! If you have return values that aren't serializable (like complex objects), you probably need to have severalInvoke.call
s and pass them between eachother. -
All native code is executed on the main thread.
## License
MIT