Skip to content

A collection of sample React Native Libraries that will show you how to use the New Architecture (Fabric & TurboModules) step-by-step.

License

Notifications You must be signed in to change notification settings

react-native-community/RNNewArchitectureLibraries

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 

Repository files navigation

RUN

This doc contains the logs of the steps done to achieve the final result.

Table of contents

Steps

  1. mkdir example-library
  2. touch example-library/package.json
  3. Paste the following code into the package.json file
{
    "name": "example-library",
    "version": "0.0.1",
    "description": "Showcase Turbomodule with backward compatibility",
    "react-native": "src/index",
    "source": "src/index",
    "files": [
        "src",
        "android",
        "ios",
        "example-library.podspec",
        "!android/build",
        "!ios/build",
        "!**/__tests__",
        "!**/__fixtures__",
        "!**/__mocks__"
    ],
    "keywords": ["react-native", "ios", "android"],
    "repository": "https://github.com/<your_github_handle>/example-library",
    "author": "<Your Name> <your_email@your_provider.com> (https://github.com/<your_github_handle>)",
    "license": "MIT",
    "bugs": {
        "url": "https://github.com/<your_github_handle>/example-library/issues"
    },
    "homepage": "https://github.com/<your_github_handle>/example-library#readme",
    "devDependencies": {},
    "peerDependencies": {
        "react": "*",
        "react-native": "*"
    }
}
  1. mkdir example-library/src
  2. touch example-library/src/index.js
  3. Paste the following content into the index.js
// @flow
import { NativeModules } from 'react-native'

export default NativeModules.Calculator;
  1. mkdir example-library/ios
  2. Open Xcode
  3. Create a new static library in the ios folder called RNCalculator. Keep Objective-C as language.
  4. Make sure that the Create Git repository on my mac option is unchecked
  5. Open finder and arrange the files and folder as shown below:
    example-library
    '-> ios
        '-> RNCalculator
            '-> RNCalculator.h
            '-> RNCalculator.m
        '-> RNCalculator.xcodeproj
    
    It is important that the RNCalculator.xcodeproj is a direct child of the example-library/ios folder.
  6. Open the RNCalculator.h file and update the code as it follows:
    - #import <Foundation/Foundation.h>
    + #import <React/RCTBridgeModule.h>
    
    + @interface RNCalculator : NSObject <RCTBridgeModule>
    
    @end
  7. Open the RNCalculator.m file and replace the code with the following:
    #import "RNCalculator.h"
    
    @implementation RNCalculator
    
    RCT_EXPORT_MODULE(Calculator)
    
    RCT_REMAP_METHOD(add, addA:(NSInteger)a
                            andB:(NSInteger)b
                    withResolver:(RCTPromiseResolveBlock) resolve
                    withRejecter:(RCTPromiseRejectBlock) reject)
    {
        NSNumber *result = [[NSNumber alloc] initWithInteger:a+b];
        resolve(result);
    }
    
    @end
  8. In the example-library folder, create a example-library.podspec file
  9. Copy this code in the podspec file
require "json"

package = JSON.parse(File.read(File.join(__dir__, "package.json")))

Pod::Spec.new do |s|
  s.name            = "example-library"
  s.version         = package["version"]
  s.summary         = package["description"]
  s.description     = package["description"]
  s.homepage        = package["homepage"]
  s.license         = package["license"]
  s.platforms       = { :ios => "11.0" }
  s.author          = package["author"]
  s.source          = { :git => package["repository"], :tag => "#{s.version}" }

  s.source_files    = "ios/**/*.{h,m,mm,swift}"

  s.dependency "React-Core"
end
  1. Create a folder example-library/android
  2. Create a file example-library/android/build.gradle and add this code:
    buildscript {
        ext.safeExtGet = {prop, fallback ->
            rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
        }
        repositories {
            google()
        gradlePluginPortal()
        }
        dependencies {
            classpath("com.android.tools.build:gradle:7.0.4")
        }
    }
    
    apply plugin: 'com.android.library'
    
    android {
        compileSdkVersion safeExtGet('compileSdkVersion', 31)
    
        defaultConfig {
            minSdkVersion safeExtGet('minSdkVersion', 21)
            targetSdkVersion safeExtGet('targetSdkVersion', 31)
        }
    }
    
    repositories {
        maven {
            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
            url "$projectDir/../node_modules/react-native/android"
        }
        mavenCentral()
        google()
    }
    
    dependencies {
        implementation 'com.facebook.react:react-native:+'
    }
  3. Create a file example-library/android/src/main/AndroidManifest.xml and add this code:
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.rnnewarchitecturelibrary">
    </manifest>
  4. Create a file example-library/android/src/main/java/com/rnnewarchitecturelibrary/CalculatorModule.java and add this code:
    package com.rnnewarchitecturelibrary;
    
    import com.facebook.react.bridge.NativeModule;
    import com.facebook.react.bridge.Promise;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.bridge.ReactContext;
    import com.facebook.react.bridge.ReactContextBaseJavaModule;
    import com.facebook.react.bridge.ReactMethod;
    import java.util.Map;
    import java.util.HashMap;
    
    public class CalculatorModule extends ReactContextBaseJavaModule {
        CalculatorModule(ReactApplicationContext context) {
            super(context);
        }
    
        @Override
        public String getName() {
            return "Calculator";
        }
    
        @ReactMethod
        public void add(int a, int b, Promise promise) {
            promise.resolve(a + b);
        }
    }
  5. Create a file example-library/android/src/main/java/com/rnnewarchitecturelibrary/CalculatorPackage.java and add this code:
    package com.rnnewarchitecturelibrary;
    
    import com.facebook.react.ReactPackage;
    import com.facebook.react.bridge.NativeModule;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.uimanager.ViewManager;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    public class CalculatorPackage implements ReactPackage {
    
    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
    
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new CalculatorModule(reactContext));
        return modules;
    }
    
    }
  1. At the same level of example-library run npx react-native init OldArchitecture
  2. cd OldArchitecture && yarn add ../example-library
  3. Open OldArchitecture/App.js file and replace the content with:
    /**
     * Sample React Native App
     * https://github.com/facebook/react-native
     *
     * @format
     * @flow strict-local
     */
    import React from 'react';
    import {useState} from "react";
    import type {Node} from 'react';
    import {
    SafeAreaView,
    StatusBar,
    Text,
    Button,
    } from 'react-native';
    import Calculator from 'example-library/src/index'
    const App: () => Node = () => {
    const [currentResult, setResult] = useState<number | null>(null);
    return (
        <SafeAreaView>
        <StatusBar barStyle={'dark-content'}/>
        <Text style={{marginLeft:20, marginTop:20}}>3+7={currentResult ?? "??"}</Text>
        <Button title="Compute" onPress={async () => {
            const result = await Calculator.add(3, 7);
            setResult(result);
        }} />
        </SafeAreaView>
    );
    };
    export default App;
  4. To run the App on iOS, install the dependencies: cd ios && pod install && cd ..
  5. npx react-native start (In another terminal, to run Metro)
  6. Run the app
    1. if using iOS: npx react-native run-ios
    2. if using Android: npx react-native run-android
  7. Click on the Compute button and see the app working

Note: OldArchitecture app has not been committed not to pollute the repository.

  1. touch example-library/src/NativeCalculator.js
  2. Paste the following code:
    // @flow
    import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
    import { TurboModuleRegistry } from 'react-native';
    
    export interface Spec extends TurboModule {
    // your module methods go here, for example:
    add(a: number, b: number): Promise<number>;
    }
    export default (TurboModuleRegistry.get<Spec>(
    'Calculator'
    ): ?Spec);
  1. Open the example-library/package.json
  2. Add the following snippet at the end of it:
    ,
    "codegenConfig": {
        "libraries": [
            {
            "name": "RNCalculatorSpec",
            "type": "modules",
            "jsSrcsDir": "src"
            }
        ]
    }
  1. Open the example-library/android/build.gradle file and update the code as follows:
    + def isNewArchitectureEnabled() {
    +    return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
    +}
    
    apply plugin: 'com.android.library'
    +if (isNewArchitectureEnabled()) {
    +    apply plugin: 'com.facebook.react'
    +}
    
    // ... other parts of the build file
    
    dependencies {
        implementation 'com.facebook.react:react-native:+'
    }
    
    + if (isNewArchitectureEnabled()) {
    +     react {
    +         jsRootDir = file("../src/")
    +         libraryName = "calculator"
    +         codegenJavaPackageName = "com.rnnewarchitecturelibrary"
    +     }
    + }
    
  1. Open the example-library/example-library.podspec file
  2. Before the Pod::Spec.new do |s| add the following code:
    folly_version = '2021.06.28.00-v2'
    folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
  3. Before the end tag, add the following code
    # This guard prevent to install the dependencies when we run `pod install` in the old architecture.
    if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
        s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
        s.pod_target_xcconfig    = {
            "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
            "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
        }
    
        s.dependency "React-Codegen"
        s.dependency "RCT-Folly", folly_version
        s.dependency "RCTRequired"
        s.dependency "RCTTypeSafety"
        s.dependency "ReactCommon/turbomodule/core"
    end
  1. In the ios/RNCalculator folder, rename the RNCalculator.m into RNCalculator.mm
  2. Open it and add the following import:
    // Thanks to this guard, we won't import this header when we build for the old architecture.
    #ifdef RCT_NEW_ARCH_ENABLED
    #import "RNCalculatorSpec.h"
    #endif
  3. Then, before the @end keyword, add the following code:
    // Thanks to this guard, we won't compile this code when we build for the old architecture.
    #ifdef RCT_NEW_ARCH_ENABLED
    - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
        (const facebook::react::ObjCTurboModule::InitParams &)params
    {
        return std::make_shared<facebook::react::NativeCalculatorSpecJSI>(params);
    }
    #endif
  1. Open the example-library/android/src/main/java/com/rnnewarchitecturelibrary/CalculatorModule.java and modify it as it follows:
    public class CalculatorModule extends ReactContextBaseJavaModule {
    
    +    public static final String NAME = "Calculator";
    
        CalculatorModule(ReactApplicationContext context) {
            super(context);
        }
    
        @Override
        public String getName() {
    -       return "Calculator";
    +       return NAME;
        }
  2. Open the example-library/android/src/main/java/com/rnnewarchitecturelibrary/CalculatorPackage.java and replace its content with:
    package com.rnnewarchitecturelibrary;
    
    import androidx.annotation.Nullable;
    import com.facebook.react.bridge.NativeModule;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.module.model.ReactModuleInfo;
    import com.facebook.react.module.model.ReactModuleInfoProvider;
    import com.facebook.react.TurboReactPackage;
    import com.facebook.react.uimanager.ViewManager;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.HashMap;
    import java.util.Map;
    
    public class CalculatorPackage extends TurboReactPackage {
    
        @Nullable
        @Override
        public NativeModule getModule(String name, ReactApplicationContext reactContext) {
            if (name.equals(CalculatorModule.NAME)) {
                return new CalculatorModule(reactContext);
            } else {
                return null;
            }
        }
    
        @Override
        public ReactModuleInfoProvider getReactModuleInfoProvider() {
            return () -> {
                final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();
                moduleInfos.put(
                        CalculatorModule.NAME,
                        new ReactModuleInfo(
                                CalculatorModule.NAME,
                                CalculatorModule.NAME,
                                false, // canOverrideExistingModule
                                false, // needsEagerInit
                                true, // hasConstants
                                false, // isCxxModule
                                false // isTurboModule
                ));
                return moduleInfos;
            };
        }
    }
  1. Open the example-library/android/build.gradle file and update the code as it follows:
        defaultConfig {
            minSdkVersion safeExtGet('minSdkVersion', 21)
            targetSdkVersion safeExtGet('targetSdkVersion', 31)
    +        buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
    +    }
    +
    +    sourceSets {
    +        main {
    +            if (isNewArchitectureEnabled()) {
    +                java.srcDirs += ['src/newarch']
    +            } else {
    +                java.srcDirs += ['src/oldarch']
    +            }
    +        }
        }
    }
  2. Open the example-library/android/src/main/java/com/rnnewarchitecturelibrary/CalculatorPackage.java and update the getReactModuleInfoProvider function as it follows:
    public ReactModuleInfoProvider getReactModuleInfoProvider() {
        return () -> {
            final Map<String, ReactModuleInfo> moduleInfos = new HashMap<>();
    +       boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
            moduleInfos.put(
                    CalculatorModule.NAME,
                    new ReactModuleInfo(
                            CalculatorModule.NAME,
                            CalculatorModule.NAME,
                            false, // canOverrideExistingModule
                            false, // needsEagerInit
                            true, // hasConstants
                            false, // isCxxModule
    -                       false, // isTurboModule
    +                       isTurboModule // isTurboModule
            ));
            return moduleInfos;
        };
  3. Create a file example-library/android/src/newarch/java/com/rnnewarchitecturelibrary/CalculatorModule.java (notice the newarch child of the src folder) and paste the following code:
    package com.rnnewarchitecturelibrary;
    
    import androidx.annotation.NonNull;
    import com.facebook.react.bridge.NativeModule;
    import com.facebook.react.bridge.Promise;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.bridge.ReactContext;
    import com.facebook.react.bridge.ReactContextBaseJavaModule;
    import com.facebook.react.bridge.ReactMethod;
    import java.util.Map;
    import java.util.HashMap;
    
    public class CalculatorModule extends NativeCalculatorSpec {
    
        public static final String NAME = "Calculator";
    
        CalculatorModule(ReactApplicationContext context) {
            super(context);
        }
    
        @Override
        @NonNull
        public String getName() {
            return NAME;
        }
    
        @Override
        public void add(double a, double b, Promise promise) {
            promise.resolve(a + b);
        }
    }
  4. Create a file example-library/android/src/oldarch/java/com/rnnewarchitecturelibrary/CalculatorModule.java (notice the oldarch child of the src folder) and paste the following code:
    package com.rnnewarchitecturelibrary;
    
    import com.facebook.react.bridge.NativeModule;
    import com.facebook.react.bridge.Promise;
    import com.facebook.react.bridge.ReactApplicationContext;
    import com.facebook.react.bridge.ReactContext;
    import com.facebook.react.bridge.ReactContextBaseJavaModule;
    import com.facebook.react.bridge.ReactMethod;
    import java.util.Map;
    import java.util.HashMap;
    
    public class CalculatorModule extends ReactContextBaseJavaModule {
    
        public static final String NAME = "Calculator";
    
        CalculatorModule(ReactApplicationContext context) {
            super(context);
        }
    
        @Override
        public String getName() {
            return NAME;
        }
    
        @ReactMethod
        public void add(int a, int b, Promise promise) {
            promise.resolve(a + b);
        }
    }
  1. Create a new example-library/android/src/main/java/com/rnnewarchitecturelibrary/CalculatorModuleImpl.java file (notice that the src's subfolder is now main) and paste the following code:
    package com.rnnewarchitecturelibrary;
    
    import androidx.annotation.NonNull;
    import com.facebook.react.bridge.Promise;
    import java.util.Map;
    import java.util.HashMap;
    
    public class CalculatorModuleImpl {
    
        public static final String NAME = "Calculator";
    
        public static void add(double a, double b, Promise promise) {
            promise.resolve(a + b);
        }
    
    }
  2. Open the example-library/android/src/main/java/com/rnnewarchitecturelibrary/CalculatorPackage.java file and replace all the instances of CalculatorModule with CalculatorModuleImpl
  3. Open the example-library/android/src/newarch/java/com/rnnewarchitecturelibrary/CalculatorModule.java file and update it as it follows:
    public class CalculatorModule extends NativeCalculatorSpec {
    
    -    public static final String NAME = "Calculator";
    
        CalculatorModule(ReactApplicationContext context) {
            super(context);
        }
    
        @Override
        @NonNull
        public String getName() {
    -        return NAME;
    +        return CalculatorModuleImpl.NAME;
        }
    
        @Override
        public void add(double a, double b, Promise promise) {
    -        promise.resolve(a + b);
    +        CalculatorModuleImpl.add(a, b, promise);
        }
    }
  4. Open the example-library/android/src/oldarch/java/com/rnnewarchitecturelibrary/CalculatorModule.java and update it as it follows:
    public class CalculatorModule extends ReactContextBaseJavaModule {
    
    -    public static final String NAME = "Calculator";
    
        CalculatorModule(ReactApplicationContext context) {
            super(context);
        }
    
        @Override
        public String getName() {
    -        return NAME;
    +        return CalculatorModuleImpl.NAME;
        }
    
        @ReactMethod
        public void add(int a, int b, Promise promise) {
    -        promise.resolve(a + b);
    +        CalculatorModuleImpl.add(a, b, promise);
        }
    }
  1. Open the src/index.js file
  2. Replace the code with the following:
    // @flow
    import { NativeModules } from 'react-native'
    
    const isTurboModuleEnabled = global.__turboModuleProxy != null;
    
    const calculator = isTurboModuleEnabled ?
    require("./NativeCalculator").default :
    NativeModules.Calculator;
    
    export default calculator;
  1. At the same level of example-library run npx react-native init NewArchitecture --version next (next takes the next version that is about to be released. Any version >= 0.68 should work)
  2. cd NewArchitecture && yarn add ../example-library
  3. Open NewArchitecture/App.js file and replace the content with the same file used for the OldArchitecture.
  4. To run the App on iOS, install the dependencies: cd ios && pod install && cd ..
  5. npx react-native start (In another terminal, to run Metro)
  6. Run the app:
    1. iOS: npx react-native run-ios
    2. Android npx react-native run-android
  7. Click on the Compute button and see the app working

Note: NewArchitecture app has not been committed not to pollute the repository.

Android Autolinking

Autolinking doesn’t work with the new architecture out of the box. Therefore you need to ask the user of your library to do the following steps:

  1. Open the NewArchitecture/android/app/build.gradle file and update the file as it follows:
        "PROJECT_BUILD_DIR=$buildDir",
        "REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
    -   "REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build"
    +   "REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
    +   "NODE_MODULES_DIR=$rootDir/../node_modules/"
        cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
        cppFlags "-std=c++17"
  2. Open the NewArchitecture/android/app/src/main/jni/Android.mk file and update the file as it follows:
        # If you wish to add a custom TurboModule or Fabric component in your app you
        # will have to include the following autogenerated makefile.
        # include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk
    +
    +   # Includes the MK file for `example-library`
    +
    +   include $(NODE_MODULES_DIR)/example-library/android/build/generated/source/codegen/jni/Android.mk
        include $(CLEAR_VARS)
  3. In the same file above, go to the LOCAL_SHARED_LIBRARIES setting and add the following line:
        libreact_codegen_rncore \
    +   libreact_codegen_calculator \
        libreact_debug \
  4. Open the NewArchitecture/android/app/src/main/jni/MainApplicationModuleProvider.cpp file and update the file as it follows:
    1. Add the import for the calculator:
          #include <answersolver.h>
      +   #include <calculator.h>
    2. Add the following check in the MainApplicationModuleProvider constructor:
          // auto module = samplelibrary_ModuleProvider(moduleName, params);
          // if (module != nullptr) {
          //    return module;
          // }
      
      +    auto module = calculator_ModuleProvider(moduleName, params);
      +    if (module != nullptr) {
      +        return module;
      +    }
      
          return rncore_ModuleProvider(moduleName, params);
      }

About

A collection of sample React Native Libraries that will show you how to use the New Architecture (Fabric & TurboModules) step-by-step.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published