From ddddffd0a0f1d2f6d147062ec7b3894ed0a8eaf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20M=C4=99drek?= Date: Sun, 24 Mar 2024 20:32:23 +0100 Subject: [PATCH] feat(#38): make module TurboModule-compatible - create JS Codegen spec - use install_modules_dependencies util in podspec if possible, to install React deps - migrate Android module class to conform to codegenerated spec when new arch enabled - copy codegen-ed to oldarch Android source set to make module work when new arch disabled - migrate iOS module class to conform to codegenerated spec when new arch enabled --- android/build.gradle | 17 +++++++ .../org/linusu/RNGetRandomValuesModule.java | 21 ++------ .../org/linusu/RNGetRandomValuesPackage.java | 48 ++++++++++++------- .../linusu/NativeRNGetRandomValuesSpec.java | 25 ++++++++++ index.js | 6 ++- ios/RNGetRandomValues.h | 2 +- ios/RNGetRandomValues.m | 20 -------- ios/RNGetRandomValues.mm | 29 +++++++++++ package.json | 12 ++++- react-native-get-random-values.podspec | 9 ++-- specs/NativeRNGetRandomValues.js | 16 +++++++ 11 files changed, 144 insertions(+), 61 deletions(-) create mode 100644 android/src/oldarch/java/org/linusu/NativeRNGetRandomValuesSpec.java delete mode 100644 ios/RNGetRandomValues.m create mode 100644 ios/RNGetRandomValues.mm create mode 100644 specs/NativeRNGetRandomValues.js diff --git a/android/build.gradle b/android/build.gradle index 077d5be..e71585a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -15,7 +15,14 @@ buildscript { } } +def isNewArchitectureEnabled() { + return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" +} + apply plugin: 'com.android.library' +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} def safeExtGet(prop, fallback) { rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback @@ -32,12 +39,22 @@ android { defaultConfig { minSdkVersion safeExtGet('minSdkVersion', 16) targetSdkVersion safeExtGet('targetSdkVersion', 30) + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() versionCode 1 versionName "1.0" } lintOptions { abortOnError false } + sourceSets { + main { + if (isNewArchitectureEnabled()) { + java.srcDirs += ['src/newarch/java', "${project.buildDir}/generated/source/codegen/java"] + } else { + java.srcDirs += ['src/oldarch/java'] + } + } + } } repositories { diff --git a/android/src/main/java/org/linusu/RNGetRandomValuesModule.java b/android/src/main/java/org/linusu/RNGetRandomValuesModule.java index 37c728d..2397110 100644 --- a/android/src/main/java/org/linusu/RNGetRandomValuesModule.java +++ b/android/src/main/java/org/linusu/RNGetRandomValuesModule.java @@ -1,32 +1,19 @@ package org.linusu; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - import android.util.Base64; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.Callback; - -public class RNGetRandomValuesModule extends ReactContextBaseJavaModule { - private final ReactApplicationContext reactContext; +import java.security.SecureRandom; +public class RNGetRandomValuesModule extends NativeRNGetRandomValuesSpec { public RNGetRandomValuesModule(ReactApplicationContext reactContext) { super(reactContext); - this.reactContext = reactContext; } @Override - public String getName() { - return "RNGetRandomValues"; - } - - @ReactMethod(isBlockingSynchronousMethod = true) - public String getRandomBase64(int byteLength) throws NoSuchAlgorithmException { - byte[] data = new byte[byteLength]; + public String getRandomBase64(double byteLength) { + byte[] data = new byte[(int)byteLength]; SecureRandom random = new SecureRandom(); random.nextBytes(data); diff --git a/android/src/main/java/org/linusu/RNGetRandomValuesPackage.java b/android/src/main/java/org/linusu/RNGetRandomValuesPackage.java index b5703b2..6799478 100644 --- a/android/src/main/java/org/linusu/RNGetRandomValuesPackage.java +++ b/android/src/main/java/org/linusu/RNGetRandomValuesPackage.java @@ -1,28 +1,44 @@ package org.linusu; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; -import com.facebook.react.ReactPackage; +import com.facebook.react.TurboReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.uimanager.ViewManager; -import com.facebook.react.bridge.JavaScriptModule; +import com.facebook.react.module.model.ReactModuleInfo; +import com.facebook.react.module.model.ReactModuleInfoProvider; -public class RNGetRandomValuesPackage implements ReactPackage { - @Override - public List createNativeModules(ReactApplicationContext reactContext) { - return Arrays.asList(new RNGetRandomValuesModule(reactContext)); - } +import java.util.HashMap; +import java.util.Map; - // Deprecated from RN 0.47 - public List> createJSModules() { - return Collections.emptyList(); +public class RNGetRandomValuesPackage extends TurboReactPackage { + @Nullable + @Override + public NativeModule getModule(@NonNull String name, @NonNull ReactApplicationContext reactContext) { + if (name.equals(RNGetRandomValuesModule.NAME)) { + return new RNGetRandomValuesModule(reactContext); + } + return null; } @Override - public List createViewManagers(ReactApplicationContext reactContext) { - return Collections.emptyList(); + public ReactModuleInfoProvider getReactModuleInfoProvider() { + return () -> { + boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; + final Map moduleInfos = new HashMap<>(); + moduleInfos.put( + RNGetRandomValuesModule.NAME, + new ReactModuleInfo( + RNGetRandomValuesModule.NAME, + RNGetRandomValuesModule.NAME, + false, // canOverrideExistingModule + false, // needsEagerInit + false, // hasConstants + false, // isCxxModule + isTurboModule // isTurboModule + )); + return moduleInfos; + }; } } diff --git a/android/src/oldarch/java/org/linusu/NativeRNGetRandomValuesSpec.java b/android/src/oldarch/java/org/linusu/NativeRNGetRandomValuesSpec.java new file mode 100644 index 0000000..957bb0c --- /dev/null +++ b/android/src/oldarch/java/org/linusu/NativeRNGetRandomValuesSpec.java @@ -0,0 +1,25 @@ +package org.linusu; + +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; +import javax.annotation.Nonnull; + +public abstract class NativeRNGetRandomValuesSpec extends ReactContextBaseJavaModule implements TurboModule { + public static final String NAME = "RNGetRandomValues"; + + public NativeRNGetRandomValuesSpec(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + public @Nonnull String getName() { + return NAME; + } + + @ReactMethod(isBlockingSynchronousMethod = true) + @DoNotStrip + public abstract String getRandomBase64(double byteLength); +} diff --git a/index.js b/index.js index e143bfe..d0ba77a 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,8 @@ const base64Decode = require('fast-base64-decode') const { NativeModules } = require('react-native') +const NativeRNGetRandomValues = require('./specs/NativeRNGetRandomValues').default + class TypeMismatchError extends Error {} class QuotaExceededError extends Error {} @@ -24,8 +26,8 @@ function insecureRandomValues (array) { * @returns {string} */ function getRandomBase64 (byteLength) { - if (NativeModules.RNGetRandomValues) { - return NativeModules.RNGetRandomValues.getRandomBase64(byteLength) + if (NativeRNGetRandomValues) { + return NativeRNGetRandomValues.getRandomBase64(byteLength) } else if (NativeModules.ExpoRandom) { // Expo SDK 41-44 return NativeModules.ExpoRandom.getRandomBase64String(byteLength) diff --git a/ios/RNGetRandomValues.h b/ios/RNGetRandomValues.h index b88e04a..00a68d7 100644 --- a/ios/RNGetRandomValues.h +++ b/ios/RNGetRandomValues.h @@ -1,5 +1,5 @@ #import @interface RNGetRandomValues : NSObject --(NSString*)getRandomBase64:(NSUInteger)byteLength; + @end diff --git a/ios/RNGetRandomValues.m b/ios/RNGetRandomValues.m deleted file mode 100644 index 4eaa540..0000000 --- a/ios/RNGetRandomValues.m +++ /dev/null @@ -1,20 +0,0 @@ -#import "RNGetRandomValues.h" - -@implementation RNGetRandomValues - -- (dispatch_queue_t)methodQueue -{ - return dispatch_get_main_queue(); -} -RCT_EXPORT_MODULE() - -RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSString*, getRandomBase64:(NSUInteger)byteLength) { - NSMutableData *data = [NSMutableData dataWithLength:byteLength]; - int result = SecRandomCopyBytes(kSecRandomDefault, byteLength, data.mutableBytes); - if (result != errSecSuccess) { - @throw([NSException exceptionWithName:@"NO_RANDOM_BYTES" reason:@"Failed to aquire secure random bytes" userInfo:nil]); - } - return [data base64EncodedStringWithOptions:0]; -} - -@end diff --git a/ios/RNGetRandomValues.mm b/ios/RNGetRandomValues.mm new file mode 100644 index 0000000..4bf7ce2 --- /dev/null +++ b/ios/RNGetRandomValues.mm @@ -0,0 +1,29 @@ +#import "RNGetRandomValues.h" + +#if RCT_NEW_ARCH_ENABLED +#import "RNGetRandomValuesSpec.h" + +@interface RNGetRandomValues () +@end +#endif + +@implementation RNGetRandomValues + +RCT_EXPORT_MODULE() + +RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSString*, getRandomBase64:(double)byteLength) { + NSMutableData *data = [NSMutableData dataWithLength:(NSUInteger)byteLength]; + int result = SecRandomCopyBytes(kSecRandomDefault, (NSUInteger)byteLength, data.mutableBytes); + if (result != errSecSuccess) { + @throw([NSException exceptionWithName:@"NO_RANDOM_BYTES" reason:@"Failed to aquire secure random bytes" userInfo:nil]); + } + return [data base64EncodedStringWithOptions:0]; +} + +#if RCT_NEW_ARCH_ENABLED +- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { + return std::make_shared(params); +} +#endif + +@end diff --git a/package.json b/package.json index 72c35d0..599c6f0 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "fast-base64-decode": "^1.0.0" }, "peerDependencies": { - "react-native": ">=0.56" + "react-native": ">=0.71" }, "keywords": [ "Crypto.getRandomValues", @@ -16,5 +16,13 @@ "getRandomValues", "polyfill", "react-native" - ] + ], + "codegenConfig": { + "name": "RNGetRandomValuesSpec", + "type": "modules", + "jsSrcsDir": "specs", + "android": { + "javaPackageName": "org.linusu" + } + } } diff --git a/react-native-get-random-values.podspec b/react-native-get-random-values.podspec index f7075eb..64c34fc 100644 --- a/react-native-get-random-values.podspec +++ b/react-native-get-random-values.podspec @@ -13,8 +13,11 @@ Pod::Spec.new do |s| s.platforms = { :ios => "9.0", :tvos => "9.0", :osx => "10.14" } s.source = { :git => "https://github.com/LinusU/react-native-get-random-values.git", :tag => "v#{s.version}" } - s.source_files = "ios/**/*.{h,m,swift}" - s.requires_arc = true + s.source_files = "ios/**/*.{h,m,mm,swift}" - s.dependency "React-Core" + if respond_to?(:install_modules_dependencies, true) + install_modules_dependencies(s) + else + s.dependency "React-Core" + end end diff --git a/specs/NativeRNGetRandomValues.js b/specs/NativeRNGetRandomValues.js new file mode 100644 index 0000000..3b4a5eb --- /dev/null +++ b/specs/NativeRNGetRandomValues.js @@ -0,0 +1,16 @@ +/** + * @flow strict-local + * @format + */ + +import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport'; +import type { Int32 } from 'react-native/Libraries/Types/CodegenTypes'; +import { TurboModuleRegistry } from 'react-native'; + +export interface Spec extends TurboModule { + +getRandomBase64: (byteLength: Int32) => string; +} + +export default (TurboModuleRegistry.get( + "RNGetRandomValues" +): Spec);