react-native-nitro-modules 0.17.0 → 0.18.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.
@@ -12,7 +12,12 @@ Pod::Spec.new do |s|
12
12
  s.license = package["license"]
13
13
  s.authors = package["author"]
14
14
 
15
- s.platforms = { :ios => min_ios_version_supported, :visionos => 1.0 }
15
+ s.platforms = {
16
+ :ios => min_ios_version_supported,
17
+ :visionos => 1.0,
18
+ :macos => 10.13,
19
+ :tvos => 13.4,
20
+ }
16
21
  s.source = { :git => "https://github.com/mrousavy/nitro.git", :tag => "#{s.version}" }
17
22
 
18
23
  # VisionCamera Core C++ bindings
@@ -4,6 +4,8 @@
4
4
 
5
5
  #pragma once
6
6
 
7
+ #include "AssertPromiseState.hpp"
8
+ #include "NitroDefines.hpp"
7
9
  #include "ThreadPool.hpp"
8
10
  #include "TypeInfo.hpp"
9
11
  #include <exception>
@@ -17,14 +19,14 @@ namespace margelo::nitro {
17
19
 
18
20
  using namespace facebook;
19
21
 
20
- template <typename TResult, typename TError = std::exception>
22
+ template <typename TResult>
21
23
  class Promise final {
22
24
  public:
23
25
  using OnResolvedFunc = std::function<void(const TResult&)>;
24
- using OnRejectedFunc = std::function<void(const TError&)>;
26
+ using OnRejectedFunc = std::function<void(const std::exception_ptr&)>;
25
27
 
26
28
  public:
27
- // Promise cannot be deleted.
29
+ // Promise cannot be copied.
28
30
  Promise(const Promise&) = delete;
29
31
  // Promise can be moved.
30
32
  Promise(Promise&&) = default;
@@ -53,13 +55,9 @@ public:
53
55
  // Run the code, then resolve.
54
56
  TResult result = run();
55
57
  promise->resolve(std::move(result));
56
- } catch (const TError& exception) {
57
- // It threw an std::exception.
58
- promise->reject(exception);
59
58
  } catch (...) {
60
- // It threw a different error.
61
- std::string name = TypeInfo::getCurrentExceptionName();
62
- promise->reject(std::runtime_error("Unknown non-std error! Name: " + name));
59
+ // It threw an error.
60
+ promise->reject(std::current_exception());
63
61
  }
64
62
  });
65
63
  return promise;
@@ -85,9 +83,9 @@ public:
85
83
  /**
86
84
  * Creates an immediately rejected Promise.
87
85
  */
88
- static std::shared_ptr<Promise> rejected(TError&& error) {
86
+ static std::shared_ptr<Promise> rejected(const std::exception_ptr& error) {
89
87
  auto promise = create();
90
- promise->reject(std::move(error));
88
+ promise->reject(error);
91
89
  return promise;
92
90
  }
93
91
 
@@ -97,33 +95,45 @@ public:
97
95
  */
98
96
  void resolve(TResult&& result) {
99
97
  std::unique_lock lock(*_mutex);
100
- _result = std::move(result);
98
+ #ifdef NITRO_DEBUG
99
+ assertPromiseState(*this, PromiseTask::WANTS_TO_RESOLVE);
100
+ #endif
101
+ _state = std::move(result);
101
102
  for (const auto& onResolved : _onResolvedListeners) {
102
- onResolved(std::get<TResult>(_result));
103
+ onResolved(std::get<TResult>(_state));
103
104
  }
104
105
  }
105
106
  void resolve(const TResult& result) {
106
107
  std::unique_lock lock(*_mutex);
107
- _result = result;
108
+ #ifdef NITRO_DEBUG
109
+ assertPromiseState(*this, PromiseTask::WANTS_TO_RESOLVE);
110
+ #endif
111
+ _state = result;
108
112
  for (const auto& onResolved : _onResolvedListeners) {
109
- onResolved(std::get<TResult>(_result));
113
+ onResolved(std::get<TResult>(_state));
110
114
  }
111
115
  }
112
116
  /**
113
117
  * Rejects this Promise with the given error, and calls any pending listeners.
114
118
  */
115
- void reject(TError&& exception) {
119
+ void reject(const std::exception& exception) {
116
120
  std::unique_lock lock(*_mutex);
117
- _result = std::move(exception);
121
+ #ifdef NITRO_DEBUG
122
+ assertPromiseState(*this, PromiseTask::WANTS_TO_REJECT);
123
+ #endif
124
+ _state = std::make_exception_ptr(exception);
118
125
  for (const auto& onRejected : _onRejectedListeners) {
119
- onRejected(std::get<TError>(_result));
126
+ onRejected(std::get<std::exception_ptr>(_state));
120
127
  }
121
128
  }
122
- void reject(const TError& exception) {
129
+ void reject(const std::exception_ptr& exception) {
123
130
  std::unique_lock lock(*_mutex);
124
- _result = exception;
131
+ #ifdef NITRO_DEBUG
132
+ assertPromiseState(*this, PromiseTask::WANTS_TO_REJECT);
133
+ #endif
134
+ _state = exception;
125
135
  for (const auto& onRejected : _onRejectedListeners) {
126
- onRejected(std::get<TError>(_result));
136
+ onRejected(exception);
127
137
  }
128
138
  }
129
139
 
@@ -134,9 +144,9 @@ public:
134
144
  */
135
145
  void addOnResolvedListener(OnResolvedFunc&& onResolved) {
136
146
  std::unique_lock lock(*_mutex);
137
- if (std::holds_alternative<TResult>(_result)) {
147
+ if (std::holds_alternative<TResult>(_state)) {
138
148
  // Promise is already resolved! Call the callback immediately
139
- onResolved(std::get<TResult>(_result));
149
+ onResolved(std::get<TResult>(_state));
140
150
  } else {
141
151
  // Promise is not yet resolved, put the listener in our queue.
142
152
  _onResolvedListeners.push_back(std::move(onResolved));
@@ -144,9 +154,9 @@ public:
144
154
  }
145
155
  void addOnResolvedListener(const OnResolvedFunc& onResolved) {
146
156
  std::unique_lock lock(*_mutex);
147
- if (std::holds_alternative<TResult>(_result)) {
157
+ if (std::holds_alternative<TResult>(_state)) {
148
158
  // Promise is already resolved! Call the callback immediately
149
- onResolved(std::get<TResult>(_result));
159
+ onResolved(std::get<TResult>(_state));
150
160
  } else {
151
161
  // Promise is not yet resolved, put the listener in our queue.
152
162
  _onResolvedListeners.push_back(onResolved);
@@ -154,9 +164,9 @@ public:
154
164
  }
155
165
  void addOnResolvedListenerCopy(const std::function<void(TResult)>& onResolved) {
156
166
  std::unique_lock lock(*_mutex);
157
- if (std::holds_alternative<TResult>(_result)) {
167
+ if (std::holds_alternative<TResult>(_state)) {
158
168
  // Promise is already resolved! Call the callback immediately
159
- onResolved(std::get<TResult>(_result));
169
+ onResolved(std::get<TResult>(_state));
160
170
  } else {
161
171
  // Promise is not yet resolved, put the listener in our queue.
162
172
  _onResolvedListeners.push_back(onResolved);
@@ -169,9 +179,9 @@ public:
169
179
  */
170
180
  void addOnRejectedListener(OnRejectedFunc&& onRejected) {
171
181
  std::unique_lock lock(*_mutex);
172
- if (std::holds_alternative<TError>(_result)) {
182
+ if (std::holds_alternative<std::exception_ptr>(_state)) {
173
183
  // Promise is already rejected! Call the callback immediately
174
- onRejected(std::get<TError>(_result));
184
+ onRejected(std::get<std::exception_ptr>(_state));
175
185
  } else {
176
186
  // Promise is not yet rejected, put the listener in our queue.
177
187
  _onRejectedListeners.push_back(std::move(onRejected));
@@ -179,15 +189,26 @@ public:
179
189
  }
180
190
  void addOnRejectedListener(const OnRejectedFunc& onRejected) {
181
191
  std::unique_lock lock(*_mutex);
182
- if (std::holds_alternative<TError>(_result)) {
192
+ if (std::holds_alternative<std::exception_ptr>(_state)) {
183
193
  // Promise is already rejected! Call the callback immediately
184
- onRejected(std::get<TError>(_result));
194
+ onRejected(std::get<std::exception_ptr>(_state));
185
195
  } else {
186
196
  // Promise is not yet rejected, put the listener in our queue.
187
197
  _onRejectedListeners.push_back(onRejected);
188
198
  }
189
199
  }
190
200
 
201
+ public:
202
+ /**
203
+ * Gets an awaitable `std::future<T>` for this `Promise<T>`.
204
+ */
205
+ std::future<TResult> await() {
206
+ auto promise = std::make_shared<std::promise<TResult>>();
207
+ addOnResolvedListener([promise](const TResult& result) { promise->set_value(result); });
208
+ addOnRejectedListener([promise](const std::exception_ptr& error) { promise->set_exception(error); });
209
+ return promise->get_future();
210
+ }
211
+
191
212
  public:
192
213
  /**
193
214
  * Get the result of the Promise if it has been resolved.
@@ -197,17 +218,17 @@ public:
197
218
  if (!isResolved()) {
198
219
  throw std::runtime_error("Cannot get result when Promise is not yet resolved!");
199
220
  }
200
- return std::get<TResult>(_result);
221
+ return std::get<TResult>(_state);
201
222
  }
202
223
  /**
203
224
  * Get the error of the Promise if it has been rejected.
204
225
  * If the Promise is not rejected, this will throw.
205
226
  */
206
- inline const TError& getError() {
227
+ inline const std::exception_ptr& getError() {
207
228
  if (!isRejected()) {
208
229
  throw std::runtime_error("Cannot get error when Promise is not yet rejected!");
209
230
  }
210
- return std::get<TError>(_result);
231
+ return std::get<std::exception_ptr>(_state);
211
232
  }
212
233
 
213
234
  public:
@@ -216,36 +237,36 @@ public:
216
237
  */
217
238
  [[nodiscard]]
218
239
  inline bool isResolved() const noexcept {
219
- return std::holds_alternative<TResult>(_result);
240
+ return std::holds_alternative<TResult>(_state);
220
241
  }
221
242
  /**
222
243
  * Gets whether this Promise has been rejected with an error, or not.
223
244
  */
224
245
  [[nodiscard]]
225
246
  inline bool isRejected() const noexcept {
226
- return std::holds_alternative<TError>(_result);
247
+ return std::holds_alternative<std::exception_ptr>(_state);
227
248
  }
228
249
  /**
229
250
  * Gets whether this Promise has not yet been resolved nor rejected.
230
251
  */
231
252
  [[nodiscard]]
232
253
  inline bool isPending() const noexcept {
233
- return std::holds_alternative<std::monostate>(_result);
254
+ return std::holds_alternative<std::monostate>(_state);
234
255
  }
235
256
 
236
257
  private:
237
- std::variant<std::monostate, TResult, TError> _result;
258
+ std::variant<std::monostate, TResult, std::exception_ptr> _state;
238
259
  std::vector<OnResolvedFunc> _onResolvedListeners;
239
260
  std::vector<OnRejectedFunc> _onRejectedListeners;
240
261
  std::unique_ptr<std::mutex> _mutex;
241
262
  };
242
263
 
243
264
  // Specialization for void
244
- template <typename TError>
245
- class Promise<void, TError> final {
265
+ template <>
266
+ class Promise<void> final {
246
267
  public:
247
268
  using OnResolvedFunc = std::function<void()>;
248
- using OnRejectedFunc = std::function<void(const TError&)>;
269
+ using OnRejectedFunc = std::function<void(const std::exception_ptr&)>;
249
270
 
250
271
  public:
251
272
  Promise(const Promise&) = delete;
@@ -268,13 +289,9 @@ public:
268
289
  // Run the code, then resolve.
269
290
  run();
270
291
  promise->resolve();
271
- } catch (const TError& exception) {
272
- // It threw an std::exception.
273
- promise->reject(exception);
274
292
  } catch (...) {
275
- // It threw a different error.
276
- std::string name = TypeInfo::getCurrentExceptionName();
277
- promise->reject(std::runtime_error("Unknown non-std error! Name: " + name));
293
+ // It threw an error.
294
+ promise->reject(std::current_exception());
278
295
  }
279
296
  });
280
297
  return promise;
@@ -290,32 +307,41 @@ public:
290
307
  promise->resolve();
291
308
  return promise;
292
309
  }
293
- static std::shared_ptr<Promise> rejected(TError&& error) {
310
+ static std::shared_ptr<Promise> rejected(const std::exception_ptr& error) {
294
311
  auto promise = create();
295
- promise->reject(std::move(error));
312
+ promise->reject(error);
296
313
  return promise;
297
314
  }
298
315
 
299
316
  public:
300
317
  void resolve() {
301
318
  std::unique_lock lock(*_mutex);
319
+ #ifdef NITRO_DEBUG
320
+ assertPromiseState(*this, PromiseTask::WANTS_TO_RESOLVE);
321
+ #endif
302
322
  _isResolved = true;
303
323
  for (const auto& onResolved : _onResolvedListeners) {
304
324
  onResolved();
305
325
  }
306
326
  }
307
- void reject(TError&& exception) {
327
+ void reject(const std::exception& exception) {
308
328
  std::unique_lock lock(*_mutex);
309
- _error = std::move(exception);
329
+ #ifdef NITRO_DEBUG
330
+ assertPromiseState(*this, PromiseTask::WANTS_TO_REJECT);
331
+ #endif
332
+ _error = std::make_exception_ptr(exception);
310
333
  for (const auto& onRejected : _onRejectedListeners) {
311
334
  onRejected(_error.value());
312
335
  }
313
336
  }
314
- void reject(const TError& exception) {
337
+ void reject(const std::exception_ptr& exception) {
315
338
  std::unique_lock lock(*_mutex);
339
+ #ifdef NITRO_DEBUG
340
+ assertPromiseState(*this, PromiseTask::WANTS_TO_REJECT);
341
+ #endif
316
342
  _error = exception;
317
343
  for (const auto& onRejected : _onRejectedListeners) {
318
- onRejected(_error.value());
344
+ onRejected(exception);
319
345
  }
320
346
  }
321
347
 
@@ -356,7 +382,15 @@ public:
356
382
  }
357
383
 
358
384
  public:
359
- inline const TError& getError() {
385
+ std::future<void> await() {
386
+ auto promise = std::make_shared<std::promise<void>>();
387
+ addOnResolvedListener([promise]() { promise->set_value(); });
388
+ addOnRejectedListener([promise](const std::exception_ptr& error) { promise->set_exception(error); });
389
+ return promise->get_future();
390
+ }
391
+
392
+ public:
393
+ inline const std::exception_ptr& getError() {
360
394
  if (!isRejected()) {
361
395
  throw std::runtime_error("Cannot get error when Promise is not yet rejected!");
362
396
  }
@@ -380,7 +414,7 @@ public:
380
414
  private:
381
415
  std::unique_ptr<std::mutex> _mutex;
382
416
  bool _isResolved = false;
383
- std::optional<TError> _error;
417
+ std::optional<std::exception_ptr> _error;
384
418
  std::vector<OnResolvedFunc> _onResolvedListeners;
385
419
  std::vector<OnRejectedFunc> _onRejectedListeners;
386
420
  };
@@ -12,6 +12,7 @@ struct JSIConverter;
12
12
 
13
13
  #include "JSIConverter.hpp"
14
14
 
15
+ #include "TypeInfo.hpp"
15
16
  #include <exception>
16
17
  #include <jsi/jsi.h>
17
18
 
@@ -19,18 +20,27 @@ namespace margelo::nitro {
19
20
 
20
21
  using namespace facebook;
21
22
 
22
- // std::exception <> Error
23
+ // std::exception_ptr <> Error
23
24
  template <>
24
- struct JSIConverter<std::exception> final {
25
- static inline std::exception fromJSI(jsi::Runtime& runtime, const jsi::Value& error) {
25
+ struct JSIConverter<std::exception_ptr> final {
26
+ static inline std::exception_ptr fromJSI(jsi::Runtime& runtime, const jsi::Value& error) {
26
27
  jsi::Object object = error.asObject(runtime);
27
28
  std::string name = object.getProperty(runtime, "name").asString(runtime).utf8(runtime);
28
29
  std::string message = object.getProperty(runtime, "message").asString(runtime).utf8(runtime);
29
- return std::runtime_error(name + ": " + message);
30
+ return std::make_exception_ptr(std::runtime_error(name + ": " + message));
30
31
  }
31
- static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::exception& exception) {
32
- jsi::JSError error(runtime, exception.what());
33
- return jsi::Value(runtime, error.value());
32
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::exception_ptr& exception) {
33
+ try {
34
+ std::rethrow_exception(exception);
35
+ } catch (const std::exception& e) {
36
+ jsi::JSError error(runtime, e.what());
37
+ return jsi::Value(runtime, error.value());
38
+ } catch (...) {
39
+ // Some unknown exception was thrown - maybe an Objective-C error?
40
+ std::string errorName = TypeInfo::getCurrentExceptionName();
41
+ jsi::JSError error(runtime, "Unknown " + errorName + " error.");
42
+ return jsi::Value(runtime, error.value());
43
+ }
34
44
  }
35
45
  static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
36
46
  if (!value.isObject()) {
@@ -56,14 +56,12 @@ struct JSIConverter<std::function<ReturnType(Args...)>> final {
56
56
  }
57
57
 
58
58
  if constexpr (std::is_void_v<ResultingType>) {
59
- dispatcher->runAsync([&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() {
60
- callJSFunction(runtime, sharedFunction, args...);
61
- });
59
+ dispatcher->runAsync(
60
+ [&runtime, sharedFunction, ... args = std::move(args)]() { callJSFunction(runtime, sharedFunction, args...); });
62
61
  } else {
63
- return dispatcher->runAsyncAwaitable<ResultingType>(
64
- [&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() -> ResultingType {
65
- return callJSFunction(runtime, sharedFunction, args...);
66
- });
62
+ return dispatcher->runAsyncAwaitable<ResultingType>([&runtime, sharedFunction, ... args = std::move(args)]() -> ResultingType {
63
+ return callJSFunction(runtime, sharedFunction, args...);
64
+ });
67
65
  }
68
66
  };
69
67
  }
@@ -37,8 +37,8 @@ struct JSIConverter<std::shared_ptr<Promise<TResult>>> final {
37
37
  return JSIConverter<std::function<void(TResult)>>::toJSI(runtime, [=](const TResult& result) { promise->resolve(result); });
38
38
  }
39
39
  }();
40
- auto catchCallback = JSIConverter<std::function<void(std::exception)>>::toJSI(
41
- runtime, [=](const std::exception& exception) { promise->reject(exception); });
40
+ auto catchCallback = JSIConverter<std::function<void(std::exception_ptr)>>::toJSI(
41
+ runtime, [=](const std::exception_ptr& exception) { promise->reject(exception); });
42
42
 
43
43
  // Chain .then listeners on JS Promise (onResolved and onRejected)
44
44
  jsi::Object jsPromise = value.asObject(runtime);
@@ -65,7 +65,7 @@ struct JSIConverter<std::shared_ptr<Promise<TResult>>> final {
65
65
  promise->addOnResolvedListener(std::move(resolver));
66
66
  }
67
67
  // Add rejecter listener
68
- auto rejecter = JSIConverter<std::function<void(std::exception)>>::fromJSI(runtime, arguments[1]);
68
+ auto rejecter = JSIConverter<std::function<void(std::exception_ptr)>>::fromJSI(runtime, arguments[1]);
69
69
  promise->addOnRejectedListener(std::move(rejecter));
70
70
 
71
71
  return jsi::Value::undefined();
@@ -89,7 +89,7 @@ struct JSIConverter<std::shared_ptr<Promise<TResult>>> final {
89
89
  // Promise is already rejected - just return immediately
90
90
  jsi::Object promiseObject = runtime.global().getPropertyAsObject(runtime, "Promise");
91
91
  jsi::Function createRejectedPromise = promiseObject.getPropertyAsFunction(runtime, "reject");
92
- jsi::Value error = JSIConverter<std::exception>::toJSI(runtime, promise->getError());
92
+ jsi::Value error = JSIConverter<std::exception_ptr>::toJSI(runtime, promise->getError());
93
93
  return createRejectedPromise.call(runtime, std::move(error));
94
94
  } else {
95
95
  throw std::runtime_error("Promise has invalid state!");
@@ -5,6 +5,8 @@
5
5
  // Created by Marc Rousavy on 14.07.24.
6
6
  //
7
7
 
8
+ #pragma once
9
+
8
10
  #include <string>
9
11
 
10
12
  namespace margelo::nitro {
@@ -7,7 +7,6 @@
7
7
 
8
8
  #pragma once
9
9
 
10
- #include <jsi/jsi.h>
11
10
  #include <type_traits>
12
11
 
13
12
  namespace margelo::nitro {
@@ -0,0 +1,34 @@
1
+ //
2
+ // AssertPromiseState.hpp
3
+ // NitroModules
4
+ //
5
+ // Created by Marc Rousavy on 20.11.24.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ namespace margelo::nitro {
11
+ template <typename TResult>
12
+ class Promise;
13
+ } // namespace margelo::nitro
14
+
15
+ #include "Promise.hpp"
16
+ #include "TypeInfo.hpp"
17
+ #include <exception>
18
+ #include <string>
19
+
20
+ namespace margelo::nitro {
21
+
22
+ enum PromiseTask { WANTS_TO_RESOLVE, WANTS_TO_REJECT };
23
+
24
+ template <typename TResult>
25
+ void assertPromiseState(Promise<TResult>& promise, PromiseTask task) {
26
+ if (!promise.isPending()) [[unlikely]] {
27
+ std::string taskString = task == WANTS_TO_RESOLVE ? "resolve" : "reject";
28
+ std::string state = promise.isResolved() ? "resolved" : "rejected";
29
+ throw std::runtime_error("Cannot " + taskString + " Promise<" + TypeInfo::getFriendlyTypename<TResult>() + "> - it is already " +
30
+ state + "!");
31
+ }
32
+ }
33
+
34
+ } // namespace margelo::nitro
@@ -111,7 +111,7 @@ private:
111
111
  bool* _isDeleted;
112
112
  std::atomic_size_t* _strongRefCount;
113
113
  std::atomic_size_t* _weakRefCount;
114
- std::mutex* _mutex;
114
+ std::recursive_mutex* _mutex;
115
115
  };
116
116
 
117
117
  } // namespace margelo::nitro
@@ -34,7 +34,7 @@ public:
34
34
 
35
35
  explicit OwningReference(T* value)
36
36
  : _value(value), _isDeleted(new bool(false)), _strongRefCount(new std::atomic_size_t(1)), _weakRefCount(new std::atomic_size_t(0)),
37
- _mutex(new std::mutex()) {}
37
+ _mutex(new std::recursive_mutex()) {}
38
38
 
39
39
  OwningReference(const OwningReference& ref)
40
40
  : _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
@@ -229,7 +229,7 @@ private:
229
229
  bool* _isDeleted;
230
230
  std::atomic_size_t* _strongRefCount;
231
231
  std::atomic_size_t* _weakRefCount;
232
- std::mutex* _mutex;
232
+ std::recursive_mutex* _mutex;
233
233
  };
234
234
 
235
235
  } // namespace margelo::nitro
@@ -24,17 +24,6 @@ struct TypeInfo final {
24
24
  public:
25
25
  TypeInfo() = delete;
26
26
 
27
- /**
28
- * Get the name of the currently thrown exception
29
- */
30
- static inline const char* getCurrentExceptionName() {
31
- #if __has_include(<cxxabi.h>)
32
- return __cxxabiv1::__cxa_current_exception_type()->name();
33
- #else
34
- return "<unknown>";
35
- #endif
36
- }
37
-
38
27
  static inline std::string replaceRegex(const std::string& original, const std::string& pattern, const std::string& replacement) {
39
28
  std::regex re(pattern);
40
29
  return std::regex_replace(original, re, replacement);
@@ -112,6 +101,18 @@ public:
112
101
  std::string string = stream.str();
113
102
  return string.substr(0, string.length() - 2);
114
103
  }
104
+
105
+ /**
106
+ * Get the name of the currently thrown exception
107
+ */
108
+ static inline std::string getCurrentExceptionName() {
109
+ #if __has_include(<cxxabi.h>)
110
+ std::string name = __cxxabiv1::__cxa_current_exception_type()->name();
111
+ return demangleName(name);
112
+ #else
113
+ return "<unknown>";
114
+ #endif
115
+ }
115
116
  };
116
117
 
117
118
  } // namespace margelo::nitro
@@ -12,13 +12,20 @@ import Foundation
12
12
  *
13
13
  * Throw this error in Nitro Modules to provide clear and concise error messages to JS.
14
14
  */
15
- public enum RuntimeError: Error {
15
+ @frozen
16
+ public enum RuntimeError: Error, CustomStringConvertible {
16
17
  case error(withMessage: String)
17
-
18
+
19
+ public var description: String {
20
+ switch self {
21
+ case .error(let message): return message
22
+ }
23
+ }
24
+
18
25
  /**
19
26
  * Creates a new `RuntimeError` from the given C++ `std::exception`.
20
27
  */
21
- public static func from(cppError: std.exception) -> RuntimeError {
28
+ public static func from(cppError: std.exception_ptr) -> RuntimeError {
22
29
  let message = margelo.nitro.get_exception_message(cppError)
23
30
  return .error(withMessage: String(message))
24
31
  }
@@ -28,7 +35,7 @@ public extension Error {
28
35
  /**
29
36
  * Converts this `Error` to a C++ `std::exception`.
30
37
  */
31
- func toCpp() -> std.exception {
38
+ func toCpp() -> std.exception_ptr {
32
39
  let message = String(describing: self)
33
40
  return margelo.nitro.make_exception(std.string(message))
34
41
  }
@@ -57,7 +57,7 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) {
57
57
  // 5. Install Nitro
58
58
  nitro::install(*runtime, dispatcher);
59
59
  return nil;
60
- } catch (std::exception& error) {
60
+ } catch (const std::exception& error) {
61
61
  // ?. Any C++ error occurred (probably in nitro::install()?)
62
62
  return [NSString stringWithCString:error.what() encoding:kCFStringEncodingUTF8];
63
63
  } catch (...) {
@@ -12,12 +12,19 @@
12
12
 
13
13
  namespace margelo::nitro {
14
14
 
15
- static inline std::exception make_exception(const std::string& message) {
16
- return std::runtime_error(message);
15
+ static inline std::exception_ptr make_exception(const std::string& message) {
16
+ return std::make_exception_ptr(std::runtime_error(message));
17
17
  }
18
18
 
19
- static inline std::string get_exception_message(const std::exception& exception) {
20
- return exception.what();
19
+ static inline std::string get_exception_message(const std::exception_ptr& exception) {
20
+ try {
21
+ std::rethrow_exception(exception);
22
+ } catch (const std::exception& error) {
23
+ return error.what();
24
+ } catch (...) {
25
+ std::string errorName = TypeInfo::getCurrentExceptionName();
26
+ return "Unknown " + errorName + " exception";
27
+ }
21
28
  }
22
29
 
23
30
  } // namespace margelo::nitro
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-modules",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Insanely fast native C++, Swift or Kotlin modules with a statically compiled binding layer to JSI.",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -28,6 +28,8 @@
28
28
  "ios",
29
29
  "android",
30
30
  "visionOS",
31
+ "tvOS",
32
+ "macOS",
31
33
  "cpp",
32
34
  "framework",
33
35
  "react",
@@ -70,7 +72,7 @@
70
72
  "jest": "*",
71
73
  "react": "*",
72
74
  "react-native": "*",
73
- "react-native-builder-bob": "^0.32.0"
75
+ "react-native-builder-bob": "^0.33.1"
74
76
  },
75
77
  "peerDependencies": {
76
78
  "react": "*",