Use the NativeScript runtime from React Native
MIT License
Use the NativeScript runtime in a React Native app!
Still a work-in-progress. See the TODO section at the bottom of the Readme for current progress.
In short: We are not yet running NativeScript code in React Native. However, we do have a script to automatically integrate the NativeScript library set up in a React Native iOS project. I've not paid much attention to Android at all, yet.
npm install react-native-nativescript-runtime
import NativescriptRuntime from "react-native-nativescript-runtime";
// ...
const result = await NativescriptRuntime.multiply(3, 7);
I'll explain the repo structure.
This repo was initialised using the standard react-native-builder-bob tool, which bootstraps a best-practice boilerplate for making a React Native plugin (whilst also testing it on an example app).
To get an impression of what changes I've made beyond initialising the repo, see the next section where I provide a diff.
To understand what the purpose of this labyrinth of folders is, see my notes below.
Things to keep in mind:
/example
/ios
/android
/scripts
, for use by the iOS and/or Android native modules.XCFrameworks.zip
that we want to simply unzip into the root of the app, rather than bundle into it as-is)..
CONTRIBUTING.md
LICENSE
README.md
android # The React Native Android native module.
build.gradle
src
main
AndroidManifest.xml
java
com
reactnativenativescriptruntime
NativescriptRuntimeModule.java
NativescriptRuntimePackage.java
babel.config.js # The Babel config for all TSX?/JSX? sources excluding `/example`, which has its own Babel config.
example # The folder for the example app. Unlike the convention in NativeScript, it is source-controlled.
android # The Android example app. I haven't done much work on it yet.
app
build.gradle
gradle
gradle.properties
gradlew
gradlew.bat
nativescript.build.gradle
nativescript.buildscript.gradle
settings.gradle
app.json # The React Native example app's metadata (display name, etc). I think this is an Expo concept.
babel.config.js # The React Native example app's Babel config.
index.tsx # The React Native example app's entrypoint.
ios # The iOS example app.
File.swift
NativeScript # This is copied from the `ios` folder vended by the npm package.
NativeScript.xcframework # This is unzipped from the `ios/XCFrameworks.zip` folder vended by the npm package.
NativescriptRuntimeExample
NativescriptRuntimeExample-Bridging-Header.h
NativescriptRuntimeExample.xcodeproj
NativescriptRuntimeExample.xcworkspace
Podfile # The Podfile for the iOS app. Calls upon use_nativescript.rb.
Podfile.lock
Pods
TNSWidgets.xcframework # This is unzipped from the `ios/XCFrameworks.zip` folder vended by the npm package.
__MACOSX
internal # This is copied from @nativescript/ios.
use_nativescript.rb # The script to install NativeScript. Once finalised, I plan to move this logic into:
| # /react-native-nativescript-runtime.podspec, or;
| # /scripts/use_nativescript.rb.
metro.config.js # The bundler config for the React Native example app.
nativescript
node_modules
package-lock.json
package.json
src # The sources for the React Native example app.
| # I'm debating whether or not to nest the NativeScript sources in src/nativescript.
| # It's not clear what would be more intuitive in a React Native project.
ios # The React Native iOS native module.
NativeScript # Copied from @nativescript/capacitor. Originally from @nativescript/ui-mobile-base.
App-Bridging-Header.h
Runtime.h
Runtime.m
NativescriptRuntime.h # React Native iOS native module header. Not yet filled in.
NativescriptRuntime.m # React Native iOS native module implementation. Not yet filled in.
NativescriptRuntime.xcodeproj # React Native iOS native module xcodeproj.
XCFrameworks.zip # The JSC runtime, copied from @nativescript/capacitor.
# Unlike @nativescript/ios/framework/internal/XCFrameworks.zip found in @nativescript/[email protected],
# it lacks the TNSRuntime.h header (perhaps that's a JSC-only thing?).
# Oddly, in @nativescript/[email protected], there is no XCFrameworks.zip file at all!
# So it seems that this zip really was made bespoke for the Capacitor integration.
# It sounds like main reason we're using JSC rather than V8 is because this project is based on
# a camera project that nStudio did for a client in Ecuador.
# The good news is, this .zip is potentially stable indefinitely, as JSC is a slow-moving project.
package.json # When publishing this module to npm, it'll be from the root of the repo, using this.
react-native-nativescript-runtime.podspec # Specifies any native iOS files to be included in the CocoaPod.
scripts # Scripts to be used by both the npm package and (if necessary) the native modules.
bootstrap.js
build-nativescript.js
extractFrameworks.js
src # This is where you write the TypeScript API corresponding to your native module.
__tests__
index.tsx
tsconfig.build.json # This examines all TypeScript sources excluding the example app, `/example`.
tsconfig.json
Here is a tree of changes I've made, relative to a freshly initialised react-native-builder-bob plugin. Note that this is very approximate, as things are changing rapidly from commit to commmit and I may simply miss some things. But it should at least give a general orientation as to what's specific to this plugin rather than just being typical React Native plugin boilerplate.
use_nativescript.rb
iOS installer script, which is equivalent to the postinstall.ts
script in the NativeScript Capacitor repo).Beware that I've not actually committed the Android aspects yet, but eventually they'll be shaped like this.
.
CONTRIBUTING.md
LICENSE
README.md
android
build.gradle *
src
main
babel.config.js
example
android
app
build.gradle *
debug.keystore
proguard-rules.pro
src
main
AndroidManifest.xml *
build.gradle *
gradle
gradle.properties
gradlew
gradlew.bat
+ nativescript.build.gradle
+ nativescript.buildscript.gradle
settings.gradle
app.json
babel.config.js
index.tsx
ios
File.swift
+ NativeScript
+ NativeScript.xcframework
NativescriptRuntimeExample
NativescriptRuntimeExample-Bridging-Header.h
NativescriptRuntimeExample.xcodeproj *
NativescriptRuntimeExample.xcworkspace
Podfile *
Podfile.lock *
Pods
+ TNSWidgets.xcframework
+ __MACOSX
+ internal
+ use_nativescript.rb
metro.config.js
+ nativescript
+ build
+ package.json
+ references.d.ts
+ src
+ tsconfig.json
node_modules
package-lock.json *
package.json *
src
App.tsx
ios
+ NativeScript
+ App-Bridging-Header.h
+ Runtime.h
+ Runtime.m
NativescriptRuntime.h
NativescriptRuntime.m
NativescriptRuntime.xcodeproj
project.pbxproj
project.xcworkspace
+ XCFrameworks.zip
package.json
react-native-nativescript-runtime.podspec
scripts
bootstrap.js
+ build-nativescript.js
src
__tests__
index.test.tsx
index.tsx
tsconfig.build.json
tsconfig.json
Nothing working end-to-end yet; very much under construction.
embed
folder. Reasonably confident that I've integrated the build scripts correctly, but not so sure about all the other stuff.$rootDir
, $userDir/nsconfig.json
, etc., which may all make incorrect assumptions.@nativescript/android
instead.Auto-provisioning of the Xcode project is complete. Some steps remain manual for now.
AppDelegate.swift
AppDelegate.{m,swift}
(see the code blocks surrounded by // START NativeScript runtime
and // END NativeScript runtime
), or just give instructions.use_nativescript.rb
up from the app to the iOS native module. It's only really there at the moment so that I have a shorter path to type out when running it on the CLI.TNSWidgets
from @nativescript/core
rather than committing it to this repo.NativeScript.xcframework
can be taken from anywhere.@nativescript/ios
, both v6.x.x (JSC) and v8.x.x (V8), but it wasn't there. Seems it's bespoke to the Capacitor effort.Despite bundling XCFrameworks.zip
, we do still need to ensure that the root project's node_modules
holds @nativescript/ios
(whether as a direct dependency or as a subdependency of our npm package), because that's where our Cocoapod pulls the whole @nativescript/ios/framework/internal
folder from (which provides things like the nativescript-pre-build
script).
There's an argument that maybe we should just bundle the internal
folder into the native module as we're already doing so for XCFrameworks.zip
, but I prefer to re-use published packages wherever possible. Ideally at some point we'd be able to just cleanly source everything from @nativescript/ios
.
It's probably best for it to be the responsibility of the app itself to install @nativescript/ios
and/or @nativescript/android
, to save disk space for anyone working on a single-platform project.
Figure out how to make NativeScript bundle (see scripts/build-nativescript.js
).
Build an API for sending messages to the NativeScript runtime from the React Native JS context. i.e. make a React Native native module under /ios
and /android
, with reference to the Capacitor one.
I initialised a React Native plugin using react-native-builder-bob, selecting "Java & Objective-C" as the library type:
npx create-react-native-library react-native-nativescript-runtime
I ported the NativeScript Capacitor postinstall script to Ruby (see example/ios/use_nativescript.rb
), adjusting it for the structure of a React Native app: https://github.com/NativeScript/capacitor/blob/main/src/postinstall.ts
See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT