react-native-electrode-bridge
v1.6.0
Published
Low level communication bridge between React Native js and native
Downloads
576
Readme
react-native-electrode-bridge
This project is essentially a React Native library, consisting of a JavaScript module and an associated Android and iOS Native modules.
Features
It is built on top of the React Native built-in bridging constructs (to communicate between JavaScript and native code). It offers a clean bi-directional communication API, exposing methods to send events and/or requests from/to any side of the bridge (JS/Native). It provides more options and flexibility to communicate between the JS/Native side that is not provided out of the box by React Native. Ultimately, it can help with integrating React Native applications into existing native code bases. It might be used as one of the basic building blocks of React Native apps and native modules.
Here is a non-exhaustive list of a few reasons why to use this library as the low level communication bridge instead of the built-in React Native constructs:
- Isolates the host application from React Native library types and specifics (Vanilla Java/Android/Swift implementation)
- Messages can be sent either to the other side of the bridge or on the same side (or both)
- Request timeout supported
- Offers an easy way to send requests from Native to JS side.
- Offers type safety on native side while communicating to JS side.
- More than a bridge, it can be used as a message hub that allows React Native apps / native modules intercommunication
Read more about bridge HERE
Compatibility
| React Native Version | Bridge Version | | -------------------- | -------------- | | v0.42->v0.47 | v1.5.0+ | | v0.48+ | v1.5.9+ |
Getting Started
Depending on which part of the bridge(Android|iOS|JS) you are working on open the code in respective IDEs.
Bridge message types
Communication through the Electrode Native bridge is based on message exchanges between JavaScript and the Native application. The Electrode Native bridge processes three message types: Request
, Response
, and Event
.
Request
A Request message is used to request data from a receiver or to request an action to be performed by a receiver. A Request message always results in an associated response message that can contain either the requested data or indicate the result of an action. A Request message can optionally contain a payload. For any given Request message type, there can be only one associated receiver. The receiver handles the request and issues a response message. From a developer perspective, a Request message can be thought as being a method call.Response
A Response message is the result of a single Request message. A Response message can optionally contain a payload. From a developer perspective, a Response message can be thought as the return value of a method. The value can be of a specific type or not (void).Event
An Event message is a "fire and forget" message. The sender of the Event message does not expect a response --so the receiver is known as a listener. Unlike a Request message, an Event message can be received by multiple listeners. All registered listeners (on the JavaScript side and native side) for a specific event message type will receive the Event message.
JavaScript
import {electrodeBridge} from 'react-native-electrode-bridge';
Once you import the module, you can interact with the electrodeBridge
instance through a few API methods:
electrodeBridge.sendRequest
electrodeBridge.sendRequest(
name: String, {
data: Object = {},
timeout: Number = DEFAULT_REQUEST_TIMEOUT_IN_MS /* 5000 */
}): Promise
Sends a request with a specific name
through the bridge.
Mandatory:
name
: The name of the request to emit
Optional:
data
: An object to include as the data payload of the request (Default: {})timeout
: A timeout in milliseconds, after which, if no response was received, the returned promise will be rejected with error codeEREQUESTIMEOUT
. (Default: 5000)
Example:
electrodeBridge.sendRequest(
"myapp.get.current.weather", {
data: { latlng: `37.381435,-122.036909` },
timeout: 6000
}).then(resp) {
// Do whatever you need to do with the response
}
electrodeBridge.emitEvent
electrodeBridge.emitEvent(
name: String, {
data: Object = {}
}): void
Emits an event with a specific name
through the bridge.
Mandatory:
name
: The name of the event to emit
Optional:
data
: An object to include as the data payload of the event (Default: {})
Example:
electrodeBridge.emitEvent('myapp.some.event');
electrodeBridge.registerRequestHandler
electrodeBridge.registerRequestHandler(
name: String,
handler: Promise): void
Registers a handler that can handle a specific request name
.
Please note that if an handler already exists for the specific request name (on the side you are making the call) the method will throw an error. Current implementation only allows one request handler to be associated to a given request name.
Mandatory:
name
: The name of the request this handler can handlehandler
: The handler function, taking a single parameter being the data of the request and returning a Promise. Implementer of the handler should either resolve the promise with an object being the response data (if any) or reject the promise with an Error.
Example:
electrodeBridge.registerRequestHandler('myapp.awesomerequest', requestData => {
return Promise.resolve({hello: 'World'});
});
electrodeBridge.registerEventListener
electrodeBridge.registerEventListener(
name: String,
handler: Function): void
Registers an event listener that will be invoked whenever an event of the specific type
is received by the bridge.
Mandatory:
name
: The name of the event that this listener is interested inhandler
: A function to handle an incoming event. The function takes a single parameter being the data payload of the event (if any).
Example:
electrodeBridge.registerEventListener('myapp.coolevent', eventData => {
// Do whatever you need to do
});
Android
First step is to add the ElectrodeBridgePackage
containing the ElectrodeBridge
Native Module to the list of packages included in your app:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ElectrodeBridgePackage() // ADD THIS LINE !
);
}
Then, access to API methods is provided through static methods of the ElectrodeBridgeHolder
class.
ElectrodeBridge
can deal with any PrimitiveWrapper
or Bridgeable
as the request and response types.
ElectrodeBridgeHolder.sendRequest
void sendRequest(
@NonNull ElectrodeBridgeRequest request,
@NonNull ElectrodeBridgeResponseListener<ElectrodeBridgeResponse> responseListener);
Sends a request through the bridge.
Mandatory:
request
: A request instance created usingElectrodeBridgeRequest.Builder
responseListener
: An instance ofElectrodeBridgeResponseListener
to be notified of the response.
To make it easier to construct a request and send it via bridge the RequestProcessor
class can be used
Example:
new RequestProcessor<>("my.request.name", <input data>, <ExpectedResponse>.class, new ElectrodeBridgeResponseListener<ExpectedObjectType>() {
@Override
public void onFailure(@NonNull FailureMessage failureMessage) {
// Handle failure
}
@Override
public void onSuccess(@Nullable ExpectedObjectType responseData) {
// Do whatever you need to do with the response
}
}).execute();
The RequestProcessor
takes care of generating a ElectrodeBridgeRequest
and sending it over to the ElectrodeBridge
In case of a request not expecting any ElectrodeBridgeRequest
use None
as the type.
<input data>
: can be null.
ElectrodeBridge.emitEvent
void emitEvent(@NonNull ElectrodeBridgeEvent event)
Emits an event through the bridge.
Mandatory:
event
: An event instance created usingElectrodeBridgeEvent.Builder
To make is easier to construct an event and emit it via bridge the EventProcessor
class can be used
Example:
new EventProcessor<>("my.event.name", <data>).execute();
<data>
can be null
ElectrodeBridge.registerRequestHandler
UUID registerRequestHandler(
@NonNull String name,
@NonNull ElectrodeBridgeRequestHandler requestHandler);
Registers a handler that can handle a specific request name
.
When a request is fired, for example from JS side, ElectrodeBridge
first looks for a registered request handler on JS side, if not found bridge will forward the request to Native side.
Mandatory:
name
: The name of the request this handler can handlerequestHandler
an instance ofElectrodeBridgeRequestHandler
that should take care of handling the request and completing it.
To make is easier to construct a request handler and register it to the bridge a RequestHandlerProcessor
class can be used
Example:
new RequestHandlerProcessor<>("my.request.name", <ExpectedRequest>.class, <ExpectedResponse>.class, new ElectrodeBridgeRequestHandler<ExpectedRequestType, ExpectedResponseType>() {
@Override
public void onRequest(@Nullable ExpectedRequestType payload, @NonNull ElectrodeBridgeResponseListener<ExpectedResponse> responseListener) {
// Handle the request (sync or async) and call one of the completion methods once done
requestCompletioner.onSuccess(expectedResponse); OR
requestCompletion.onFailure(failureMessage); // With error
}
}).execute();
ElectrodeBridge.registerEventListener
void addEventListener(
@NonNull String eventName,
@NonNull ElectrodeBridgeEventListener eventListener);
)
Mandatory:
name
: The name of the event that this listener is interested ineventListener
an instance ofElectrodeBridgeEventListener
that is interested in knowing when an event is emitted.
Example:
new EventListenerProcessor<>("my.event.name", <ExpectedEvent>.class, new ElectrodeBridgeEventListener<ExpectedEvent>() {
@Override
public void onEvent(@Nullable ExpectedEvent eventPayload) {
// Do what you need to do now
}
}).execute();
Note: Multiple event listeners can be registered for the same event.
Android remarks
This bridge implementation does not make use any third party library, so that we don't lock in any client into a specific framework. It makes use of vanilla Java/Android types to expose communication methods.
The client native app might want to build a specific adapter around the bridge, so that the native app can make use of whatever framework fits them best (Rx/Bolts/Otto ... for communication ... Jackson/Gson for serialization ...).
It would be nice at some point to see adapters for a specific frameworks being redistributed as libraries to be used by others.
iOS
First step is to install ElectrodeReactNativeBridge
as dependency through your preferred way.
Next step is to import ElectrodeReactNativeBridge.h
into your app
#import <ElectrodeReactNativeBridge/ElectrodeReactNativeBridge.h>
Then, access to API methods is provided through static methods of the ElectrodeBridgeHolder
class.
ElectrodeBridgeHolder
can deal with any primitives
or Bridgeable
as the request and response types.
sendRequest:completionHandler
+ (void)sendRequest:(ElectrodeBridgeRequest *)request
completionHandler:(ElectrodeBridgeResponseCompletionHandler)completion;
Sends a request through the bridge.
Mandatory:
request
: A request instance created byElectrodeBridgeRequest
classcompletionHandler
: An block that takes anid _Nullable data
and anid<ElectrodeFailureMessage> _Nullable message
to notify its listener on completion of a request. When the request failed, a failure message must be send back; Otherwise, the request is assumed to be successful. In the case of success,data
associated with the request could be pass back to the listener(optional).
To make it easier to construct a request and send it via bridge the ElectrodeRequestProcessor
class can be used
Example:
let requestProcessor = ElectrodeRequestProcessor<TReq, TResp, TItem>(
requestName: "my.request.name",
requestPayload: <request data>,
respClass: <ExpectedResponseObjectType>.self,
responseItemType: <ItemObjectType>.self, //only needed if response is an array. e.g. for [Person], it will be Person.self
responseCompletionHandler: responseCompletionHandler: { any, failureMessage in
if let failure = failureMessage {
// handle failure
} else {
// handle success
}
})
requestProcessor.execute()
The ElectrodeRequestProcessor
takes care of generating a ElectrodeBridgeRequest
and sending it over to the ElectrodeBridge
In case of a request not expecting any ElectrodeBridgeRequest
use None
as the type.
requestPayload
and responseItemType
are Optional
.
sendEvent
+ (void)sendEvent:(ElectrodeBridgeEvent *)event;
Emits an event through the bridge.
Mandatory:
event
: An event instance ofElectrodeBridgeEvent
To make is easier to construct an event and emit it via bridge the EventProcessor
class can be used
Example:
let eventProcessor = EventProcessor(eventName: "<event name>", eventPayload: <event payload>)
eventProcessor.execute()
eventPayload
is Optional
registerRequestHanlderWithName:requestCompletionHandler
+ (void)registerRequestHanlderWithName:(NSString *)name
requestCompletionHandler:(ElectrodeBridgeRequestCompletionHandler)completion;
Registers a handler that can handle a specific request name
.
When a request is fired, for example from JS side, ElectrodeBridge
first looks for a registered request handler on JS side, if not found bridge will forward the request to Native side.
Mandatory:
name
: The name of the request this handler can handlerequestHandler
: aElectrodeBridgeRequestCompletionHandler
block that should take care of handling the request and completing it.
To make is easier to construct a request handler and register it to the bridge a ElectrodeRequestHandlerProcessor
class can be used
Example:
let requestHandlerProcessor = ElectrodeRequestHandlerProcessor(
requestName: "<your request name>",
reqClass: <YourRequestParamClass>.self,
respClass: <ExpectedResponseClass>.self,
requestCompletionHandler: { data, responseCompletionHandler in
// data is of type <ExpectedResponseClass>
// responseCompletionHandler is a block of ElectrodeBridgeRequestCompletionHandler
})
requestHandlerProcessor.execute()
addEventListnerWithName:eventListner
+ (void)addEventListnerWithName:(NSString *)name
eventListner:(ElectrodeBridgeEventListener)eventListner;
Mandatory:
name
: The name of the event that this listener is interested ineventListener
: aElectrodeBridgeEventListener
ElectrodeBridgeEventListener that is interested in knowing when an event is emitted.
Example:
let listenerProcessor = EventListenerProcessor(
eventName: "<YourEventName>",
eventPayloadClass: <PayloadClass>.self,
eventListener: { payload in
// the payload emitted with the event
})
listenerProcessor.execute()
Note: Multiple event listeners can be registered for the same event.