| toc_max_heading_level | 3 |
|---|
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
Nitrogen is Nitro's code-generator. It parses TypeScript code using an AST parser to generate native interfaces from TypeScript definitions.
interface Math extends HybridObject {
readonly pi: number
add(a: number, b: number): number
}protocol HybridMathSpec: HybridObject {
var pi: Double { get }
func add(a: Double, b: Double) -> Double
}When HybridMathSpec is not implemented properly on the native side (e.g. if add(..) is missing, or if a type is incorrect), the app will not compile, which ensures full type-safety and null-safety at compile-time.
Nitrogen is a fully optional CLI that does some of the work for you.
You can also build Nitro Modules and create Hybrid Objects without nitrogen, by just calling the registerHybrids method yourself.
Nitrogen should be used by library-authors, and generated specs should be committed to the repository/package.
If you build an app that uses libraries built with Nitro, you do not need to run nitrogen yourself.
Nitrogen should be installed as a dev-dependency in the Nitro Module (library).
```sh npm i nitrogen --save-dev ``` ```sh yarn add nitrogen -D ``` ```sh pnpm add nitrogen -D ``` ```sh bun i nitrogen -d ```Each Nitro Module needs to have a nitro.json configuration file.
Create a nitro.json file in the root directory of your Nitro Module (next to package.json), and add the following content:
{
"$schema": "https://nitro.margelo.com/nitro.schema.json",
"cxxNamespace": ["math"],
"ios": {
"iosModuleName": "NitroMath"
},
"android": {
"androidNamespace": ["math"],
"androidCxxLibName": "NitroMath"
},
"autolinking": {}
}Tweak your module name and namespaces as needed.
Nitrogen parses all TypeScript files that end in .nitro.ts.
For example, let's create Math.nitro.ts:
import { type HybridObject } from 'react-native-nitro-modules'
interface Math extends HybridObject<{ ios: 'swift', android: 'kotlin' }> {
add(a: number, b: number): number
}Now run nitrogen:
```sh npx nitrogen ``` ```sh yarn nitrogen ``` ```sh pnpm nitrogen ``` ```sh bun nitrogen ```This will always generate a shared C++ interface, and then optionall also Swift and Kotlin sub-classes. The specs go into ./nitrogen/generated/:
🔧 Loading nitro.json config...
🚀 Nitrogen runs at ~/Projects/nitro/example/dummy
🔍 Nitrogen found 1 spec in ~/Projects/nitro/example/dummy
⏳ Parsing Math.nitro.ts...
⚙️ Generating specs for HybridObject "Math"...
shared: Generating C++ code...
⛓️ Setting up build configs for autolinking...
🎉 Generated 1/1 HybridObject in 0.6s!
💡 Your code is in ./nitrogen/generated
‼️ Added 8 files - you need to run `pod install`/sync gradle to update files!
:::important
You should push the files in ./nitrogen/generated/ to git, and make sure those files are part of your npm package.
This way your library will always ship a working package as a whole (including generated interfaces), and the user does not need to do anything else than to install your package.
:::
All the generated sources (./nitrogen/generated/) need to be part of your library's code - so we need to add it to the iOS/Android build files.
If you created a library using the Nitro Module template, your library already includes nitrogen's generated sources.
On iOS, you need to call add_nitrogen_files(...) from your library's .podspec. Put this at the very end of your spec declaration:
Pod::Spec.new do |s|
# ...
load 'nitrogen/generated/ios/NitroExample+autolinking.rb'
add_nitrogen_files(s)
endOn Android, you first need to add the autogenerated Java/Kotlin sources to your build.gradle. Put this at the top of your build.gradle, right after any other apply calls:
apply from: '../nitrogen/generated/android/NitroExample+autolinking.gradle'Then, add the autogenerated C++ sources to your CMakeLists.txt. Put this somewhere after add_library(...):
include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroExample+autolinking.cmake):::tip
Replace NitroExample with your Nitro Module's name as defined in your nitro.json.
:::
To implement Math now, you just need to implement the spec:
Nitro needs to be able to initialize an instance of your Hybrid Object - so we need to tell it how to do that.
In your nitro.json, register HybridMath in the "autolinking" section:
json { ... "autolinking": { "Math": { "swift": "HybridMath", "kotlin": "HybridMath" } } }
json { ... "autolinking": { "Math": { "cpp": "HybridMath" } } }
Make sure HybridMath is default-constructible and scoped inside the correct namespace/package/file, then run Nitrogen.
If you created a library using the Nitro Module template, your library already initializes the C++ autolinking process from your *Package.kt and OnLoad.cpp files.
In your JNI OnLoad function (OnLoad.cpp or cpp-adapter.cpp), initialize your module:
#include <jni.h>
#include "NitroMathOnLoad.hpp"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
return margelo::nitro::math::initialize(vm);
}Then, to actually load and initialize the C++ part of your library (which calls JNI_OnLoad from above), call initializeNative() from your library's entry point (*Package.kt):
public class NitroMathPackage: TurboReactPackage() {
// ...
companion object {
init {
NitroMathOnLoad.initializeNative();
}
}
}If you are using ProGuard on Android, make sure to add a @DoNotStrip annotation above your HybridMath class (and constructor) so Nitro can construct it from C++ in release builds.
And finally, to initialize HybridMath from JS you just need to call createHybridObject:
export const MathModule = NitroModules.createHybridObject<Math>("Math")
const result = MathModule.add(5, 7)