@onekeyfe/react-native-background-thread 1.1.46 → 1.1.48
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/BackgroundThread.podspec +4 -2
- package/android/CMakeLists.txt +29 -0
- package/android/build.gradle +42 -0
- package/android/src/main/cpp/cpp-adapter.cpp +222 -0
- package/android/src/main/java/com/backgroundthread/BTLogger.kt +55 -0
- package/android/src/main/java/com/backgroundthread/BackgroundThreadManager.kt +258 -0
- package/android/src/main/java/com/backgroundthread/BackgroundThreadModule.kt +17 -17
- package/cpp/SharedRPC.cpp +223 -0
- package/cpp/SharedRPC.h +50 -0
- package/cpp/SharedStore.cpp +184 -0
- package/cpp/SharedStore.h +28 -0
- package/ios/BackgroundRunnerReactNativeDelegate.h +0 -17
- package/ios/BackgroundRunnerReactNativeDelegate.mm +83 -123
- package/ios/BackgroundThread.h +1 -0
- package/ios/BackgroundThread.mm +6 -22
- package/ios/BackgroundThreadManager.h +6 -12
- package/ios/BackgroundThreadManager.mm +39 -30
- package/lib/module/NativeBackgroundThread.js.map +1 -1
- package/lib/module/SharedRPC.js +6 -0
- package/lib/module/SharedRPC.js.map +1 -0
- package/lib/module/SharedStore.js +6 -0
- package/lib/module/SharedStore.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeBackgroundThread.d.ts +2 -4
- package/lib/typescript/src/NativeBackgroundThread.d.ts.map +1 -1
- package/lib/typescript/src/SharedRPC.d.ts +12 -0
- package/lib/typescript/src/SharedRPC.d.ts.map +1 -0
- package/lib/typescript/src/SharedStore.d.ts +14 -0
- package/lib/typescript/src/SharedStore.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/NativeBackgroundThread.ts +2 -4
- package/src/SharedRPC.ts +16 -0
- package/src/SharedStore.ts +18 -0
- package/src/index.tsx +4 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#include "SharedRPC.h"
|
|
2
|
+
|
|
3
|
+
#ifdef __ANDROID__
|
|
4
|
+
#include <android/log.h>
|
|
5
|
+
#define RPC_LOG(...) __android_log_print(ANDROID_LOG_INFO, "SharedRPC", __VA_ARGS__)
|
|
6
|
+
#else
|
|
7
|
+
#define RPC_LOG(...)
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
std::mutex SharedRPC::mutex_;
|
|
11
|
+
std::unordered_map<std::string, RPCValue> SharedRPC::slots_;
|
|
12
|
+
std::vector<RuntimeListener> SharedRPC::listeners_;
|
|
13
|
+
|
|
14
|
+
void SharedRPC::install(jsi::Runtime &rt) {
|
|
15
|
+
auto rpc = std::make_shared<SharedRPC>();
|
|
16
|
+
auto obj = jsi::Object::createFromHostObject(rt, rpc);
|
|
17
|
+
rt.global().setProperty(rt, "sharedRPC", std::move(obj));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
void SharedRPC::install(jsi::Runtime &rt, RPCRuntimeExecutor executor,
|
|
21
|
+
const std::string &runtimeId) {
|
|
22
|
+
auto rpc = std::make_shared<SharedRPC>();
|
|
23
|
+
auto obj = jsi::Object::createFromHostObject(rt, rpc);
|
|
24
|
+
rt.global().setProperty(rt, "sharedRPC", std::move(obj));
|
|
25
|
+
|
|
26
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
27
|
+
// Remove any existing listener with the same runtimeId (reload scenario).
|
|
28
|
+
// IMPORTANT: The old listener's callback is a jsi::Function tied to the old
|
|
29
|
+
// runtime. On reload, that runtime is already destroyed, so calling
|
|
30
|
+
// ~jsi::Function() would crash (null deref in Pointer::~Pointer).
|
|
31
|
+
// We intentionally leak the callback to avoid this.
|
|
32
|
+
for (auto &listener : listeners_) {
|
|
33
|
+
if (listener.runtimeId == runtimeId && listener.callback) {
|
|
34
|
+
new std::shared_ptr<jsi::Function>(std::move(listener.callback));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
listeners_.erase(
|
|
38
|
+
std::remove_if(listeners_.begin(), listeners_.end(),
|
|
39
|
+
[&runtimeId](const RuntimeListener &l) {
|
|
40
|
+
return l.runtimeId == runtimeId;
|
|
41
|
+
}),
|
|
42
|
+
listeners_.end());
|
|
43
|
+
listeners_.push_back({runtimeId, &rt, std::move(executor), nullptr});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void SharedRPC::reset() {
|
|
47
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
48
|
+
slots_.clear();
|
|
49
|
+
listeners_.clear();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void SharedRPC::notifyOtherRuntime(jsi::Runtime &callerRt, const std::string &callId) {
|
|
53
|
+
// Collect executors and callbacks under lock, then invoke outside lock
|
|
54
|
+
// to avoid deadlock (executor may schedule work that also acquires mutex_).
|
|
55
|
+
std::vector<std::pair<RPCRuntimeExecutor, std::shared_ptr<jsi::Function>>> toNotify;
|
|
56
|
+
{
|
|
57
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
58
|
+
RPC_LOG("notifyOtherRuntime: callId=%s, listeners=%zu, callerRt=%p",
|
|
59
|
+
callId.c_str(), listeners_.size(), &callerRt);
|
|
60
|
+
for (auto &listener : listeners_) {
|
|
61
|
+
RPC_LOG(" listener: id=%s, rt=%p, hasCallback=%d",
|
|
62
|
+
listener.runtimeId.c_str(), listener.runtime, listener.callback != nullptr);
|
|
63
|
+
if (listener.runtime == &callerRt) continue;
|
|
64
|
+
if (!listener.callback) continue;
|
|
65
|
+
toNotify.emplace_back(listener.executor, listener.callback);
|
|
66
|
+
}
|
|
67
|
+
RPC_LOG(" toNotify count: %zu", toNotify.size());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (auto &[executor, cb] : toNotify) {
|
|
71
|
+
auto id = callId;
|
|
72
|
+
RPC_LOG(" invoking executor for callId=%s", id.c_str());
|
|
73
|
+
executor([cb, id](jsi::Runtime &rt) {
|
|
74
|
+
RPC_LOG(" executor work running for callId=%s", id.c_str());
|
|
75
|
+
try {
|
|
76
|
+
cb->call(rt, jsi::String::createFromUtf8(rt, id));
|
|
77
|
+
RPC_LOG(" cb->call succeeded for callId=%s", id.c_str());
|
|
78
|
+
} catch (const jsi::JSError &e) {
|
|
79
|
+
RPC_LOG(" JSError in cb->call: %s", e.getMessage().c_str());
|
|
80
|
+
} catch (...) {
|
|
81
|
+
RPC_LOG(" Unknown error in cb->call");
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
RPCValue SharedRPC::extractValue(jsi::Runtime &rt, const jsi::Value &val) {
|
|
88
|
+
if (val.isBool()) {
|
|
89
|
+
return val.getBool();
|
|
90
|
+
}
|
|
91
|
+
if (val.isNumber()) {
|
|
92
|
+
return val.getNumber();
|
|
93
|
+
}
|
|
94
|
+
if (val.isString()) {
|
|
95
|
+
return val.getString(rt).utf8(rt);
|
|
96
|
+
}
|
|
97
|
+
throw jsi::JSError(rt,
|
|
98
|
+
"SharedRPC: unsupported value type. "
|
|
99
|
+
"Only bool, number, and string are supported.");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
jsi::Value SharedRPC::toJSI(jsi::Runtime &rt, const RPCValue &val) {
|
|
103
|
+
if (std::holds_alternative<bool>(val)) {
|
|
104
|
+
return jsi::Value(std::get<bool>(val));
|
|
105
|
+
}
|
|
106
|
+
if (std::holds_alternative<double>(val)) {
|
|
107
|
+
return jsi::Value(std::get<double>(val));
|
|
108
|
+
}
|
|
109
|
+
// std::string
|
|
110
|
+
return jsi::String::createFromUtf8(rt, std::get<std::string>(val));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
jsi::Value SharedRPC::get(jsi::Runtime &rt, const jsi::PropNameID &name) {
|
|
114
|
+
auto prop = name.utf8(rt);
|
|
115
|
+
|
|
116
|
+
// write(callId: string, value: bool | number | string): void
|
|
117
|
+
if (prop == "write") {
|
|
118
|
+
return jsi::Function::createFromHostFunction(
|
|
119
|
+
rt, name, 2,
|
|
120
|
+
[this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args,
|
|
121
|
+
size_t count) -> jsi::Value {
|
|
122
|
+
if (count < 2 || !args[0].isString()) {
|
|
123
|
+
throw jsi::JSError(
|
|
124
|
+
rt, "SharedRPC.write expects (callId: string, value)");
|
|
125
|
+
}
|
|
126
|
+
auto callId = args[0].getString(rt).utf8(rt);
|
|
127
|
+
auto value = extractValue(rt, args[1]);
|
|
128
|
+
{
|
|
129
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
130
|
+
slots_.insert_or_assign(callId, std::move(value));
|
|
131
|
+
}
|
|
132
|
+
// Notify OUTSIDE the lock
|
|
133
|
+
notifyOtherRuntime(rt, callId);
|
|
134
|
+
return jsi::Value::undefined();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// read(callId: string): bool | number | string | undefined
|
|
139
|
+
// Deletes the entry after reading (read-and-delete semantics).
|
|
140
|
+
if (prop == "read") {
|
|
141
|
+
return jsi::Function::createFromHostFunction(
|
|
142
|
+
rt, name, 1,
|
|
143
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args,
|
|
144
|
+
size_t count) -> jsi::Value {
|
|
145
|
+
if (count < 1 || !args[0].isString()) {
|
|
146
|
+
throw jsi::JSError(
|
|
147
|
+
rt, "SharedRPC.read expects (callId: string)");
|
|
148
|
+
}
|
|
149
|
+
auto callId = args[0].getString(rt).utf8(rt);
|
|
150
|
+
{
|
|
151
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
152
|
+
auto it = slots_.find(callId);
|
|
153
|
+
if (it == slots_.end()) {
|
|
154
|
+
return jsi::Value::undefined();
|
|
155
|
+
}
|
|
156
|
+
auto value = std::move(it->second);
|
|
157
|
+
slots_.erase(it);
|
|
158
|
+
return toJSI(rt, value);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// has(callId: string): boolean
|
|
164
|
+
if (prop == "has") {
|
|
165
|
+
return jsi::Function::createFromHostFunction(
|
|
166
|
+
rt, name, 1,
|
|
167
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args,
|
|
168
|
+
size_t count) -> jsi::Value {
|
|
169
|
+
if (count < 1 || !args[0].isString()) {
|
|
170
|
+
throw jsi::JSError(
|
|
171
|
+
rt, "SharedRPC.has expects (callId: string)");
|
|
172
|
+
}
|
|
173
|
+
auto callId = args[0].getString(rt).utf8(rt);
|
|
174
|
+
{
|
|
175
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
176
|
+
return jsi::Value(slots_.count(callId) > 0);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// pendingCount: number (getter, not a function)
|
|
182
|
+
if (prop == "pendingCount") {
|
|
183
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
184
|
+
return jsi::Value(static_cast<double>(slots_.size()));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// onWrite(callback: (callId: string) => void): void
|
|
188
|
+
if (prop == "onWrite") {
|
|
189
|
+
return jsi::Function::createFromHostFunction(
|
|
190
|
+
rt, name, 1,
|
|
191
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args,
|
|
192
|
+
size_t count) -> jsi::Value {
|
|
193
|
+
if (count < 1 || !args[0].isObject() ||
|
|
194
|
+
!args[0].asObject(rt).isFunction(rt)) {
|
|
195
|
+
throw jsi::JSError(rt, "SharedRPC.onWrite expects a function");
|
|
196
|
+
}
|
|
197
|
+
auto fn = std::make_shared<jsi::Function>(
|
|
198
|
+
args[0].asObject(rt).asFunction(rt));
|
|
199
|
+
{
|
|
200
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
201
|
+
for (auto &listener : listeners_) {
|
|
202
|
+
if (listener.runtime == &rt) {
|
|
203
|
+
listener.callback = std::move(fn);
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return jsi::Value::undefined();
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return jsi::Value::undefined();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
std::vector<jsi::PropNameID> SharedRPC::getPropertyNames(jsi::Runtime &rt) {
|
|
216
|
+
std::vector<jsi::PropNameID> props;
|
|
217
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "write"));
|
|
218
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "read"));
|
|
219
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "has"));
|
|
220
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "pendingCount"));
|
|
221
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "onWrite"));
|
|
222
|
+
return props;
|
|
223
|
+
}
|
package/cpp/SharedRPC.h
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <jsi/jsi.h>
|
|
4
|
+
#include <functional>
|
|
5
|
+
#include <memory>
|
|
6
|
+
#include <mutex>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <unordered_map>
|
|
9
|
+
#include <variant>
|
|
10
|
+
#include <vector>
|
|
11
|
+
|
|
12
|
+
namespace jsi = facebook::jsi;
|
|
13
|
+
|
|
14
|
+
using RPCValue = std::variant<bool, double, std::string>;
|
|
15
|
+
|
|
16
|
+
// Executes a callback on a specific runtime's JS thread.
|
|
17
|
+
// The implementation is platform-specific (iOS vs Android).
|
|
18
|
+
using RPCRuntimeExecutor = std::function<void(std::function<void(jsi::Runtime &)>)>;
|
|
19
|
+
|
|
20
|
+
struct RuntimeListener {
|
|
21
|
+
std::string runtimeId; // "main" or "background"
|
|
22
|
+
jsi::Runtime *runtime;
|
|
23
|
+
RPCRuntimeExecutor executor;
|
|
24
|
+
std::shared_ptr<jsi::Function> callback; // JS onWrite callback
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
class SharedRPC : public jsi::HostObject {
|
|
28
|
+
public:
|
|
29
|
+
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &name) override;
|
|
30
|
+
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
|
|
31
|
+
|
|
32
|
+
/// Legacy install without executor (no cross-runtime notification)
|
|
33
|
+
static void install(jsi::Runtime &rt);
|
|
34
|
+
|
|
35
|
+
/// Install with executor — enables cross-runtime write notifications
|
|
36
|
+
/// runtimeId should be "main" or "background" — used for dedup on reload.
|
|
37
|
+
static void install(jsi::Runtime &rt, RPCRuntimeExecutor executor,
|
|
38
|
+
const std::string &runtimeId);
|
|
39
|
+
|
|
40
|
+
static void reset();
|
|
41
|
+
|
|
42
|
+
private:
|
|
43
|
+
static RPCValue extractValue(jsi::Runtime &rt, const jsi::Value &val);
|
|
44
|
+
static jsi::Value toJSI(jsi::Runtime &rt, const RPCValue &val);
|
|
45
|
+
void notifyOtherRuntime(jsi::Runtime &callerRt, const std::string &callId);
|
|
46
|
+
|
|
47
|
+
static std::mutex mutex_;
|
|
48
|
+
static std::unordered_map<std::string, RPCValue> slots_;
|
|
49
|
+
static std::vector<RuntimeListener> listeners_;
|
|
50
|
+
};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#include "SharedStore.h"
|
|
2
|
+
|
|
3
|
+
// Static member definitions
|
|
4
|
+
std::mutex SharedStore::mutex_;
|
|
5
|
+
std::unordered_map<std::string, StoreValue> SharedStore::data_;
|
|
6
|
+
|
|
7
|
+
void SharedStore::install(jsi::Runtime &rt) {
|
|
8
|
+
auto store = std::make_shared<SharedStore>();
|
|
9
|
+
auto obj = jsi::Object::createFromHostObject(rt, store);
|
|
10
|
+
rt.global().setProperty(rt, "sharedStore", std::move(obj));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
void SharedStore::reset() {
|
|
14
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
15
|
+
data_.clear();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
StoreValue SharedStore::extractValue(jsi::Runtime &rt,
|
|
19
|
+
const jsi::Value &val) {
|
|
20
|
+
if (val.isBool()) {
|
|
21
|
+
return val.getBool();
|
|
22
|
+
}
|
|
23
|
+
if (val.isNumber()) {
|
|
24
|
+
return val.getNumber();
|
|
25
|
+
}
|
|
26
|
+
if (val.isString()) {
|
|
27
|
+
return val.getString(rt).utf8(rt);
|
|
28
|
+
}
|
|
29
|
+
throw jsi::JSError(rt,
|
|
30
|
+
"SharedStore: unsupported value type. "
|
|
31
|
+
"Only bool, number, and string are supported.");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
jsi::Value SharedStore::toJSI(jsi::Runtime &rt, const StoreValue &val) {
|
|
35
|
+
if (std::holds_alternative<bool>(val)) {
|
|
36
|
+
return jsi::Value(std::get<bool>(val));
|
|
37
|
+
}
|
|
38
|
+
if (std::holds_alternative<double>(val)) {
|
|
39
|
+
return jsi::Value(std::get<double>(val));
|
|
40
|
+
}
|
|
41
|
+
if (std::holds_alternative<std::string>(val)) {
|
|
42
|
+
return jsi::String::createFromUtf8(rt, std::get<std::string>(val));
|
|
43
|
+
}
|
|
44
|
+
return jsi::Value::undefined();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
jsi::Value SharedStore::get(jsi::Runtime &rt, const jsi::PropNameID &name) {
|
|
48
|
+
auto prop = name.utf8(rt);
|
|
49
|
+
|
|
50
|
+
// set(key: string, value: bool | number | string): void
|
|
51
|
+
if (prop == "set") {
|
|
52
|
+
return jsi::Function::createFromHostFunction(
|
|
53
|
+
rt, name, 2,
|
|
54
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args,
|
|
55
|
+
size_t count) -> jsi::Value {
|
|
56
|
+
if (count < 2 || !args[0].isString()) {
|
|
57
|
+
throw jsi::JSError(
|
|
58
|
+
rt, "SharedStore.set expects (string key, value)");
|
|
59
|
+
}
|
|
60
|
+
auto key = args[0].getString(rt).utf8(rt);
|
|
61
|
+
auto val = extractValue(rt, args[1]);
|
|
62
|
+
{
|
|
63
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
64
|
+
data_[std::move(key)] = std::move(val);
|
|
65
|
+
}
|
|
66
|
+
return jsi::Value::undefined();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// get(key: string): bool | number | string | undefined
|
|
71
|
+
if (prop == "get") {
|
|
72
|
+
return jsi::Function::createFromHostFunction(
|
|
73
|
+
rt, name, 1,
|
|
74
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args,
|
|
75
|
+
size_t count) -> jsi::Value {
|
|
76
|
+
if (count < 1 || !args[0].isString()) {
|
|
77
|
+
throw jsi::JSError(
|
|
78
|
+
rt, "SharedStore.get expects a string key");
|
|
79
|
+
}
|
|
80
|
+
auto key = args[0].getString(rt).utf8(rt);
|
|
81
|
+
{
|
|
82
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
83
|
+
auto it = data_.find(key);
|
|
84
|
+
if (it == data_.end()) {
|
|
85
|
+
return jsi::Value::undefined();
|
|
86
|
+
}
|
|
87
|
+
return toJSI(rt, it->second);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// has(key: string): boolean
|
|
93
|
+
if (prop == "has") {
|
|
94
|
+
return jsi::Function::createFromHostFunction(
|
|
95
|
+
rt, name, 1,
|
|
96
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args,
|
|
97
|
+
size_t count) -> jsi::Value {
|
|
98
|
+
if (count < 1 || !args[0].isString()) {
|
|
99
|
+
throw jsi::JSError(
|
|
100
|
+
rt, "SharedStore.has expects a string key");
|
|
101
|
+
}
|
|
102
|
+
auto key = args[0].getString(rt).utf8(rt);
|
|
103
|
+
{
|
|
104
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
105
|
+
return jsi::Value(data_.count(key) > 0);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// delete(key: string): boolean
|
|
111
|
+
if (prop == "delete") {
|
|
112
|
+
return jsi::Function::createFromHostFunction(
|
|
113
|
+
rt, name, 1,
|
|
114
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args,
|
|
115
|
+
size_t count) -> jsi::Value {
|
|
116
|
+
if (count < 1 || !args[0].isString()) {
|
|
117
|
+
throw jsi::JSError(
|
|
118
|
+
rt, "SharedStore.delete expects a string key");
|
|
119
|
+
}
|
|
120
|
+
auto key = args[0].getString(rt).utf8(rt);
|
|
121
|
+
{
|
|
122
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
123
|
+
return jsi::Value(data_.erase(key) > 0);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// keys(): string[]
|
|
129
|
+
if (prop == "keys") {
|
|
130
|
+
return jsi::Function::createFromHostFunction(
|
|
131
|
+
rt, name, 0,
|
|
132
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *,
|
|
133
|
+
size_t) -> jsi::Value {
|
|
134
|
+
std::vector<std::string> allKeys;
|
|
135
|
+
{
|
|
136
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
137
|
+
allKeys.reserve(data_.size());
|
|
138
|
+
for (const auto &pair : data_) {
|
|
139
|
+
allKeys.push_back(pair.first);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
auto arr = jsi::Array(rt, allKeys.size());
|
|
143
|
+
for (size_t i = 0; i < allKeys.size(); i++) {
|
|
144
|
+
arr.setValueAtIndex(
|
|
145
|
+
rt, i, jsi::String::createFromUtf8(rt, allKeys[i]));
|
|
146
|
+
}
|
|
147
|
+
return arr;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// clear(): void
|
|
152
|
+
if (prop == "clear") {
|
|
153
|
+
return jsi::Function::createFromHostFunction(
|
|
154
|
+
rt, name, 0,
|
|
155
|
+
[](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *,
|
|
156
|
+
size_t) -> jsi::Value {
|
|
157
|
+
{
|
|
158
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
159
|
+
data_.clear();
|
|
160
|
+
}
|
|
161
|
+
return jsi::Value::undefined();
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// size: number (getter, not a function)
|
|
166
|
+
if (prop == "size") {
|
|
167
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
168
|
+
return jsi::Value(static_cast<double>(data_.size()));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return jsi::Value::undefined();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
std::vector<jsi::PropNameID> SharedStore::getPropertyNames(jsi::Runtime &rt) {
|
|
175
|
+
std::vector<jsi::PropNameID> props;
|
|
176
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "set"));
|
|
177
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "get"));
|
|
178
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "has"));
|
|
179
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "delete"));
|
|
180
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "keys"));
|
|
181
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "clear"));
|
|
182
|
+
props.push_back(jsi::PropNameID::forUtf8(rt, "size"));
|
|
183
|
+
return props;
|
|
184
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <jsi/jsi.h>
|
|
4
|
+
#include <mutex>
|
|
5
|
+
#include <string>
|
|
6
|
+
#include <unordered_map>
|
|
7
|
+
#include <variant>
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
namespace jsi = facebook::jsi;
|
|
11
|
+
|
|
12
|
+
using StoreValue = std::variant<bool, double, std::string>;
|
|
13
|
+
|
|
14
|
+
class SharedStore : public jsi::HostObject {
|
|
15
|
+
public:
|
|
16
|
+
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &name) override;
|
|
17
|
+
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
|
|
18
|
+
|
|
19
|
+
static void install(jsi::Runtime &rt);
|
|
20
|
+
static void reset();
|
|
21
|
+
|
|
22
|
+
private:
|
|
23
|
+
static StoreValue extractValue(jsi::Runtime &rt, const jsi::Value &val);
|
|
24
|
+
static jsi::Value toJSI(jsi::Runtime &rt, const StoreValue &val);
|
|
25
|
+
|
|
26
|
+
static std::mutex mutex_;
|
|
27
|
+
static std::unordered_map<std::string, StoreValue> data_;
|
|
28
|
+
};
|
|
@@ -18,7 +18,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
18
18
|
|
|
19
19
|
@interface BackgroundReactNativeDelegate : RCTDefaultReactNativeFactoryDelegate
|
|
20
20
|
|
|
21
|
-
//@property (nonatomic) std::shared_ptr<const facebook::react::SandboxReactNativeViewEventEmitter> eventEmitter;
|
|
22
21
|
@property (nonatomic, assign) BOOL hasOnMessageHandler;
|
|
23
22
|
@property (nonatomic, assign) BOOL hasOnErrorHandler;
|
|
24
23
|
|
|
@@ -26,28 +25,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
26
25
|
|
|
27
26
|
@property (nonatomic, readwrite) std::string origin;
|
|
28
27
|
|
|
29
|
-
@property (nonatomic, copy) void (^onMessageCallback)(NSString *message);
|
|
30
|
-
|
|
31
28
|
/**
|
|
32
29
|
* Initializes the delegate.
|
|
33
30
|
* @return Initialized delegate instance with filtered module access
|
|
34
31
|
*/
|
|
35
32
|
- (instancetype)init;
|
|
36
33
|
|
|
37
|
-
/**
|
|
38
|
-
* Posts a message to the JavaScript runtime.
|
|
39
|
-
* @param message C++ string containing the JSON.stringified message
|
|
40
|
-
*/
|
|
41
|
-
- (void)postMessage:(const std::string &)message;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Routes a message to a specific sandbox delegate.
|
|
45
|
-
* @param message The message to route
|
|
46
|
-
* @param targetId The ID of the target sandbox
|
|
47
|
-
* @return true if the message was successfully routed, false otherwise
|
|
48
|
-
*/
|
|
49
|
-
- (bool)routeMessage:(const std::string &)message toSandbox:(const std::string &)targetId;
|
|
50
|
-
|
|
51
34
|
@end
|
|
52
35
|
|
|
53
36
|
NS_ASSUME_NONNULL_END
|