GitHub - react-native-community/RNNewArchitectureLibraries at feat/back-turbomodule-070 (original) (raw)

Table of contents

Steps

[Setup] Create the calculator folder and the package.json

  1. mkdir calculator
  2. touch calculator/package.json
  3. Paste the following code into the package.json file

{ "name": "calculator", "version": "0.0.1", "description": "Showcase Turbomodule with backward compatibility", "react-native": "src/index", "source": "src/index", "files": [ "src", "android", "ios", "calculator.podspec", "!android/build", "!ios/build", "!/tests", "!/fixtures", "!**/mocks" ], "keywords": ["react-native", "ios", "android"], "repository": "https://github.com//calculator", "author": " <your_email@your_provider.com> (https://github.com/)", "license": "MIT", "bugs": { "url": "https://github.com//calculator/issues" }, "homepage": "https://github.com//calculator#readme", "devDependencies": {}, "peerDependencies": { "react": "", "react-native": "" } }

[Native Module] Create the JS import

  1. mkdir calculator/src
  2. touch calculator/src/index.js
  3. Paste the following content into the index.js

// @flow import { NativeModules } from 'react-native'

export default NativeModules.Calculator;

[Native Module] Create the iOS implementation

  1. mkdir calculator/ios
  2. Create an ios/RNCalculator.h file and fill it with the following code:

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
@interface RNCalculator : NSObject
@end 3. Create an ios/RNCalculator.m file and replace the code with the following:
#import "RNCalculator.h"
@implementation RNCalculator
RCT_EXPORT_MODULE()
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 4. In the calculator folder, create a calculator.podspec file 5. 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 = "calculator" 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

[Native Module] Create the Android implementation

  1. Create a folder calculator/android
  2. Create a file calculator/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 calculator/android/src/main/AndroidManifest.xml and add this code:
4. Create a file `calculator/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 "RNCalculator"; } @ReactMethod public void add(int a, int b, Promise promise) { promise.resolve(a + b); } } 5. Create a file `calculator/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 createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List createNativeModules(ReactApplicationContext reactContext) { List modules = new ArrayList<>(); modules.add(new CalculatorModule(reactContext)); return modules; } }

[Native Module] Test The Native Module

  1. At the same level of calculator run npx react-native init OldArchitecture --version 0.70.0-rc.2
  2. cd OldArchitecture && yarn add ../calculator
  3. Open OldArchitecture/App.js file and replace the content with:
    /**
  1. To run the App on iOS, install the dependencies: cd ios && bundle install && bundle exec pod install && cd ..
  2. Run the app
    1. if using iOS: npx react-native run-ios
    2. if using Android: npx react-native run-android
  3. Click on the Compute button and see the app working

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

[TurboModule] Add the JavaScript specs

  1. touch calculator/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;
    }
    export default (TurboModuleRegistry.get(
    'RNCalculator'
    ): ?Spec);

[TurboModule] Set up CodeGen

  1. Open the calculator/package.json
  2. Add the following snippet at the end of it:
    ,
    "codegenConfig": {
    "name": "RNCalculatorSpec",
    "type": "modules",
    "jsSrcsDir": "src",
    "android": {
    "javaPackageName": "com.rnnewarchitecturelibrary"
    }
    }

[TurboModule] Set up build.gradle

  1. Open the calculator/android/build.gradle file and update the code as follows:

+}
apply plugin: 'com.android.library'
+if (isNewArchitectureEnabled()) {

+}
// ... other parts of the build file
dependencies {
implementation 'com.facebook.react:react-native:+'
}

[TurboModule] Set up podspec file

  1. Open the calculator/calculator.podspec file
  2. Before the Pod::Spec.new do |s| add the following code:
    folly_version = '2021.07.22.00'
    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

[TurboModule] Update the Native iOS code

  1. In the ios/RNCalculator folder, rename the RNCalculator.m into RNCalculator.mm
  2. Open it and replace its content with:

#import "RNCalculator.h"
// 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
@implementation RNCalculator
RCT_EXPORT_MODULE()
RCT_REMAP_METHOD(add, addA:(NSInteger)a
andB:(NSInteger)b
withResolver:(RCTPromiseResolveBlock) resolve
withRejecter:(RCTPromiseRejectBlock) reject)
{
return [self add:a b:b resolve:resolve reject:reject];
}
// Thanks to this guard, we won't compile this code when we build for the old architecture.
#ifdef RCT_NEW_ARCH_ENABLED

#endif

  1. Open the ios/RNCalculator.h file and replace its content with:

#import <Foundation/Foundation.h>
#ifdef RCT_NEW_ARCH_ENABLED
#import <RNCalculatorSpec/RNCalculatorSpec.h>
@interface RNCalculator: NSObject
#else
#import <React/RCTBridgeModule.h>
@interface RNCalculator : NSObject
#endif
@end

[TurboModule] Android: Convert ReactPackage to a backward compatible TurboReactPackage

  1. Open the calculator/android/src/main/java/com/rnnewarchitecturelibrary/CalculatorModule.java and modify it as it follows:
    public class CalculatorModule extends ReactContextBaseJavaModule {
CalculatorModule(ReactApplicationContext context) {  
    super(context);  
}  
@Override  
public String getName() {  
  1. Open the calculator/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;
    };
    }
    }

[TurboModule] Android: Update the Native code to use two sourcesets

  1. Open the calculator/android/build.gradle file and update the code as it follows:
    defaultConfig {
    minSdkVersion safeExtGet('minSdkVersion', 21)
    targetSdkVersion safeExtGet('targetSdkVersion', 31)

} 2. Open the calculator/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<>();

  1. Create a file calculator/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 = "RNCalculator";
    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);
    }
    }
  2. Create a file calculator/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 = "RNCalculator";
    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);
    }
    }

[TurboModule] Android: Refactor the code to use a shared implementation

  1. Create a new calculator/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 = "RNCalculator";
    public static void add(double a, double b, Promise promise) {
    promise.resolve(a + b);
    }
    }
  2. Open the calculator/android/src/main/java/com/rnnewarchitecturelibrary/CalculatorPackage.java file and update the following lines:
    public NativeModule getModule(String name, ReactApplicationContext reactContext) {
  1. Open the calculator/android/src/newarch/java/com/rnnewarchitecturelibrary/CalculatorModule.java file and update it as it follows:
    public class CalculatorModule extends NativeCalculatorSpec {
CalculatorModule(ReactApplicationContext context) {  
    super(context);  
}  
@Override  
@NonNull  
public String getName() {  
  1. Open the calculator/android/src/oldarch/java/com/rnnewarchitecturelibrary/CalculatorModule.java and update it as it follows:
    public class CalculatorModule extends ReactContextBaseJavaModule {
CalculatorModule(ReactApplicationContext context) {  
    super(context);  
}  
@Override  
public String getName() {  
  1. Remove the android/src/main/java/com/rnnewarchitecturelibrary/CalculatorModule.java (the one in the main folder).

[TurboModule] Unify JavaScript interface

  1. Open the src/index.js file
  2. Replace the code with the following:
    // @flow
    export default require("./NativeCalculator").default;

[TurboModule] Test the Turbomodule

  1. At the same level of calculator run npx react-native init NewArchitecture --version 0.70.0-rc.2
  2. cd NewArchitecture && yarn add ../calculator
  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 && bundle install && RCT_NEW_ARCH_ENABLED=1 bundle exec pod install && cd ..
  5. Open the NewArchitecture/android/gradle.properties and update them as it follows:
  1. Run the app:
    1. iOS: npx react-native run-ios
    2. Android npx react-native run-android
  2. Click on the Compute button and see the app working

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