diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 45d257b2..00000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,133 +0,0 @@ - -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall - community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or advances of - any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, - without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -[INSERT CONTACT METHOD]. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index 284c2955..fcefba66 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -1,15 +1,25 @@ -cmake_minimum_required(VERSION 3.4.1) -project(AudioContext) +cmake_minimum_required(VERSION 3.9.0) +project(react-native-audio-context) set (CMAKE_VERBOSE_MAKEFILE ON) set (CMAKE_CXX_STANDARD 14) -add_library(react-native-audio-context SHARED - ../cpp/react-native-audio-context.cpp +add_library(react-native-audio-context + SHARED + ../cpp/JSIExampleHostObject.cpp cpp-adapter.cpp ) # Specifies a path to native header files. include_directories( ../cpp + ../node_modules/react-native/ReactCommon/jsi +) + +find_package(ReactAndroid REQUIRED CONFIG) + +target_link_libraries( + react-native-audio-context + ReactAndroid::jsi + android ) diff --git a/android/build.gradle b/android/build.gradle index a2525771..431dd98a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,3 +1,5 @@ +import com.android.Version + buildscript { repositories { google() @@ -5,7 +7,7 @@ buildscript { } dependencies { - classpath "com.android.tools.build:gradle:7.2.1" + classpath "com.android.tools.build:gradle:7.2.2" } } @@ -19,6 +21,7 @@ def isNewArchitectureEnabled() { } apply plugin: "com.android.library" +apply plugin: 'org.jetbrains.kotlin.android' if (isNewArchitectureEnabled()) { apply plugin: "com.facebook.react" @@ -32,8 +35,8 @@ def getExtOrIntegerDefault(name) { return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["AudioContext_" + name]).toInteger() } -def supportsNamespace() { - def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.') +static def supportsNamespace() { + def parsed = Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.') def major = parsed[0].toInteger() def minor = parsed[1].toInteger() @@ -64,6 +67,7 @@ android { cmake { cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all" abiFilters (*reactNativeArchitectures()) + arguments "-DANDROID_STL=c++_shared" } } } @@ -76,6 +80,7 @@ android { buildFeatures { buildConfig true + prefab true } buildTypes { @@ -103,6 +108,9 @@ android { } } } + kotlinOptions { + jvmTarget = '17' + } } repositories { @@ -116,12 +124,13 @@ dependencies { // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" + implementation 'androidx.core:core-ktx:1.13.1' } if (isNewArchitectureEnabled()) { react { jsRootDir = file("../src/") - libraryName = "AudioContext" + libraryName = "react-native-audio-context" codegenJavaPackageName = "com.audiocontext" } } diff --git a/android/cpp-adapter.cpp b/android/cpp-adapter.cpp index 4c41beed..a7b0247d 100644 --- a/android/cpp-adapter.cpp +++ b/android/cpp-adapter.cpp @@ -1,8 +1,20 @@ #include -#include "react-native-audio-context.h" +#include +#include "JSIExampleHostObject.h" + +using namespace facebook; + +void install(jsi::Runtime& runtime) { + auto hostObject = std::make_shared(); + auto object = jsi::Object::createFromHostObject(runtime, hostObject); + runtime.global().setProperty(runtime, "__JSIExampleProxy", std::move(object)); +} extern "C" -JNIEXPORT jdouble JNICALL -Java_com_audiocontext_AudioContextModule_nativeMultiply(JNIEnv *env, jclass type, jdouble a, jdouble b) { - return audiocontext::multiply(a, b); +JNIEXPORT void JNICALL +Java_com_audiocontext_jsi_JSIExampleModule_00024Companion_nativeInstall(JNIEnv *env, jobject clazz, jlong jsiPtr) { + auto runtime = reinterpret_cast(jsiPtr); + if (runtime) { + install(*runtime); + } } diff --git a/android/src/main/java/com/audiocontext/AudioContextModule.java b/android/src/main/java/com/audiocontext/AudioContextModule.java deleted file mode 100644 index 260f08d2..00000000 --- a/android/src/main/java/com/audiocontext/AudioContextModule.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.audiocontext; - -import androidx.annotation.NonNull; - -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.module.annotations.ReactModule; - -@ReactModule(name = AudioContextModule.NAME) -public class AudioContextModule extends NativeAudioContextSpec { - public static final String NAME = "AudioContext"; - - public AudioContextModule(ReactApplicationContext reactContext) { - super(reactContext); - } - - @Override - @NonNull - public String getName() { - return NAME; - } - - static { - System.loadLibrary("react-native-audio-context"); - } - - private static native double nativeMultiply(double a, double b); - - // Example method - // See https://reactnative.dev/docs/native-modules-android - @Override - public double multiply(double a, double b) { - return nativeMultiply(a, b); - } -} diff --git a/android/src/main/java/com/audiocontext/AudioContextPackage.java b/android/src/main/java/com/audiocontext/AudioContextPackage.java deleted file mode 100644 index 0fa83f7c..00000000 --- a/android/src/main/java/com/audiocontext/AudioContextPackage.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.audiocontext; - -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 java.util.HashMap; -import java.util.Map; - -public class AudioContextPackage extends TurboReactPackage { - - @Nullable - @Override - public NativeModule getModule(String name, ReactApplicationContext reactContext) { - if (name.equals(AudioContextModule.NAME)) { - return new AudioContextModule(reactContext); - } else { - return null; - } - } - - @Override - public ReactModuleInfoProvider getReactModuleInfoProvider() { - return () -> { - final Map moduleInfos = new HashMap<>(); - moduleInfos.put( - AudioContextModule.NAME, - new ReactModuleInfo( - AudioContextModule.NAME, - AudioContextModule.NAME, - false, // canOverrideExistingModule - false, // needsEagerInit - true, // hasConstants - false, // isCxxModule - true // isTurboModule - )); - return moduleInfos; - }; - } -} diff --git a/android/src/main/java/com/audiocontext/JSIExamplePackage.kt b/android/src/main/java/com/audiocontext/JSIExamplePackage.kt new file mode 100644 index 00000000..81750b80 --- /dev/null +++ b/android/src/main/java/com/audiocontext/JSIExamplePackage.kt @@ -0,0 +1,17 @@ +package com.audiocontext + +import com.audiocontext.jsi.JSIExampleModule +import com.facebook.react.ReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.uimanager.ViewManager + +class JSIExamplePackage : ReactPackage { + override fun createNativeModules(reactContext: ReactApplicationContext): List { + return listOf(JSIExampleModule(reactContext)) + } + + override fun createViewManagers(reactContext: ReactApplicationContext): List> { + return emptyList() + } +} diff --git a/android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt b/android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt new file mode 100644 index 00000000..371c2886 --- /dev/null +++ b/android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt @@ -0,0 +1,36 @@ +package com.audiocontext.jsi + +import android.util.Log +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.module.annotations.ReactModule + +@ReactModule(name = JSIExampleModule.NAME) +class JSIExampleModule(reactContext: ReactApplicationContext?) : + ReactContextBaseJavaModule(reactContext) { + override fun getName(): String { + return NAME + } + + @ReactMethod(isBlockingSynchronousMethod = true) + fun install(): Boolean { + try { + System.loadLibrary("react-native-audio-context") + + val jsContext = reactApplicationContext.javaScriptContextHolder + + nativeInstall(jsContext!!.get()) + return true + } catch (exception: Exception) { + Log.e(NAME, "Failed to install JSI Bindings for react-native-audio-context", exception) + return false + } + } + + companion object { + const val NAME: String = "JSIExample" + + private external fun nativeInstall(jsiPtr: Long) + } +} diff --git a/cpp/JSIExampleHostObject.cpp b/cpp/JSIExampleHostObject.cpp new file mode 100644 index 00000000..75c00889 --- /dev/null +++ b/cpp/JSIExampleHostObject.cpp @@ -0,0 +1,51 @@ +#include "JSIExampleHostObject.h" +#include + +namespace example { + using namespace facebook; + + std::vector JSIExampleHostObject::getPropertyNames(jsi::Runtime &runtime) + { + std::vector propertyNames; + propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "helloWorld")); + return propertyNames; + } + + jsi::Value JSIExampleHostObject::get(jsi::Runtime &runtime, const jsi::PropNameID &propNameId) { + auto propName = propNameId.utf8(runtime); + + if (propName == "helloWorld") { + return jsi::Function::createFromHostFunction(runtime, propNameId, 2, + [this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) { + if (count != 2) { + throw std::invalid_argument("helloWorld expects exactly two arguments"); + } + return this->helloWorld(rt, args[0], args[1]); + }); + } + + throw std::runtime_error("Not yet implemented!"); + } + + void JSIExampleHostObject::set(jsi::Runtime &runtime, const jsi::PropNameID &propNameId, const jsi::Value &value) + { + auto propName = propNameId.utf8(runtime); + if (propName == "helloWorld") + { + // Do nothing + return; + } + throw std::runtime_error("Not yet implemented!"); + } + + jsi::Value JSIExampleHostObject::helloWorld(jsi::Runtime &runtime, const jsi::Value &value, const jsi::Value &value2) { + if (value.isNumber() && value2.isNumber()) { + // Extract numbers and add them + double result = value.asNumber() + value2.asNumber(); + return jsi::Value(result); + } else { + // Handle other cases (e.g., one is a number and the other is a string) + return jsi::Value::undefined(); + } + } +} diff --git a/cpp/JSIExampleHostObject.h b/cpp/JSIExampleHostObject.h new file mode 100644 index 00000000..e305e3a8 --- /dev/null +++ b/cpp/JSIExampleHostObject.h @@ -0,0 +1,25 @@ +#ifndef JSIEXAMPLEHOSTOBJECT_H +#define JSIEXAMPLEHOSTOBJECT_H + +#include + +namespace example +{ + + using namespace facebook; + + class JSI_EXPORT JSIExampleHostObject : public jsi::HostObject + { + public: + explicit JSIExampleHostObject() = default; + + public: + jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override; + void set(jsi::Runtime &, const jsi::PropNameID &name, const jsi::Value &value) override; + std::vector getPropertyNames(jsi::Runtime &rt) override; + static jsi::Value helloWorld(jsi::Runtime &, const jsi::Value &value, const jsi::Value &value2); + }; + +} // namespace margelo + +#endif /* JSIEXAMPLEHOSTOBJECT_H */ diff --git a/cpp/react-native-audio-context.cpp b/cpp/react-native-audio-context.cpp deleted file mode 100644 index da831999..00000000 --- a/cpp/react-native-audio-context.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "react-native-audio-context.h" - -namespace audiocontext { - double multiply(double a, double b) { - return a * b; - } -} diff --git a/cpp/react-native-audio-context.h b/cpp/react-native-audio-context.h deleted file mode 100644 index f9c194e3..00000000 --- a/cpp/react-native-audio-context.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef AUDIOCONTEXT_H -#define AUDIOCONTEXT_H - -namespace audiocontext { - double multiply(double a, double b); -} - -#endif /* AUDIOCONTEXT_H */ diff --git a/example/android/app/src/main/java/com/audiocontextexample/MainApplication.kt b/example/android/app/src/main/java/com/audiocontextexample/MainApplication.kt index 5687b91d..5a30f4aa 100644 --- a/example/android/app/src/main/java/com/audiocontextexample/MainApplication.kt +++ b/example/android/app/src/main/java/com/audiocontextexample/MainApplication.kt @@ -3,41 +3,28 @@ package com.audiocontextexample import android.app.Application import com.facebook.react.PackageList import com.facebook.react.ReactApplication -import com.facebook.react.ReactHost import com.facebook.react.ReactNativeHost import com.facebook.react.ReactPackage -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load -import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost -import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.soloader.SoLoader class MainApplication : Application(), ReactApplication { + override val reactNativeHost: ReactNativeHost = object : ReactNativeHost(this) { + override fun getUseDeveloperSupport(): Boolean { + return BuildConfig.DEBUG + } - override val reactNativeHost: ReactNativeHost = - object : DefaultReactNativeHost(this) { - override fun getPackages(): List = - PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here, for example: - // add(MyReactNativePackage()) - } - - override fun getJSMainModuleName(): String = "index" - - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG - - override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED - } + override fun getPackages(): List { + val packages = PackageList(this).packages + return packages + } - override val reactHost: ReactHost - get() = getDefaultReactHost(applicationContext, reactNativeHost) + override fun getJSMainModuleName(): String { + return "index" + } + } override fun onCreate() { super.onCreate() - SoLoader.init(this, false) - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - // If you opted-in for the New Architecture, we load the native entry point for this app. - load() - } + SoLoader.init(this, /* native exopackage */false) } } diff --git a/example/android/build.gradle b/example/android/build.gradle index f536a792..9a71b785 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -6,6 +6,7 @@ buildscript { targetSdkVersion = 34 ndkVersion = "26.1.10909125" kotlinVersion = "1.9.22" + kotlin_version = '2.0.0' } repositories { google() @@ -15,6 +16,7 @@ buildscript { classpath("com.android.tools.build:gradle") classpath("com.facebook.react:react-native-gradle-plugin") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/example/ios/AudioContextExample.xcodeproj/project.pbxproj b/example/ios/AudioContextExample.xcodeproj/project.pbxproj index ed428ae8..f35d8b5c 100644 --- a/example/ios/AudioContextExample.xcodeproj/project.pbxproj +++ b/example/ios/AudioContextExample.xcodeproj/project.pbxproj @@ -592,7 +592,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; @@ -664,7 +667,10 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/example/ios/Podfile b/example/ios/Podfile index 5dd276ef..bbce5e3e 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,3 @@ -ENV['RCT_NEW_ARCH_ENABLED'] = '1' - # Resolve react_native_pods.rb with node to allow for hoisting require Pod::Executable.execute_command('node', ['-p', 'require.resolve( diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index b3eff311..841860c1 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1395,15 +1395,15 @@ SPEC CHECKSUMS: React-jsitracing: 6b3c8c98313642140530f93c46f5a6ca4530b446 React-logger: fa92ba4d3a5d39ac450f59be2a3cec7b099f0304 React-Mapbuffer: 9f68550e7c6839d01411ac8896aea5c868eff63a - react-native-audio-context: 11ae1a36b7d91d7cbd04cf5d5e27a6a9fb145eb8 + react-native-audio-context: 4184c3164b1911b9a2632be8dfeecc0cb3d41c9b React-nativeconfig: fa5de9d8f4dbd5917358f8ad3ad1e08762f01dcb React-NativeModulesApple: 585d1b78e0597de364d259cb56007052d0bda5e5 React-perflogger: 7bb9ba49435ff66b666e7966ee10082508a203e8 React-RCTActionSheet: a2816ae2b5c8523c2bc18a8f874a724a096e6d97 React-RCTAnimation: e78f52d7422bac13e1213e25e9bcbf99be872e1a - React-RCTAppDelegate: 4843f73d1089552a7d7f4ec6d29e9942c8f5e161 + React-RCTAppDelegate: 24f46de486cfa3a9f46e4b0786eaf17d92e1e0c6 React-RCTBlob: 9f9d6599d1b00690704dadc4a4bc33a7e76938be - React-RCTFabric: 56eb7973b13cd9d7be03ca06f621ed0edd124b81 + React-RCTFabric: 609e66bb0371b9082c62ed677ee0614efe711bf2 React-RCTImage: 39dd5aee6b92213845e1e7a7c41865801dc33493 React-RCTLinking: 35d742a982f901f9ea416d772763e2da65c2dc7d React-RCTNetwork: b078576c0c896c71905f841716b9f9f5922111dc @@ -1422,6 +1422,6 @@ SPEC CHECKSUMS: SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Yoga: 88480008ccacea6301ff7bf58726e27a72931c8d -PODFILE CHECKSUM: 1b2da41d5518b36ddce0d4e029682759a82955cc +PODFILE CHECKSUM: 122f19e943aa833c1ad02dd5fd0e0364e65fe9ce COCOAPODS: 1.14.3 diff --git a/example/src/App.tsx b/example/src/App.tsx index c65350db..c4e096b2 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,15 +1,19 @@ -import * as React from 'react'; - +import React from 'react'; import { StyleSheet, View, Text } from 'react-native'; -import { multiply } from 'react-native-audio-context'; +import { JSIExample } from '../../src/JSIExample'; -const result = multiply(3, 7); +const App: React.FC = () => { + const sayHello = () => { + //JSIExample.helloWorld = 'Hello World'; + return JSIExample.helloWorld(2, 3); + }; -const App: React.FC = () => ( - - Result: {result} - -); + return ( + + {sayHello()} + + ); +}; export default App; @@ -18,10 +22,27 @@ const styles = StyleSheet.create({ flex: 1, alignItems: 'center', justifyContent: 'center', + paddingHorizontal: 20, + }, + keys: { + fontSize: 14, + color: 'grey', + }, + title: { + fontSize: 16, + color: 'black', + marginRight: 10, }, - box: { - width: 60, - height: 60, + row: { + flexDirection: 'row', + alignItems: 'center', + }, + textInput: { + flex: 1, marginVertical: 20, + borderWidth: StyleSheet.hairlineWidth, + borderColor: 'black', + borderRadius: 5, + padding: 10, }, }); diff --git a/ios/AudioContext.h b/ios/AudioContext.h deleted file mode 100644 index de2a3b10..00000000 --- a/ios/AudioContext.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifdef __cplusplus -#import "react-native-audio-context.h" -#endif - -#ifdef RCT_NEW_ARCH_ENABLED -#import "RNAudioContextSpec.h" - -@interface AudioContext : NSObject -#else -#import - -@interface AudioContext : NSObject -#endif - -@end diff --git a/ios/AudioContext.mm b/ios/AudioContext.mm deleted file mode 100644 index dca8c82e..00000000 --- a/ios/AudioContext.mm +++ /dev/null @@ -1,21 +0,0 @@ -#import "AudioContext.h" - -@implementation AudioContext -RCT_EXPORT_MODULE() - -// Don't compile this code when we build for the old architecture. -#ifdef RCT_NEW_ARCH_ENABLED -- (NSNumber *)multiply:(double)a b:(double)b { - NSNumber *result = @(audiocontext::multiply(a, b)); - - return result; -} - -- (std::shared_ptr)getTurboModule: - (const facebook::react::ObjCTurboModule::InitParams &)params -{ - return std::make_shared(params); -} -#endif - -@end diff --git a/ios/JSIExampleModule.h b/ios/JSIExampleModule.h new file mode 100644 index 00000000..bcf1dd02 --- /dev/null +++ b/ios/JSIExampleModule.h @@ -0,0 +1,5 @@ +#import + +@interface JSIExampleModule : NSObject + +@end diff --git a/ios/JSIExampleModule.mm b/ios/JSIExampleModule.mm new file mode 100644 index 00000000..0e3fb779 --- /dev/null +++ b/ios/JSIExampleModule.mm @@ -0,0 +1,38 @@ +#import "JSIExampleModule.h" + +#import +#import +#import + +#import "../cpp/JSIExampleHostObject.h" + +@implementation JSIExampleModule + +RCT_EXPORT_MODULE(JSIExample) + +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) +{ + NSLog(@"Installing JSI bindings for react-native-audio-context..."); + RCTBridge* bridge = [RCTBridge currentBridge]; + RCTCxxBridge* cxxBridge = (RCTCxxBridge*)bridge; + if (cxxBridge == nil) { + return @false; + } + + using namespace facebook; + + auto jsiRuntime = (jsi::Runtime*) cxxBridge.runtime; + if (jsiRuntime == nil) { + return @false; + } + auto& runtime = *jsiRuntime; + + auto hostObject = std::make_shared(); + auto object = jsi::Object::createFromHostObject(runtime, hostObject); + runtime.global().setProperty(runtime, "__JSIExampleProxy", std::move(object)); + + NSLog(@"Successfully installed JSI bindings for react-native-audio-context!"); + return @true; +} + +@end diff --git a/package.json b/package.json index 827b8d4a..2b2771ac 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-react": "^7.34.3", "jest": "^29.7.0", "prettier": "^3.0.3", "react": "18.2.0", @@ -163,7 +164,7 @@ "jsSrcsDir": "src" }, "create-react-native-library": { - "type": "module-new", + "type": "module-legacy", "languages": "cpp", "version": "0.37.1" } diff --git a/src/JSIExample.ts b/src/JSIExample.ts new file mode 100644 index 00000000..ddd6070b --- /dev/null +++ b/src/JSIExample.ts @@ -0,0 +1,68 @@ +import { NativeModules, Platform } from 'react-native'; + +export interface JSIExampleWrapper { + helloWorld(num1: number, num2: number): string; +} + +// global func declaration for JSI functions +declare global { + function nativeCallSyncHook(): unknown; + var __JSIExampleProxy: JSIExampleWrapper | undefined; +} + +// Check if the constructor exists. If not, try installing the JSI bindings. +if (global.__JSIExampleProxy == null) { + // Get the native JSIExample ReactModule + const JSIExampleModule = NativeModules.JSIExample; + if (JSIExampleModule == null) { + let message = + 'Failed to install react-native-fast-crypto: The native `JSIExample` Module could not be found.'; + message += + '\n* Make sure react-native-fast-crypto is correctly autolinked (run `npx react-native config` to verify)'; + if (Platform.OS === 'ios' || Platform.OS === 'macos') { + message += '\n* Make sure you ran `pod install` in the ios/ directory.'; + } + if (Platform.OS === 'android') { + message += '\n* Make sure gradle is synced.'; + } + // check if Expo + const ExpoConstants = + NativeModules.NativeUnimoduleProxy?.modulesConstants?.ExponentConstants; + if (ExpoConstants != null) { + if (ExpoConstants.appOwnership === 'expo') { + // We're running Expo Go + throw new Error( + 'react-native-fast-crypto is not supported in Expo Go! Use EAS (`expo prebuild`) or eject to a bare workflow instead.' + ); + } else { + // We're running Expo bare / standalone + message += '\n* Make sure you ran `expo prebuild`.'; + } + } + + message += '\n* Make sure you rebuilt the app.'; + throw new Error(message); + } + + // Check if we are running on-device (JSI) + if (global.nativeCallSyncHook == null || JSIExampleModule.install == null) { + throw new Error( + 'Failed to install react-native-fast-crypto: React Native is not running on-device. JSIExample can only be used when synchronous method invocations (JSI) are possible. If you are using a remote debugger (e.g. Chrome), switch to an on-device debugger (e.g. Flipper) instead.' + ); + } + + // Call the synchronous blocking install() function + const result = JSIExampleModule.install(); + if (result !== true) + throw new Error( + `Failed to install react-native-fast-crypto: The native JSIExample Module could not be installed! Looks like something went wrong when installing JSI bindings: ${result}` + ); + + // Check again if the constructor now exists. If not, throw an error. + if (global.__JSIExampleProxy == null) + throw new Error( + 'Failed to install react-native-fast-crypto, the native initializer function does not exist. Are you trying to use JSIExample from different JS Runtimes?' + ); +} + +export const JSIExample: JSIExampleWrapper = global.__JSIExampleProxy; diff --git a/src/NativeAudioContext.ts b/src/NativeAudioContext.ts deleted file mode 100644 index f294aef8..00000000 --- a/src/NativeAudioContext.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { TurboModule } from 'react-native'; -import { TurboModuleRegistry } from 'react-native'; - -export interface Spec extends TurboModule { - multiply(a: number, b: number): number; -} - -export default TurboModuleRegistry.getEnforcing('AudioContext'); diff --git a/src/index.tsx b/src/index.tsx index f6f79871..41f5c943 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1 @@ -const AudioContext = require('./NativeAudioContext').default; - -export function multiply(a: number, b: number): number { - return AudioContext.multiply(a, b); -} +export * from './JSIExample'; diff --git a/yarn.lock b/yarn.lock index 67f30028..ce070645 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5988,7 +5988,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react@npm:^7.30.1": +"eslint-plugin-react@npm:^7.30.1, eslint-plugin-react@npm:^7.34.3": version: 7.34.3 resolution: "eslint-plugin-react@npm:7.34.3" dependencies: @@ -10886,6 +10886,7 @@ __metadata: eslint: ^8.51.0 eslint-config-prettier: ^9.0.0 eslint-plugin-prettier: ^5.0.1 + eslint-plugin-react: ^7.34.3 jest: ^29.7.0 prettier: ^3.0.3 react: 18.2.0