Skip to content

Latest commit

 

History

History
330 lines (263 loc) · 9.08 KB

File metadata and controls

330 lines (263 loc) · 9.08 KB
toc_max_heading_level 3

import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';

Nitrogen

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 optional

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.

Who uses Nitrogen?

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.

Configuration

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.

Usage

Nitrogen parses all TypeScript files that end in .nitro.ts.

1. Write TypeScript specs

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
}

2. Generate native specs

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. :::

3. Add generated sources to your library

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.

iOS

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)
end

Android

On 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. :::

4. Implement the Hybrid Objects

To implement Math now, you just need to implement the spec:

```swift title="HybridMath.swift" class HybridMath : HybridMathSpec { public func add(a: Double, b: Double) throws -> Double { return a + b } } ``` ```kotlin title="HybridMath.kt" class HybridMath : HybridMathSpec() { override fun add(a: Double, b: Double): Double { return a + b } } ```
```cpp title="HybridMath.hpp" class HybridMath: public HybridMathSpec { public: HybridMath(): HybridObject(TAG) {} public: double add(double a, double b) override; }; ```
```cpp title="HybridMath.cpp" double HybridMath::add(double a, double b) { return a + b; } ```

5. Register the Hybrid Objects

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.

5.1. Initialize Android (C++)

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();
    }
  }
}

5.2. (Optional) ProGuard

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.

6. Initialize the Hybrid Objects

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)