From 86a7e9021c99d5f96c5a67ae7b4692f48874c240 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Mon, 3 Jul 2023 07:44:52 +0200 Subject: [PATCH] feat: add TurboModule support (#910) Co-authored-by: Shawn Dempsey Co-authored-by: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Co-authored-by: Shawn Dempsey --- .github/workflows/ci.yml | 4 +- RNCAsyncStorage.podspec | 22 +++- android/build.gradle | 39 ++++++- .../asyncstorage/AsyncStoragePackage.java | 105 ++++++++++++++++++ .../asyncstorage/AsyncStoragePackage.kt | 58 ++++++++++ .../asyncstorage/AsyncStorageModule.java | 10 +- .../asyncstorage/AsyncStoragePackage.java | 58 ---------- .../asyncstorage/next/StorageModule.kt | 24 ++-- .../NativeAsyncStorageModuleSpec.java | 47 ++++++++ example/android/build.gradle | 8 ++ example/android/gradle.properties | 4 +- .../contents.xcworkspacedata | 3 + example/macos/Podfile | 6 + ios/RNCAsyncStorage.h | 12 +- ios/{RNCAsyncStorage.m => RNCAsyncStorage.mm} | 8 ++ package.json | 13 ++- react-native.config.js | 5 +- src/NativeAsyncStorageModule.ts | 26 +++++ yarn.lock | 9 +- 19 files changed, 375 insertions(+), 86 deletions(-) create mode 100644 android/src/javaPackage/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java create mode 100644 android/src/kotlinPackage/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.kt delete mode 100644 android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java create mode 100644 android/src/oldarch/java/com/reactnativecommunity/asyncstorage/NativeAsyncStorageModuleSpec.java rename ios/{RNCAsyncStorage.m => RNCAsyncStorage.mm} (99%) create mode 100644 src/NativeAsyncStorageModule.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5fc1c472..2e3254ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,7 +72,7 @@ jobs: yarn bundle:ios --dev false - name: Install Pods run: | - pod install + RCT_NEW_ARCH_ENABLED=1 pod install working-directory: example/ios - name: Boot simulator run: | @@ -109,7 +109,7 @@ jobs: yarn bundle:macos - name: Install Pods run: | - pod install + RCT_NEW_ARCH_ENABLED=1 pod install working-directory: example/macos - name: Build run: | diff --git a/RNCAsyncStorage.podspec b/RNCAsyncStorage.podspec index 5d74402a..25c9420d 100644 --- a/RNCAsyncStorage.podspec +++ b/RNCAsyncStorage.podspec @@ -2,6 +2,8 @@ require 'json' package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) +fabric_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1' + Pod::Spec.new do |s| s.name = "RNCAsyncStorage" s.version = package['version'] @@ -10,10 +12,24 @@ Pod::Spec.new do |s| s.authors = package['author'] s.homepage = package['homepage'] - s.platforms = { :ios => "9.0", :tvos => "9.2", :osx => "10.14" } s.source = { :git => "https://github.com/react-native-async-storage/async-storage.git", :tag => "v#{s.version}" } - s.source_files = "ios/**/*.{h,m}" + s.source_files = "ios/**/*.{h,m,mm}" + + if fabric_enabled + folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' + + s.pod_target_xcconfig = { + 'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/boost" "$(PODS_ROOT)/boost-for-react-native" "$(PODS_ROOT)/RCT-Folly"', + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + } + s.platforms = { ios: '13.4', tvos: '11.0', :osx => "10.15" } + s.compiler_flags = folly_compiler_flags + ' -DRCT_NEW_ARCH_ENABLED' + + install_modules_dependencies(s) + else + s.platforms = { :ios => "9.0", :tvos => "9.2", :osx => "10.14" } - s.dependency 'React-Core' + s.dependency "React-Core" + end end diff --git a/android/build.gradle b/android/build.gradle index 462db2d2..0ed6d484 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -29,6 +29,14 @@ def getVersionOrDefault(String flagName, String defaultVersion) { rootProject.hasProperty(flagName) ? rootProject.properties[flagName] : defaultVersion } +def isNewArchitectureEnabled() { + // To opt-in for the New Architecture, you can either: + // - Set `newArchEnabled` to true inside the `gradle.properties` file + // - Invoke gradle with `-newArchEnabled=true` + // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` + return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" +} + configurations { compileClasspath } @@ -73,8 +81,22 @@ if (useNextStorage) { apply from: './testresults.gradle' } +if (isNewArchitectureEnabled()) { + apply plugin: "com.facebook.react" +} + android { compileSdkVersion safeExtGet('compileSdkVersion', 32) + // Used to override the NDK path/version by allowing users to customize + // the NDK path/version from their root project (e.g. for M1 support) + if (rootProject.hasProperty("ndkPath")) { + ndkPath rootProject.ext.ndkPath + } + if (rootProject.hasProperty("ndkVersion")) { + ndkVersion rootProject.ext.ndkVersion + } + + defaultConfig { minSdkVersion safeExtGet('minSdkVersion', 23) targetSdkVersion safeExtGet('targetSdkVersion', 32) @@ -94,6 +116,20 @@ android { } } } + + sourceSets.main { + java { + if (useNextStorage) { + srcDirs += 'src/kotlinPackage/java' + } else { + srcDirs += 'src/javaPackage/java' + } + + if (!isNewArchitectureEnabled()) { + srcDirs += 'src/oldarch/java' + } + } + } } repositories { @@ -137,6 +173,5 @@ dependencies { testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesTest_version" } - //noinspection GradleDynamicVersion - implementation 'com.facebook.react:react-native:+' // From node_modules + implementation 'com.facebook.react:react-native:+' // from node_modules } diff --git a/android/src/javaPackage/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java b/android/src/javaPackage/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java new file mode 100644 index 00000000..253cd9f0 --- /dev/null +++ b/android/src/javaPackage/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.reactnativecommunity.asyncstorage; + +import com.facebook.react.TurboReactPackage; +import com.facebook.react.ViewManagerOnDemandReactPackage; +import com.facebook.react.bridge.ModuleSpec; +import com.facebook.react.bridge.JavaScriptModule; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.module.annotations.ReactModuleList; +import com.facebook.react.module.model.ReactModuleInfo; +import com.facebook.react.module.model.ReactModuleInfoProvider; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; +import com.facebook.react.uimanager.ViewManager; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +@ReactModuleList( + nativeModules = { + AsyncStorageModule.class, + } +) +public class AsyncStoragePackage extends TurboReactPackage { + + @Override + protected List getViewManagers(ReactApplicationContext reactContext) { + return null; + } + + @Override + public NativeModule getModule(String name, @Nonnull ReactApplicationContext reactContext) { + switch (name) { + case AsyncStorageModule.NAME: + return new AsyncStorageModule(reactContext); + default: + return null; + } + } + + @Override + public ReactModuleInfoProvider getReactModuleInfoProvider() { + try { + Class reactModuleInfoProviderClass = + Class.forName("com.reactnativecommunity.asyncstorage.AsyncStoragePackage$$ReactModuleInfoProvider"); + return (ReactModuleInfoProvider) reactModuleInfoProviderClass.newInstance(); + } catch (ClassNotFoundException e) { + // ReactModuleSpecProcessor does not run at build-time. Create this ReactModuleInfoProvider by + // hand. + return new ReactModuleInfoProvider() { + @Override + public Map getReactModuleInfos() { + final Map reactModuleInfoMap = new HashMap<>(); + + Class[] moduleList = + new Class[] { + AsyncStorageModule.class, + }; + + for (Class moduleClass : moduleList) { + ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class); + + reactModuleInfoMap.put( + reactModule.name(), + new ReactModuleInfo( + reactModule.name(), + moduleClass.getName(), + reactModule.canOverrideExistingModule(), + reactModule.needsEagerInit(), + reactModule.hasConstants(), + reactModule.isCxxModule(), + TurboModule.class.isAssignableFrom(moduleClass))); + } + + return reactModuleInfoMap; + } + }; + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException( + "No ReactModuleInfoProvider for com.reactnativecommunity.asyncstorage.AsyncStoragePackage$$ReactModuleInfoProvider", e); + } + } + + // Deprecated in RN 0.47 + public List> createJSModules() { + return Collections.emptyList(); + } + + @Override + @SuppressWarnings("rawtypes") + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } +} \ No newline at end of file diff --git a/android/src/kotlinPackage/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.kt b/android/src/kotlinPackage/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.kt new file mode 100644 index 00000000..f8d26b6e --- /dev/null +++ b/android/src/kotlinPackage/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.kt @@ -0,0 +1,58 @@ +package com.reactnativecommunity.asyncstorage + +import com.facebook.react.TurboReactPackage +import com.facebook.react.ViewManagerOnDemandReactPackage +import com.facebook.react.bridge.ModuleSpec +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.module.annotations.ReactModuleList +import com.facebook.react.module.model.ReactModuleInfo +import com.facebook.react.module.model.ReactModuleInfoProvider +import com.facebook.react.turbomodule.core.interfaces.TurboModule +import com.facebook.react.uimanager.ReactShadowNode +import com.facebook.react.uimanager.ViewManager +import com.reactnativecommunity.asyncstorage.next.StorageModule + +@ReactModuleList( + nativeModules = [ + StorageModule::class + ] +) +class AsyncStoragePackage : TurboReactPackage() { + override fun getModule(name: String, context: ReactApplicationContext): NativeModule? = when (name) { + StorageModule.NAME -> StorageModule(context) + else -> null + } + + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { + try { + val reactModuleInfoProviderClass = + Class.forName("com.reactnativecommunity.asyncstorage.AsyncStoragePackage$\$ReactModuleInfoProvider") + return reactModuleInfoProviderClass.newInstance() as ReactModuleInfoProvider + } catch (e: ClassNotFoundException) { + return ReactModuleInfoProvider { + val reactModule: ReactModule = StorageModule::class.java.getAnnotation( + ReactModule::class.java)!! + + mutableMapOf( + StorageModule.NAME to ReactModuleInfo( + reactModule.name, + StorageModule::class.java.name, + reactModule.canOverrideExistingModule, + reactModule.needsEagerInit, + reactModule.hasConstants, + reactModule.isCxxModule, + TurboModule::class.java.isAssignableFrom(StorageModule::class.java) + ) + ) + } + } catch (e: InstantiationException) { + throw RuntimeException("No ReactModuleInfoProvider for AsyncStoragePackage$\$ReactModuleInfoProvider", e) + } catch (e: IllegalAccessException) { + throw RuntimeException("No ReactModuleInfoProvider for AsyncStoragePackage$\$ReactModuleInfoProvider", e) + } + } + + override fun getViewManagers(reactContext: ReactApplicationContext?): MutableList? = null +} \ No newline at end of file diff --git a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageModule.java b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageModule.java index 906008e2..3e402486 100644 --- a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageModule.java +++ b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageModule.java @@ -34,10 +34,10 @@ @ReactModule(name = AsyncStorageModule.NAME) public final class AsyncStorageModule - extends ReactContextBaseJavaModule implements ModuleDataCleaner.Cleanable, LifecycleEventListener { + extends NativeAsyncStorageModuleSpec implements ModuleDataCleaner.Cleanable, LifecycleEventListener { // changed name to not conflict with AsyncStorage from RN repo - public static final String NAME = "RNC_AsyncSQLiteDBStorage"; + public static final String NAME = "RNCAsyncStorage"; // SQL variable number limit, defined by SQLITE_LIMIT_VARIABLE_NUMBER: // https://raw.githubusercontent.com/android/platform_external_sqlite/master/dist/sqlite3.c @@ -110,6 +110,7 @@ public void onHostDestroy() { * (key, null) for the keys that haven't been found. */ @ReactMethod + @Override public void multiGet(final ReadableArray keys, final Callback callback) { if (keys == null) { callback.invoke(AsyncStorageErrorUtil.getInvalidKeyError(null), null); @@ -183,6 +184,7 @@ protected void doInBackgroundGuarded(Void... params) { * The insertion will replace conflicting (key, value) pairs. */ @ReactMethod + @Override public void multiSet(final ReadableArray keyValueArray, final Callback callback) { if (keyValueArray.size() == 0) { callback.invoke(); @@ -248,6 +250,7 @@ protected void doInBackgroundGuarded(Void... params) { * Removes all rows of the keys given. */ @ReactMethod + @Override public void multiRemove(final ReadableArray keys, final Callback callback) { if (keys.size() == 0) { callback.invoke(); @@ -300,6 +303,7 @@ protected void doInBackgroundGuarded(Void... params) { * of the given keys, if they exist. */ @ReactMethod + @Override public void multiMerge(final ReadableArray keyValueArray, final Callback callback) { new GuardedAsyncTask(getReactApplicationContext()) { @Override @@ -362,6 +366,7 @@ protected void doInBackgroundGuarded(Void... params) { * Clears the database. */ @ReactMethod + @Override public void clear(final Callback callback) { new GuardedAsyncTask(getReactApplicationContext()) { @Override @@ -385,6 +390,7 @@ protected void doInBackgroundGuarded(Void... params) { * Returns an array with all keys from the database. */ @ReactMethod + @Override public void getAllKeys(final Callback callback) { new GuardedAsyncTask(getReactApplicationContext()) { @Override diff --git a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java b/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java deleted file mode 100644 index 28a78408..00000000 --- a/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.reactnativecommunity.asyncstorage; - -import android.util.Log; -import com.facebook.react.ReactPackage; -import com.facebook.react.bridge.JavaScriptModule; -import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.uimanager.ViewManager; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class AsyncStoragePackage implements ReactPackage { - @Override - public List createNativeModules(ReactApplicationContext reactContext) { - - List moduleList = new ArrayList<>(1); - - if (BuildConfig.AsyncStorage_useNextStorage) { - try { - Class storageClass = Class.forName("com.reactnativecommunity.asyncstorage.next.StorageModule"); - NativeModule inst = (NativeModule) storageClass.getDeclaredConstructor(new Class[]{ReactContext.class}).newInstance(reactContext); - moduleList.add(inst); - AsyncLocalStorageUtil.verifyAndForceSqliteCheckpoint(reactContext); - } catch (Exception e) { - String message = "Something went wrong when initializing module:" - + "\n" - + e.getCause().getClass() - + "\n" - + "Cause:" + e.getCause().getLocalizedMessage(); - Log.e("AsyncStorage_Next", message); - } - } else { - moduleList.add(new AsyncStorageModule(reactContext)); - } - - return moduleList; - } - - // Deprecated in RN 0.47 - public List> createJSModules() { - return Collections.emptyList(); - } - - @Override - @SuppressWarnings("rawtypes") - public List createViewManagers(ReactApplicationContext reactContext) { - return Collections.emptyList(); - } -} \ No newline at end of file diff --git a/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageModule.kt b/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageModule.kt index ade13167..41018e61 100644 --- a/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageModule.kt +++ b/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageModule.kt @@ -4,10 +4,11 @@ import android.content.Context import androidx.annotation.VisibleForTesting import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.Callback -import com.facebook.react.bridge.ReactContext -import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactMethod import com.facebook.react.bridge.ReadableArray +import com.facebook.react.module.annotations.ReactModule +import com.reactnativecommunity.asyncstorage.NativeAsyncStorageModuleSpec import com.reactnativecommunity.asyncstorage.SerialExecutor import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope @@ -16,8 +17,9 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.asExecutor import kotlinx.coroutines.launch -class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), CoroutineScope { - override fun getName() = "RNC_AsyncSQLiteDBStorage" +@ReactModule(name = StorageModule.NAME) +class StorageModule(reactContext: ReactApplicationContext) : NativeAsyncStorageModuleSpec(reactContext), CoroutineScope { + override fun getName() = NAME // this executor is not used by the module, but it must exists here due to // Detox relying on this implementation detail to run @@ -30,6 +32,8 @@ class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), private val storage = StorageSupplier.getInstance(reactContext) companion object { + const val NAME = "RNCAsyncStorage" + @JvmStatic fun getStorageInstance(ctx: Context): AsyncStorageAccess { return StorageSupplier.getInstance(ctx) @@ -37,7 +41,7 @@ class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), } @ReactMethod - fun multiGet(keys: ReadableArray, cb: Callback) { + override fun multiGet(keys: ReadableArray, cb: Callback) { launch(createExceptionHandler(cb)) { val entries = storage.getValues(keys.toKeyList()) cb(null, entries.toKeyValueArgument()) @@ -45,7 +49,7 @@ class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), } @ReactMethod - fun multiSet(keyValueArray: ReadableArray, cb: Callback) { + override fun multiSet(keyValueArray: ReadableArray, cb: Callback) { launch(createExceptionHandler(cb)) { val entries = keyValueArray.toEntryList() storage.setValues(entries) @@ -54,7 +58,7 @@ class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), } @ReactMethod - fun multiRemove(keys: ReadableArray, cb: Callback) { + override fun multiRemove(keys: ReadableArray, cb: Callback) { launch(createExceptionHandler(cb)) { storage.removeValues(keys.toKeyList()) cb(null) @@ -62,7 +66,7 @@ class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), } @ReactMethod - fun multiMerge(keyValueArray: ReadableArray, cb: Callback) { + override fun multiMerge(keyValueArray: ReadableArray, cb: Callback) { launch(createExceptionHandler(cb)) { val entries = keyValueArray.toEntryList() storage.mergeValues(entries) @@ -71,7 +75,7 @@ class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), } @ReactMethod - fun getAllKeys(cb: Callback) { + override fun getAllKeys(cb: Callback) { launch(createExceptionHandler(cb)) { val keys = storage.getKeys() val result = Arguments.createArray() @@ -81,7 +85,7 @@ class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), } @ReactMethod - fun clear(cb: Callback) { + override fun clear(cb: Callback) { launch(createExceptionHandler(cb)) { storage.clear() cb(null) diff --git a/android/src/oldarch/java/com/reactnativecommunity/asyncstorage/NativeAsyncStorageModuleSpec.java b/android/src/oldarch/java/com/reactnativecommunity/asyncstorage/NativeAsyncStorageModuleSpec.java new file mode 100644 index 00000000..c2b97c53 --- /dev/null +++ b/android/src/oldarch/java/com/reactnativecommunity/asyncstorage/NativeAsyncStorageModuleSpec.java @@ -0,0 +1,47 @@ + +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Update this file after changing NativeAsyncStorageModule.ts to match the version generated by codegen. + */ + +package com.reactnativecommunity.asyncstorage; + +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.react.bridge.Callback; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReactModuleWithSpec; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.turbomodule.core.interfaces.TurboModule; + +public abstract class NativeAsyncStorageModuleSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { + public NativeAsyncStorageModuleSpec(ReactApplicationContext reactContext) { + super(reactContext); + } + + @ReactMethod + @DoNotStrip + public abstract void multiGet(ReadableArray keys, Callback callback); + + @ReactMethod + @DoNotStrip + public abstract void multiSet(ReadableArray kvPairs, Callback callback); + + @ReactMethod + @DoNotStrip + public abstract void multiRemove(ReadableArray keys, Callback callback); + + @ReactMethod + @DoNotStrip + public abstract void multiMerge(ReadableArray kvPairs, Callback callback); + + @ReactMethod + @DoNotStrip + public abstract void getAllKeys(Callback callback); + + @ReactMethod + @DoNotStrip + public abstract void clear(Callback callback); +} diff --git a/example/android/build.gradle b/example/android/build.gradle index 02c7b6c0..0b2c332b 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -50,6 +50,14 @@ buildscript { } allprojects { + // TODO: remove when https://github.com/facebook/react-native/issues/35495 is fixed + project.pluginManager.withPlugin("com.facebook.react") { + react { + reactNativeDir = rootProject.file("../../node_modules/react-native/") + codegenDir = rootProject.file("../../node_modules/react-native-codegen/") + } + } + afterEvaluate { project -> def androidExtension = project.extensions.findByName('android') diff --git a/example/android/gradle.properties b/example/android/gradle.properties index bc71f40c..a2bce5df 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -36,7 +36,7 @@ android.enableJetifier=true # Enable new architecture, i.e. Fabric + TurboModule - implies USE_FABRIC=1. # Note that this is incompatible with web debugging. -#newArchEnabled=true +newArchEnabled=true # Uncomment the line below if building react-native from source #ANDROID_NDK_VERSION=21.4.7075529 @@ -50,3 +50,5 @@ KOTLIN_VERSION=1.8.10 # Enable dedicated thread pool executor AsyncStorage_dedicatedExecutor=true AsyncStorage_useNextStorage=true + +REACT_NATIVE_NODE_MODULES_DIR=../node_modules/react-native diff --git a/example/ios/AsyncStorageExample.xcworkspace/contents.xcworkspacedata b/example/ios/AsyncStorageExample.xcworkspace/contents.xcworkspacedata index 7b3c0ff9..02c60077 100644 --- a/example/ios/AsyncStorageExample.xcworkspace/contents.xcworkspacedata +++ b/example/ios/AsyncStorageExample.xcworkspace/contents.xcworkspacedata @@ -7,4 +7,7 @@ + + diff --git a/example/macos/Podfile b/example/macos/Podfile index 19f64521..d25aa3d6 100644 --- a/example/macos/Podfile +++ b/example/macos/Podfile @@ -2,6 +2,12 @@ require_relative '../../node_modules/react-native-test-app/macos/test_app.rb' workspace 'AsyncStorageExample.xcworkspace' +options = { + :fabric_enabled => false, + :hermes_enabled => false, + :turbomodule_enabled => false, +} + use_test_app! do |target| target.app do pod 'RNCAsyncStorage', :path => '../..' diff --git a/ios/RNCAsyncStorage.h b/ios/RNCAsyncStorage.h index 5dfac047..1743c3bd 100644 --- a/ios/RNCAsyncStorage.h +++ b/ios/RNCAsyncStorage.h @@ -10,6 +10,10 @@ #import #import +#ifdef RCT_NEW_ARCH_ENABLED +#import +#endif + #import "RNCAsyncStorageDelegate.h" /** @@ -23,7 +27,13 @@ * * Keys and values must always be strings or an error is returned. */ -@interface RNCAsyncStorage : NSObject +@interface RNCAsyncStorage : NSObject < +#ifdef RCT_NEW_ARCH_ENABLED + NativeAsyncStorageModuleSpec +#else + RCTBridgeModule +#endif + , RCTInvalidating> @property (nonatomic, weak, nullable) id delegate; diff --git a/ios/RNCAsyncStorage.m b/ios/RNCAsyncStorage.mm similarity index 99% rename from ios/RNCAsyncStorage.m rename to ios/RNCAsyncStorage.mm index 75c87471..508494a0 100644 --- a/ios/RNCAsyncStorage.m +++ b/ios/RNCAsyncStorage.mm @@ -895,4 +895,12 @@ - (BOOL)_passthroughDelegate } } +#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 d45625ae..d303cb0d 100644 --- a/package.json +++ b/package.json @@ -67,8 +67,8 @@ "react-native": "^0.0.0-0 || 0.60 - 0.72 || 1000.0.0" }, "devDependencies": { - "@babel/core": "^7.12.0", - "@babel/preset-env": "^7.1.6", + "@babel/core": "^7.20.0", + "@babel/preset-env": "^7.20.0", "@react-native-community/eslint-config": "^3.0.0", "@semantic-release/changelog": "^6.0.0", "@semantic-release/git": "^10.0.0", @@ -85,6 +85,7 @@ "react-dom": "^18.2.0", "react-native": "^0.71.0", "react-native-builder-bob": "^0.18.0", + "react-native-codegen": "^0.71.5", "react-native-macos": "^0.71.0", "react-native-test-app": "^2.5.8", "react-native-web": "~0.18.10", @@ -163,5 +164,13 @@ } ] ] + }, + "codegenConfig": { + "name": "rnasyncstorage", + "type": "modules", + "jsSrcsDir": "./src", + "android": { + "javaPackageName": "com.reactnativecommunity.asyncstorage" + } } } diff --git a/react-native.config.js b/react-native.config.js index be37067c..9268f760 100644 --- a/react-native.config.js +++ b/react-native.config.js @@ -7,7 +7,10 @@ const project = (() => { sourceDir: path.join('example', 'android'), }, ios: { - sourceDir: 'example/ios', + sourceDir: path.join('example', 'ios'), + }, + macos: { + sourceDir: path.join('example', 'maacos'), }, windows: { sourceDir: path.join('example', 'windows'), diff --git a/src/NativeAsyncStorageModule.ts b/src/NativeAsyncStorageModule.ts new file mode 100644 index 00000000..a57af4ed --- /dev/null +++ b/src/NativeAsyncStorageModule.ts @@ -0,0 +1,26 @@ +import { TurboModuleRegistry, TurboModule } from 'react-native'; + +export interface Spec extends TurboModule { + multiGet: ( + keys: string[], + callback: (error?: Object[], result?: [string, string][]) => void + ) => void; + multiSet: ( + kvPairs: [string, string][], + callback: (error?: Object[]) => void + ) => void; + multiRemove: ( + keys: readonly string[], + callback: (error?: Object[]) => void + ) => void; + multiMerge: ( + kvPairs: [string, string][], + callback: (error?: Object[]) => void + ) => void; + getAllKeys: ( + callback: (error?: Object[], result?: [string, string][]) => void + ) => void; + clear: (callback: (error?: Object[]) => void) => void; +} + +export default TurboModuleRegistry.get('RNCAsyncStorage'); diff --git a/yarn.lock b/yarn.lock index df7e7cb4..f26e266e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -100,7 +100,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.0, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.16, @babel/core@npm:^7.14.0, @babel/core@npm:^7.18.5, @babel/core@npm:^7.20.0": +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.16, @babel/core@npm:^7.14.0, @babel/core@npm:^7.18.5, @babel/core@npm:^7.20.0": version: 7.21.4 resolution: "@babel/core@npm:7.21.4" dependencies: @@ -1422,7 +1422,7 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:^7.1.6, @babel/preset-env@npm:^7.18.2, @babel/preset-env@npm:^7.20.0": +"@babel/preset-env@npm:^7.18.2, @babel/preset-env@npm:^7.20.0": version: 7.21.4 resolution: "@babel/preset-env@npm:7.21.4" dependencies: @@ -2849,8 +2849,8 @@ __metadata: version: 0.0.0-use.local resolution: "@react-native-async-storage/async-storage@workspace:." dependencies: - "@babel/core": ^7.12.0 - "@babel/preset-env": ^7.1.6 + "@babel/core": ^7.20.0 + "@babel/preset-env": ^7.20.0 "@react-native-community/eslint-config": ^3.0.0 "@semantic-release/changelog": ^6.0.0 "@semantic-release/git": ^10.0.0 @@ -2868,6 +2868,7 @@ __metadata: react-dom: ^18.2.0 react-native: ^0.71.0 react-native-builder-bob: ^0.18.0 + react-native-codegen: ^0.71.5 react-native-macos: ^0.71.0 react-native-test-app: ^2.5.8 react-native-web: ~0.18.10