mirror of
https://github.com/status-im/react-native.git
synced 2025-02-23 06:38:13 +00:00
Summary: When writing a native module in C++ using CxxModule, its not currently possible to write async methods which take two callbacks, that shouldn't be projected to JS as a promise. I hit this when trying to implement the AppState module in c++. AppState has a single method on it, getCurrentAppState, which takes two callbacks (a success and a failure callback). This change adds a couple of new CxxModule::Method ctors, which allow devs to specify that they want two callbacks, but not a promise. This is done using an extra tag in the ctor, similar to how you specify that you want to make a synchronous method. I used the existing AsyncTag to indicate that the 2 callback version should be async, and not a promise. Pull Request resolved: https://github.com/facebook/react-native/pull/21586 Reviewed By: shergin Differential Revision: D10520204 Pulled By: fkgozali fbshipit-source-id: bcb2dbd91cba3c1db987dc18960247218fdbc032
229 lines
6.8 KiB
C++
229 lines
6.8 KiB
C++
// Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
// This source code is licensed under the MIT license found in the
|
|
// LICENSE file in the root directory of this source tree.
|
|
|
|
#pragma once
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <tuple>
|
|
#include <vector>
|
|
|
|
#include <folly/dynamic.h>
|
|
|
|
using namespace std::placeholders;
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
class Instance;
|
|
|
|
}}
|
|
|
|
namespace facebook {
|
|
namespace xplat {
|
|
namespace module {
|
|
|
|
/**
|
|
* Base class for Catalyst native modules whose implementations are
|
|
* written in C++. Native methods are represented by instances of the
|
|
* Method struct. Generally, a derived class will manage an instance
|
|
* which represents the data for the module, and non-Catalyst-specific
|
|
* methods can be wrapped in lambdas which convert between
|
|
* folly::dynamic and native C++ objects. The Callback arguments will
|
|
* pass through to js functions passed to the analogous javascript
|
|
* methods. At most two callbacks will be converted. Results should
|
|
* be passed to the first callback, and errors to the second callback.
|
|
* Exceptions thrown by a method will be converted to platform
|
|
* exceptions, and handled however they are handled on that platform.
|
|
* (TODO mhorowitz #7128529: this exception behavior is not yet
|
|
* implemented.)
|
|
*
|
|
* There are two sets of constructors here. The first set initializes
|
|
* a Method using a name and anything convertible to a std::function.
|
|
* This is most useful for registering a lambda as a RN method. There
|
|
* are overloads to support functions which take no arguments,
|
|
* arguments only, and zero, one, or two callbacks.
|
|
*
|
|
* The second set of methods is similar, but instead of taking a
|
|
* function, takes the method name, an object, and a pointer to a
|
|
* method on that object.
|
|
*/
|
|
|
|
class CxxModule {
|
|
class AsyncTagType {};
|
|
class SyncTagType {};
|
|
|
|
public:
|
|
typedef std::function<std::unique_ptr<CxxModule>()> Provider;
|
|
|
|
typedef std::function<void(std::vector<folly::dynamic>)> Callback;
|
|
|
|
constexpr static AsyncTagType AsyncTag = AsyncTagType();
|
|
constexpr static SyncTagType SyncTag = SyncTagType();
|
|
|
|
struct Method {
|
|
std::string name;
|
|
|
|
size_t callbacks;
|
|
bool isPromise;
|
|
std::function<void(folly::dynamic, Callback, Callback)> func;
|
|
|
|
std::function<folly::dynamic(folly::dynamic)> syncFunc;
|
|
|
|
const char *getType() {
|
|
assert(func || syncFunc);
|
|
return func ? (isPromise ? "promise" : "async") : "sync";
|
|
}
|
|
|
|
// std::function/lambda ctors
|
|
|
|
Method(std::string aname,
|
|
std::function<void()>&& afunc)
|
|
: name(std::move(aname))
|
|
, callbacks(0)
|
|
, isPromise(false)
|
|
, func(std::bind(std::move(afunc))) {}
|
|
|
|
Method(std::string aname,
|
|
std::function<void(folly::dynamic)>&& afunc)
|
|
: name(std::move(aname))
|
|
, callbacks(0)
|
|
, isPromise(false)
|
|
, func(std::bind(std::move(afunc), std::placeholders::_1)) {}
|
|
|
|
Method(std::string aname,
|
|
std::function<void(folly::dynamic, Callback)>&& afunc)
|
|
: name(std::move(aname))
|
|
, callbacks(1)
|
|
, isPromise(false)
|
|
, func(std::bind(std::move(afunc), std::placeholders::_1, std::placeholders::_2)) {}
|
|
|
|
Method(std::string aname,
|
|
std::function<void(folly::dynamic, Callback, Callback)>&& afunc)
|
|
: name(std::move(aname))
|
|
, callbacks(2)
|
|
, isPromise(true)
|
|
, func(std::move(afunc)) {}
|
|
|
|
Method(std::string aname,
|
|
std::function<void(folly::dynamic, Callback, Callback)>&& afunc,
|
|
AsyncTagType)
|
|
: name(std::move(aname))
|
|
, callbacks(2)
|
|
, isPromise(false)
|
|
, func(std::move(afunc)) {}
|
|
|
|
// method pointer ctors
|
|
|
|
template <typename T>
|
|
Method(std::string aname, T* t, void (T::*method)())
|
|
: name(std::move(aname))
|
|
, callbacks(0)
|
|
, isPromise(false)
|
|
, func(std::bind(method, t)) {}
|
|
|
|
template <typename T>
|
|
Method(std::string aname, T* t, void (T::*method)(folly::dynamic))
|
|
: name(std::move(aname))
|
|
, callbacks(0)
|
|
, isPromise(false)
|
|
, func(std::bind(method, t, std::placeholders::_1)) {}
|
|
|
|
template <typename T>
|
|
Method(std::string aname, T* t, void (T::*method)(folly::dynamic, Callback))
|
|
: name(std::move(aname))
|
|
, callbacks(1)
|
|
, isPromise(false)
|
|
, func(std::bind(method, t, std::placeholders::_1, std::placeholders::_2)) {}
|
|
|
|
template <typename T>
|
|
Method(std::string aname, T* t, void (T::*method)(folly::dynamic, Callback, Callback))
|
|
: name(std::move(aname))
|
|
, callbacks(2)
|
|
, isPromise(true)
|
|
, func(std::bind(method, t, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)) {}
|
|
|
|
template <typename T>
|
|
Method(std::string aname,
|
|
T* t,
|
|
void (T::*method)(folly::dynamic, Callback, Callback),
|
|
AsyncTagType)
|
|
: name(std::move(aname))
|
|
, callbacks(2)
|
|
, isPromise(false)
|
|
, func(std::bind(method, t, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)) {}
|
|
|
|
// sync std::function/lambda ctors
|
|
|
|
// Overloads for functions returning void give ambiguity errors.
|
|
// I am not sure if this is a runtime/compiler bug, or a
|
|
// limitation I do not understand.
|
|
|
|
Method(std::string aname,
|
|
std::function<folly::dynamic()>&& afunc,
|
|
SyncTagType)
|
|
: name(std::move(aname))
|
|
, callbacks(0)
|
|
, isPromise(false)
|
|
, syncFunc([afunc=std::move(afunc)] (const folly::dynamic&)
|
|
{ return afunc(); })
|
|
{}
|
|
|
|
Method(std::string aname,
|
|
std::function<folly::dynamic(folly::dynamic)>&& afunc,
|
|
SyncTagType)
|
|
: name(std::move(aname))
|
|
, callbacks(0)
|
|
, isPromise(false)
|
|
, syncFunc(std::move(afunc))
|
|
{}
|
|
};
|
|
|
|
/**
|
|
* This may block, if necessary to complete cleanup before the
|
|
* object is destroyed.
|
|
*/
|
|
virtual ~CxxModule() {}
|
|
|
|
/**
|
|
* @return the name of this module. This will be the name used to {@code require()} this module
|
|
* from javascript.
|
|
*/
|
|
virtual std::string getName() = 0;
|
|
|
|
/**
|
|
* Each entry in the map will be exported as a property to JS. The
|
|
* key is the property name, and the value can be anything.
|
|
*/
|
|
virtual auto getConstants() -> std::map<std::string, folly::dynamic> { return {}; };
|
|
|
|
/**
|
|
* @return a list of methods this module exports to JS.
|
|
*/
|
|
virtual auto getMethods() -> std::vector<Method> = 0;
|
|
|
|
/**
|
|
* Called during the construction of CxxNativeModule.
|
|
*/
|
|
void setInstance(std::weak_ptr<react::Instance> instance) {
|
|
instance_ = instance;
|
|
}
|
|
|
|
/**
|
|
* @return a weak_ptr to the current instance of the bridge.
|
|
* When used with CxxNativeModule, this gives Cxx modules access to functions
|
|
* such as `callJSFunction`, allowing them to communicate back to JS outside
|
|
* of the regular callbacks.
|
|
*/
|
|
std::weak_ptr<react::Instance> getInstance() {
|
|
return instance_;
|
|
}
|
|
|
|
private:
|
|
std::weak_ptr<react::Instance> instance_;
|
|
};
|
|
|
|
}}}
|