react-native-nitro-modules 0.16.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NitroModules.podspec +2 -1
- package/README.md +6 -6
- package/android/build.gradle +10 -1
- package/android/src/main/cpp/core/JPromise.hpp +62 -5
- package/android/src/main/cpp/utils/JUnit.hpp +38 -0
- package/android/src/main/java/com/margelo/nitro/core/Promise.kt +76 -10
- package/cpp/core/HybridFunction.hpp +1 -0
- package/cpp/core/Promise.cpp +10 -0
- package/cpp/core/Promise.hpp +388 -0
- package/cpp/jsi/JSIConverter+Exception.hpp +44 -0
- package/cpp/jsi/JSIConverter+Future.hpp +45 -0
- package/cpp/jsi/JSIConverter+Promise.hpp +76 -62
- package/cpp/jsi/JSIConverter.hpp +2 -0
- package/cpp/prototype/HybridObjectPrototype.cpp +1 -0
- package/cpp/threading/ThreadPool.cpp +47 -32
- package/cpp/threading/ThreadPool.hpp +15 -3
- package/ios/core/RuntimeError.swift +18 -0
- package/ios/utils/RuntimeError.hpp +23 -0
- package/ios/utils/SwiftClosure.hpp +5 -1
- package/package.json +2 -2
- package/cpp/jsi/JSPromise.cpp +0 -58
- package/cpp/jsi/JSPromise.hpp +0 -54
- package/ios/core/PromiseHolder.hpp +0 -86
package/NitroModules.podspec
CHANGED
|
@@ -31,6 +31,7 @@ Pod::Spec.new do |s|
|
|
|
31
31
|
"cpp/core/AnyMap.hpp",
|
|
32
32
|
"cpp/core/ArrayBuffer.hpp",
|
|
33
33
|
"cpp/core/HybridObject.hpp",
|
|
34
|
+
"cpp/core/Promise.hpp",
|
|
34
35
|
"cpp/entrypoint/HybridNitroModulesProxy.hpp",
|
|
35
36
|
"cpp/entrypoint/InstallNitro.hpp",
|
|
36
37
|
"cpp/registry/HybridObjectRegistry.hpp",
|
|
@@ -40,9 +41,9 @@ Pod::Spec.new do |s|
|
|
|
40
41
|
"cpp/utils/NitroDefines.hpp",
|
|
41
42
|
# Public iOS-specific headers that will be exposed in modulemap (for Swift)
|
|
42
43
|
"ios/core/ArrayBufferHolder.hpp",
|
|
43
|
-
"ios/core/PromiseHolder.hpp",
|
|
44
44
|
"ios/core/AnyMapHolder.hpp",
|
|
45
45
|
"ios/core/HybridContext.hpp",
|
|
46
|
+
"ios/utils/RuntimeError.hpp",
|
|
46
47
|
"ios/utils/SwiftClosure.hpp",
|
|
47
48
|
]
|
|
48
49
|
|
package/README.md
CHANGED
|
@@ -169,12 +169,6 @@ The following C++ / JS types are supported out of the box:
|
|
|
169
169
|
<td><code>T?</code></td>
|
|
170
170
|
<td><code>T?</code></td>
|
|
171
171
|
</tr>
|
|
172
|
-
<tr>
|
|
173
|
-
<td><code>Promise<T></code></td>
|
|
174
|
-
<td><code>std::future<T></code></td>
|
|
175
|
-
<td><code><a href="./ios/core/Promise.swift">Promise<T></a></code></td>
|
|
176
|
-
<td><code><a href="./android/src/main/java/com/margelo/nitro/core/Promise.kt">Promise<T></a></code></td>
|
|
177
|
-
</tr>
|
|
178
172
|
<tr>
|
|
179
173
|
<td><code>(T...) => void</code></td>
|
|
180
174
|
<td><code>std::function<void (T...)></code></td>
|
|
@@ -187,6 +181,12 @@ The following C++ / JS types are supported out of the box:
|
|
|
187
181
|
<td>❌</td>
|
|
188
182
|
<td>❌</td>
|
|
189
183
|
</tr>
|
|
184
|
+
<tr>
|
|
185
|
+
<td><code>Promise<T></code></td>
|
|
186
|
+
<td><code>std::shared_ptr<<a href="./cpp/core/Promise.hpp">Promise<T></a>></code></td>
|
|
187
|
+
<td><code><a href="./ios/core/Promise.swift">Promise<T></a></code></td>
|
|
188
|
+
<td><code><a href="./android/src/main/java/com/margelo/nitro/core/Promise.kt">Promise<T></a></code></td>
|
|
189
|
+
</tr>
|
|
190
190
|
<tr>
|
|
191
191
|
<td><code>{ ... }</code></td>
|
|
192
192
|
<td><code>std::shared_ptr<<a href="./cpp/core/AnyMap.hpp">AnyMap</a>></code></td>
|
package/android/build.gradle
CHANGED
|
@@ -48,9 +48,18 @@ android {
|
|
|
48
48
|
|
|
49
49
|
externalNativeBuild {
|
|
50
50
|
cmake {
|
|
51
|
-
cppFlags "-
|
|
51
|
+
cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
|
|
52
52
|
arguments "-DANDROID_STL=c++_shared"
|
|
53
53
|
abiFilters (*reactNativeArchitectures())
|
|
54
|
+
|
|
55
|
+
buildTypes {
|
|
56
|
+
debug {
|
|
57
|
+
cppFlags "-O1 -g"
|
|
58
|
+
}
|
|
59
|
+
release {
|
|
60
|
+
cppFlags "-O2"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
54
63
|
}
|
|
55
64
|
}
|
|
56
65
|
}
|
|
@@ -7,12 +7,30 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
+
#include "Promise.hpp"
|
|
10
11
|
#include <fbjni/fbjni.h>
|
|
12
|
+
#include <mutex>
|
|
11
13
|
|
|
12
14
|
namespace margelo::nitro {
|
|
13
15
|
|
|
14
16
|
using namespace facebook;
|
|
15
17
|
|
|
18
|
+
struct JOnResolvedCallback : public jni::JavaClass<JOnResolvedCallback> {
|
|
19
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/core/Promise$OnResolvedCallback;";
|
|
20
|
+
void onResolved(const jni::alias_ref<jni::JObject>& result) const {
|
|
21
|
+
static const auto method = javaClassLocal()->getMethod<void(jni::alias_ref<jni::JObject>)>("onResolved");
|
|
22
|
+
method(self(), result);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
struct JOnRejectedCallback : public jni::JavaClass<JOnRejectedCallback> {
|
|
27
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/core/Promise$OnRejectedCallback;";
|
|
28
|
+
void onRejected(const jni::alias_ref<jni::JThrowable>& error) const {
|
|
29
|
+
static const auto method = javaClassLocal()->getMethod<void(jni::alias_ref<jni::JThrowable>)>("onRejected");
|
|
30
|
+
method(self(), error);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
16
34
|
/**
|
|
17
35
|
* Represents a Promise implemented in Java.
|
|
18
36
|
*/
|
|
@@ -20,9 +38,9 @@ class JPromise final : public jni::HybridClass<JPromise> {
|
|
|
20
38
|
public:
|
|
21
39
|
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/core/Promise;";
|
|
22
40
|
using OnResolvedFunc = std::function<void(jni::alias_ref<jni::JObject>)>;
|
|
23
|
-
using OnRejectedFunc = std::function<void(jni::alias_ref<jni::
|
|
41
|
+
using OnRejectedFunc = std::function<void(jni::alias_ref<jni::JThrowable>)>;
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
private:
|
|
26
44
|
/**
|
|
27
45
|
* Create a new, still unresolved `JPromise` from Java.
|
|
28
46
|
*/
|
|
@@ -30,14 +48,24 @@ public:
|
|
|
30
48
|
return makeCxxInstance();
|
|
31
49
|
}
|
|
32
50
|
|
|
51
|
+
public:
|
|
52
|
+
/**
|
|
53
|
+
* Create a new, still unresolved `JPromise` from C++.
|
|
54
|
+
*/
|
|
55
|
+
static jni::local_ref<JPromise::javaobject> create() {
|
|
56
|
+
return newObjectCxxArgs();
|
|
57
|
+
}
|
|
58
|
+
|
|
33
59
|
public:
|
|
34
60
|
void resolve(jni::alias_ref<jni::JObject> result) {
|
|
61
|
+
std::unique_lock lock(_mutex);
|
|
35
62
|
_result = jni::make_global(result);
|
|
36
63
|
for (const auto& onResolved : _onResolvedListeners) {
|
|
37
64
|
onResolved(_result);
|
|
38
65
|
}
|
|
39
66
|
}
|
|
40
|
-
void reject(jni::alias_ref<jni::
|
|
67
|
+
void reject(jni::alias_ref<jni::JThrowable> error) {
|
|
68
|
+
std::unique_lock lock(_mutex);
|
|
41
69
|
_error = jni::make_global(error);
|
|
42
70
|
for (const auto& onRejected : _onRejectedListeners) {
|
|
43
71
|
onRejected(_error);
|
|
@@ -46,6 +74,7 @@ public:
|
|
|
46
74
|
|
|
47
75
|
public:
|
|
48
76
|
void addOnResolvedListener(OnResolvedFunc&& onResolved) {
|
|
77
|
+
std::unique_lock lock(_mutex);
|
|
49
78
|
if (_result != nullptr) {
|
|
50
79
|
// Promise is already resolved! Call the callback immediately
|
|
51
80
|
onResolved(_result);
|
|
@@ -55,6 +84,7 @@ public:
|
|
|
55
84
|
}
|
|
56
85
|
}
|
|
57
86
|
void addOnRejectedListener(OnRejectedFunc&& onRejected) {
|
|
87
|
+
std::unique_lock lock(_mutex);
|
|
58
88
|
if (_error != nullptr) {
|
|
59
89
|
// Promise is already rejected! Call the callback immediately
|
|
60
90
|
onRejected(_error);
|
|
@@ -65,15 +95,40 @@ public:
|
|
|
65
95
|
}
|
|
66
96
|
|
|
67
97
|
private:
|
|
68
|
-
|
|
98
|
+
void addOnResolvedListenerJava(jni::alias_ref<JOnResolvedCallback> callback) {
|
|
99
|
+
std::unique_lock lock(_mutex);
|
|
100
|
+
if (_result != nullptr) {
|
|
101
|
+
// Promise is already resolved! Call the callback immediately
|
|
102
|
+
callback->onResolved(_result);
|
|
103
|
+
} else {
|
|
104
|
+
// Promise is not yet resolved, put the listener in our queue.
|
|
105
|
+
auto sharedCallback = jni::make_global(callback);
|
|
106
|
+
_onResolvedListeners.emplace_back([=](const auto& result) { sharedCallback->onResolved(result); });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
void addOnRejectedListenerJava(jni::alias_ref<JOnRejectedCallback> callback) {
|
|
110
|
+
std::unique_lock lock(_mutex);
|
|
111
|
+
if (_error != nullptr) {
|
|
112
|
+
// Promise is already rejected! Call the callback immediately
|
|
113
|
+
callback->onRejected(_error);
|
|
114
|
+
} else {
|
|
115
|
+
// Promise is not yet rejected, put the listener in our queue.
|
|
116
|
+
auto sharedCallback = jni::make_global(callback);
|
|
117
|
+
_onRejectedListeners.emplace_back([=](const auto& error) { sharedCallback->onRejected(error); });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private:
|
|
122
|
+
JPromise() = default;
|
|
69
123
|
|
|
70
124
|
private:
|
|
71
125
|
friend HybridBase;
|
|
72
126
|
using HybridBase::HybridBase;
|
|
73
127
|
jni::global_ref<jni::JObject> _result;
|
|
74
|
-
jni::global_ref<jni::
|
|
128
|
+
jni::global_ref<jni::JThrowable> _error;
|
|
75
129
|
std::vector<OnResolvedFunc> _onResolvedListeners;
|
|
76
130
|
std::vector<OnRejectedFunc> _onRejectedListeners;
|
|
131
|
+
std::mutex _mutex;
|
|
77
132
|
|
|
78
133
|
public:
|
|
79
134
|
static void registerNatives() {
|
|
@@ -81,6 +136,8 @@ public:
|
|
|
81
136
|
makeNativeMethod("initHybrid", JPromise::initHybrid),
|
|
82
137
|
makeNativeMethod("nativeResolve", JPromise::resolve),
|
|
83
138
|
makeNativeMethod("nativeReject", JPromise::reject),
|
|
139
|
+
makeNativeMethod("addOnResolvedListener", JPromise::addOnResolvedListenerJava),
|
|
140
|
+
makeNativeMethod("addOnRejectedListener", JPromise::addOnRejectedListenerJava),
|
|
84
141
|
});
|
|
85
142
|
}
|
|
86
143
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//
|
|
2
|
+
// JUnit.hpp
|
|
3
|
+
// react-native-nitro
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 19.11.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include "NitroLogger.hpp"
|
|
11
|
+
#include <fbjni/fbjni.h>
|
|
12
|
+
|
|
13
|
+
namespace margelo::nitro {
|
|
14
|
+
|
|
15
|
+
using namespace facebook;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Represents a `Unit` from Kotlin.
|
|
19
|
+
* This is similar to `void` for Java, but is actually an `Object`.
|
|
20
|
+
*/
|
|
21
|
+
class JUnit final {
|
|
22
|
+
public:
|
|
23
|
+
/**
|
|
24
|
+
* Gets the shared instance to `Unit`. This is always a static global.
|
|
25
|
+
*/
|
|
26
|
+
static jni::alias_ref<jni::JObject> instance() {
|
|
27
|
+
static jni::global_ref<jni::JObject> sharedInstance = nullptr;
|
|
28
|
+
if (sharedInstance == nullptr) {
|
|
29
|
+
jni::alias_ref<jni::JClass> clazz = jni::findClassStatic("java/lang/Object");
|
|
30
|
+
jni::JConstructor<jobject()> constructor = clazz->getConstructor<jobject()>();
|
|
31
|
+
jni::local_ref<jobject> instance = clazz->newObject(constructor);
|
|
32
|
+
sharedInstance = jni::make_global(instance);
|
|
33
|
+
}
|
|
34
|
+
return sharedInstance;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
} // namespace margelo::nitro
|
|
@@ -5,9 +5,11 @@ import com.facebook.jni.HybridData
|
|
|
5
5
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
6
6
|
import kotlinx.coroutines.CoroutineScope
|
|
7
7
|
import kotlinx.coroutines.Dispatchers
|
|
8
|
-
import kotlinx.coroutines.async
|
|
9
8
|
import kotlinx.coroutines.launch
|
|
10
9
|
import kotlin.concurrent.thread
|
|
10
|
+
import kotlin.coroutines.resume
|
|
11
|
+
import kotlin.coroutines.resumeWithException
|
|
12
|
+
import kotlin.coroutines.suspendCoroutine
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Represents a Promise that can be passed to JS.
|
|
@@ -23,17 +25,26 @@ import kotlin.concurrent.thread
|
|
|
23
25
|
@Keep
|
|
24
26
|
@DoNotStrip
|
|
25
27
|
class Promise<T> {
|
|
28
|
+
@Keep
|
|
29
|
+
@DoNotStrip
|
|
26
30
|
private val mHybridData: HybridData
|
|
27
31
|
|
|
28
32
|
/**
|
|
29
33
|
* Creates a new Promise with fully manual control over the `resolve(..)`/`reject(..)` functions
|
|
30
34
|
*/
|
|
31
|
-
|
|
35
|
+
constructor() {
|
|
32
36
|
mHybridData = initHybrid()
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
@Suppress("unused")
|
|
40
|
+
@Keep
|
|
41
|
+
@DoNotStrip
|
|
42
|
+
private constructor(hybridData: HybridData) {
|
|
43
|
+
mHybridData = hybridData
|
|
44
|
+
}
|
|
45
|
+
|
|
35
46
|
/**
|
|
36
|
-
* Resolves the Promise with the given result.
|
|
47
|
+
* Resolves the Promise with the given [result].
|
|
37
48
|
* Any `onResolved` listeners will be invoked.
|
|
38
49
|
*/
|
|
39
50
|
fun resolve(result: T) {
|
|
@@ -41,18 +52,73 @@ class Promise<T> {
|
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
/**
|
|
44
|
-
* Rejects the Promise with the given error.
|
|
55
|
+
* Rejects the Promise with the given [error].
|
|
45
56
|
* Any `onRejected` listeners will be invoked.
|
|
46
57
|
*/
|
|
47
|
-
fun reject(error:
|
|
48
|
-
nativeReject(error
|
|
58
|
+
fun reject(error: Throwable) {
|
|
59
|
+
nativeReject(error)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Add a continuation listener to this `Promise<T>`.
|
|
64
|
+
* Once the `Promise<T>` resolves, the [listener] will be called.
|
|
65
|
+
*/
|
|
66
|
+
fun then(listener: (result: T) -> Unit): Promise<T> {
|
|
67
|
+
addOnResolvedListener { boxedResult ->
|
|
68
|
+
@Suppress("UNCHECKED_CAST")
|
|
69
|
+
val result = boxedResult as? T ?: throw Error("Failed to cast Object to T!")
|
|
70
|
+
listener(result)
|
|
71
|
+
}
|
|
72
|
+
return this
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Add an error continuation listener to this `Promise<T>`.
|
|
77
|
+
* Once the `Promise<T>` rejects, the [listener] will be called with the error.
|
|
78
|
+
*/
|
|
79
|
+
fun catch(listener: (throwable: Throwable) -> Unit): Promise<T> {
|
|
80
|
+
addOnRejectedListener(listener)
|
|
81
|
+
return this
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Asynchronously await the result of the Promise.
|
|
86
|
+
* If the Promise is already resolved/rejected, this will continue immediately,
|
|
87
|
+
* otherwise it will asynchronously wait for a result or throw on a rejection.
|
|
88
|
+
* This function can only be used from a coroutine context.
|
|
89
|
+
*/
|
|
90
|
+
suspend fun await(): T {
|
|
91
|
+
return suspendCoroutine { continuation ->
|
|
92
|
+
then { result -> continuation.resume(result) }
|
|
93
|
+
catch { error -> continuation.resumeWithException(error) }
|
|
94
|
+
}
|
|
49
95
|
}
|
|
50
96
|
|
|
51
97
|
// C++ functions
|
|
52
98
|
private external fun nativeResolve(result: Any)
|
|
53
|
-
private external fun nativeReject(error:
|
|
99
|
+
private external fun nativeReject(error: Throwable)
|
|
100
|
+
private external fun addOnResolvedListener(callback: OnResolvedCallback)
|
|
101
|
+
private external fun addOnRejectedListener(callback: OnRejectedCallback)
|
|
54
102
|
private external fun initHybrid(): HybridData
|
|
55
103
|
|
|
104
|
+
// Nested callbacks - need to be JavaClasses so we can access them with JNI
|
|
105
|
+
@Keep
|
|
106
|
+
@DoNotStrip
|
|
107
|
+
private fun interface OnResolvedCallback {
|
|
108
|
+
@Suppress("unused")
|
|
109
|
+
@Keep
|
|
110
|
+
@DoNotStrip
|
|
111
|
+
fun onResolved(result: Any)
|
|
112
|
+
}
|
|
113
|
+
@Keep
|
|
114
|
+
@DoNotStrip
|
|
115
|
+
private fun interface OnRejectedCallback {
|
|
116
|
+
@Suppress("unused")
|
|
117
|
+
@Keep
|
|
118
|
+
@DoNotStrip
|
|
119
|
+
fun onRejected(error: Throwable)
|
|
120
|
+
}
|
|
121
|
+
|
|
56
122
|
companion object {
|
|
57
123
|
private val defaultScope = CoroutineScope(Dispatchers.Default)
|
|
58
124
|
|
|
@@ -71,7 +137,7 @@ class Promise<T> {
|
|
|
71
137
|
try {
|
|
72
138
|
val result = run()
|
|
73
139
|
promise.resolve(result)
|
|
74
|
-
} catch (e:
|
|
140
|
+
} catch (e: Throwable) {
|
|
75
141
|
promise.reject(e)
|
|
76
142
|
}
|
|
77
143
|
}
|
|
@@ -91,7 +157,7 @@ class Promise<T> {
|
|
|
91
157
|
try {
|
|
92
158
|
val result = run()
|
|
93
159
|
promise.resolve(result)
|
|
94
|
-
} catch (e:
|
|
160
|
+
} catch (e: Throwable) {
|
|
95
161
|
promise.reject(e)
|
|
96
162
|
}
|
|
97
163
|
}
|
|
@@ -108,7 +174,7 @@ class Promise<T> {
|
|
|
108
174
|
/**
|
|
109
175
|
* Creates a new Promise that is already rejected with the given error.
|
|
110
176
|
*/
|
|
111
|
-
fun <T> rejected(error:
|
|
177
|
+
fun <T> rejected(error: Throwable): Promise<T> {
|
|
112
178
|
return Promise<T>().apply { reject(error) }
|
|
113
179
|
}
|
|
114
180
|
}
|