How to inject non static, on demand NativeModules into React Native Android? - javascript

In a hybrid app where multiple Activity/Fragment instances nesting their own ReactRootViews share a single ReactInstanceManager instance, there seems to be no official way to inject a native module per Activity/Fragment instance. By nature, native modules are singleton like javascript modules residing in a .js file. This is not the intended behavior if JS code running inside different instances of the same Fragment wants to access Java/Kotlin side to interact with the local properties of the Fragment.
I tried using registerAdditionalPackages() method of ReactInstanceManager but it fails with an assertion error like below if used simultaneously by multiple Fragments/Activitys.
"Extending native modules with non-matching application contexts."
It wasn't a thread safety issue but a design consequence of registerAdditionalPackages() implementation. Is there another way, and if so, how do you access the injected module on the JS side?

This problem kept me awake for days. Finally, I have a solution. I hope it helps someone in the future. The solution is in Kotlin but it is quite straightforward to translate it into Java. someVariable!! is a "not null" assertion, uppercase SomeObject() calls are instance creation, SomeType:SomeOtherType is inheritence or implementation, val someVar:SomeType is a variable declaration. The rest is the same.
Steps:
1) Run the below code at the time you want to inject a module to your RN runtime. Inside of Activity.onCreate() or Fragment.onCreateView() are some good candidates. mReactInstanceManager is your singleton, global react runtime. packageToInject definition will be given later.
synchronized(mReactInstanceManager!!.currentReactContext!!) {
val nativeModuleRegistryBuilder = NativeModuleRegistryBuilder(
mReactInstanceManager!!.currentReactContext as ReactApplicationContext?,
mReactInstanceManager!!,
false
)
nativeModuleRegistryBuilder.processPackage(packageToInject)
mReactInstanceManager!!.currentReactContext!!.catalystInstance!!.extendNativeModules(nativeModuleRegistryBuilder.build())
}
2) Instance held inside packageToInject must be prepared like below to have a unique module with a unique name per Fragment/Activity instance. Make these inner classes of your Fragment or Activity.
class ReactManagerPackage : ReactPackage {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
val modules = ArrayList<NativeModule>()
modules.add(ReactBridge(reactContext))
return modules
}
}
class ReactBridge(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return myFragmentOrActivity.hashCode().toString()
}
#ReactMethod
fun showToast(text: String) {
Toast.makeText(text, Toast.LENGTH_SHORT).show()
}
}
3) Pass the myFragmentOrActivity.hashCode().toString() as a prop when you initiate mReactRootView.startReactApplication() either in your Activity.onCreate() or Fragment.onCreateView() . Put it in the bundle that you give to mReactRootView.startReactApplication() as 3rd argument.
val bundle = Bundle()
bundle.putString("fragmentOrActivityHash", myFragmentOrActivity.hashCode().toString())
mReactRootView.startReactApplication(mReactInstanceManager, "MyRootComponent", bundle )
4) Use the prop in your component (MyRootComponent in this example) to retrieve your specific bridge. (Javascript)
NativeModules[this.props.fragmentOrActivityHash].showToast("It works")
Profit!

Related

Class.name always 'e' for every class after uglify / webpack for production

I have an app which is working great in development enviroment but it is not working in production, which is caused by uglify (I think so)
I have a data which user builds and I am saving that either to file or to LocalStorage (json in both cases so doesn't matter).
The structure is built from 3 type of nodes. I have implemented property in the base class ( all inherit from one class): type =this.constructor.name and it is working great in development. When I load the app and read the cache, I go through the JSON and rebuild objects using switch (obj.type) case class1.name... etc. It is working well.
However, when I build for production, when I call class1.name or class2.name or class3.name it all returns e which makes it impossible to restore proper objects...
I do not think it is framework specific issue, but if someone would need to know I build using VueJS with Quasar Framework.
Any ideas?
constructor.name or any other function name property should never be relied in client-side JavaScript, exactly because minification is a must in production environment, and functions with meaningful names become one-letter named functions. e is a common name for a function minified with UglifyJS. This is the reason why uglified JS file has much lesser footprint than unminified file.
If function/class names are used for anything but debugging, they should be explicitly specified as static properties. Since name cannot be redefined in some engines, non-standard displayName (can also be useful for debugging) or any other property name can be used:
class Foo {
static get id() { return 'Foo' }
...
}
Or:
class Foo {
...
}
Foo.id = 'Foo';
For whoever comes after
Webpack uses UglifyJS to compress and hide aspects of your code (referred to as 'mangle'), with regards to your question specifically, it transforms all your classes to e, you have to specific either in your webpack config or cli to not do this if you'd like to preserve your classnames and/or function names.
You can disable this behavior by removing the minimization or creating your own optimization instance as shown here
Depending on your goal you can also use the below approach. In my case I just needed to be able to distinguish between class types so the below was sufficient:
class MyClass {
constructor(){
this.createClassId()
}
createClassId(){
if(!this.constructor.prototype._customClassId){
this.constructor.prototype._customClassId = uuidv4() //you choose what you want this _customClassId to be. Or have a look at nanoId which can produce shorter names
}
}
then new MyClass()._customClassId would be the same for all instances of the same class. And if, for example, you inherit from MyClass, the subclasses will have the same _customClassId month all instances of subclasses but it will be different from what the parent class holds.
If you need to control what exactly _customClassId should be for each class, you can do it as well, though it will require a bit more setup using inheritance like below:
class MyBaseClass {
constructor(){
this.createClassId()
}
createClassId(){
if(!this.constructor.prototype._customClassId){
this.constructor.prototype._customClassId = this.getCustomClassName() //you choose what you want this _customClassId to be. Or have a look at nanoId which can produce shorter names
}
getCustomClassName(){
throw new Error("Make sure to implement 'getCustomClassName' in your subclass")
}
}
Then you can extend every class where you need to access their classType/className from this MyBaseClass and override getCustomClassName providing your value.

js - How to decorate/proxy/spy on all functions? For creating a runtime profiler

So I have this decorate function that takes an object and a method-name and wraps it with external logic.
function decorate(object, methodName) {
var originalMethod = object[methodName];
object[methodName] = function () {
// pre-logic
var retVal = originalMethod.apply(this, arguments);
// post-logic
return retVal;
};
}
Now I want to wrap ALL of the functions in my application, i.e.
All the recursive public functions of the object
All private scope functions
All anonymous functions
Anything else I might have forgotten.
My purpose in doing this is to implement a "JS Profiler" that will run alongside my application during automated testing, and output performance data to logs.
I need this for testing purposes, so the solution must have minimal changes to the actual code of my application.
Possible solutions I've considered:
Public methods can be easily traversed and replaced using a recursive object traversal function.
Some hack using eval() to be able to access private methods.
Ideally, to handle all cases, I could use a proxy HTTP server (Node.js for example) that will transform each javascript file before sending it to the browser. This way my codebase will remain clean, but my tests will have the necessary changes.
The first 2 are only partial solutions, and the last one seems like an overkill and also a potential "bug factory"...
Does anyone have any other ideas on how to achieve what I need?

Closures in Typescript (Dependency Injection)

I'm getting my butt kicked trying to use TypeScript in a functional style with dependencies. Let's say I want to make a module that depends on another module.
If I wasn't using Dependency Injection it would look like this (in node).
SomeOtherModule = require("SomeOtherModule")
exports.doSomething = function() {
SomeOtherModule.blah()
}
This is how I do it with Dependency Injection
module.exports = function(SomeOtherModule) {
function doSomething() {
SomeOtherModule.blah()
}
return {doSomething: doSomething};
}
In typescript if you define a concrete class or module you can just type the functions as you export them or include them in the class. It's all right next to each other.
But since I can't define a module inside the DI function, the only way to do this that I can see would be to define an interface for the object I'm returning separately, which is annoying, because I want to have the type annotations in line with the definitions.
What's a better way to do this?
This will probably give you a good start: http://blorkfish.wordpress.com/2012/10/23/typescript-organizing-your-code-with-amd-modules-and-require-js/
I don't know if this is the best way to set it up. But I got it to work.
I ended up dropping AMD on my project, since I'm also using AngularJS and they step on each other's toes. I did keep using that same DI pattern through, so it looks like this in the end.
I'm pretty happy with it. I experimenting uses classes instead (you can get really close if you keep your module stateless and have the constructor be the injector function), but I didn't like having to use this for all the dependencies.
Also, classes don't actually buy me anything, because if I were coding to an interface I'd have to define the types twice anyway.
interface IMyService {
doSomething();
}
module.exports = function(SomeOtherModule) {
return {doSomething: doSomething}
function doSomething() {
SomeOtherModule.blah()
}
}

how to unit-test private methods in jquery plugins?

Perhaps this is a bit of a novice JQuery question but:
proper jquery plugins are written inside a closure
thus only methods defining the plugin interface are accessible from the outside
sometimes (or many times) one may need helper methods that it doesn't make sense to expose as part of plugin interface (for example because they alter internal state).
how do those get unit-tested?
For example, looking at blockUI plugin, how can methods install, remove, reset get unit-tested?
To draw a parallel, in Java I would:
create a BlockUI interface containing public methods only (by definition)
create a BlockUIImpl class implementing the above interface. This class would contain install(), remove(), reset() methods that could be public, or (package) protected
So, I would unit-test the Impl but client programmers would interact with the plugin via BlockUI interface.
The same applies here as with any other language and testing privates: To test private methods, you should exercise them via the public interface. In other words, by calling your public methods, the private methods get tested in the process because the public methods rely on the privates.
Generally private methods are not tested separately from the public interface - the entire point is that they are implementation details, and tests should generally not know too much about the specifics of the implementation.
Code written inside a function in JavaScript, or closure as you called it, is not necessarily isolated from the outside of that function.
It is useful to know that functions have visibility of the scope in which they are defined. Any closure you create carries the scope, and therefore functions, of the code that contains it.
This simple example with a jQuery plugin and an artificial "namespace" might serve to prove this assumption:
// Initialise this only when running tests
my_public_test_namespace = function(){};
jQuery.fn.makeItBlue = function() {
makeItBlue(this);
function makeItBlue(object) {
object.css('color','blue');
}
if(typeof my_public_test_namespace != "undefined") {
my_public_test_namespace.testHarness = function() {
return {
_makeItBluePrivateFn: makeItBlue
}
};
}
};
$("#myElement").makeItBlue(); // make something blue, initialise plugin
console.debug(my_public_test_namespace.testHarness()._makeItBluePrivateFn);
But don't forget you shouldn't really test privates. ;)
I came up with the same question and after navigating and finding answers that not really apply, here's what I ended up to solve a similar problem.
Problem: "I have a widget that has a behavior I want to test to ensure it's working as expected, some of the methods are called internally because they have to solve internal behavior, exposing them as public does not make sense because they wont be called from outside, testing the public methods means you wont test the internals of the widget, so finally what can I do?"
Solution: "Creata a test widget that exposes the methods you are interested in testing and use them in the qunit, here is the example:"
// Namespaces to avoid having conflicts with other things defined similarly
var formeditortest = formeditortest || {};
// widget that inherits from the container I want to test
$.widget( "app.testcontainer", $.app.container, {
executeDrop: function(drop, helper) {
var self = this;
self._executeDrop(drop, helper);
}
});
// Test cases
formeditortest.testDropSimple = function(assert) {
var container = $("<div />");
container.testcontainer();
container.testcontainer("drop", 0, 3);
assert.equal(true, $(innerDiv.children()[0]).hasClass("droparea"));
});
QUnit.test(name, function( assert ) {
formeditortest.testDropSimple(assert);
formeditortest.testDropBottom(assert);
});
Using this method the inherited testcontainer could have the preparation required to test elements and then the qunit will handle the test, this solves my problem, hope this works for someone else that is having troubles to approach these kind of tests.
Critics? welcome to comment, I want to improve this if I'm doing something silly!!!

Storing complex GWT-Types as Javascript

In my first GWT module I want to store a JavaScript object, later on I want to receive this object in my second GWT module.
Everything works fine for primitive types, but my complex type will always have all fields set to "undefined".
My class, that I want to transfer from one module to the other:
public class SomeThing {
public Set<String> strings = new HashSet<String>();
}
The entry point of my first module looks like this:
public class EntryA implements EntryPoint {
#Override
public void onModuleLoad() {
// define test data
SomeThing someThing = new SomeThing();
someThing.strings.add("hallo123");
// save data to JavaScript
saveToJavaScript(someThing);
// read and show saved data
Window.alert("ModuleA:"+readFromJavaScript());
Window.alert("ModuleA strings:"+readFromJavaScript().strings);
}
private native void saveToJavaScript(SomeThing thing) /*-{
$wnd.storedThing = thing;
}-*/;
private native SomeThing readFromJavaScript() /*-{
return $wnd.storedThing;
}-*/;
}
The entry point of my second module looks like this:
public class EntryB implements EntryPoint {
#Override
public void onModuleLoad() {
// run delayed, so that ModuleA will be executed first
new Timer() {
#Override
public void run() {
// read and show saved data
Window.alert("ModuleB:"+readFromJavaScript());
Window.alert("ModuleB strings:"+readFromJavaScript().strings);
}
}.schedule(5000);
}
private native SomeThing readFromJavaScript() /*-{
return $wnd.storedThing;
}-*/;
}
I am compiling each module separately. Both generated JavaScript files are included in one html file.
The output is:
ModuleA:moduleA.client.SomeThing#a
ModuleA strings:[hallo123]
ModuleB:moduleA.client.SomeThing#a
ModuleB strings:undefined
Does anyone have an idea how to store such complex types? Let me know, if you need some more information.
UPDATE
I found out, that it actually works, if I am "refreshing" the fields in JavaScript. I have no idea why this works!
private native SomeThing readFromJavaScript() /*-{
var a = $wnd.storedThing;
a.#moduleA.client.SomeThing::strings = a['moduleA_client_SomeThing_strings'];
return $wnd.storedThing;
}-*/;
Nevertheless I need a generic approach, which allows to transfer any object - and I don't want to have to mention every possible field... :(
Maybe this has something to do the way modules are loaded.
The preferred way to load multiple modules it is described in: Loading multiple modules in an HTML host page:
If you have multiple GWT modules in your application, there are two ways to
approach loading them.
1. Compile each module separately and include each module with a separate
<script> tag in your HTML host page.
2. Create a top level module XML definition that includes all the modules you
want to include. Compile the top level module to create a single set of
JavaScript output.
...[cut for brevity] The second approach is strongly recommended.
The reason that you can't read fields between GWT modules is that each module is compiled and obfuscated independently. This means that SomeThing.strings could be mapped to .a in one module and `.q' in another. Your "refresh" trick only works because compiling the module in detailed mode usually results in the same name.
You might want to consider using the AutoBeans framework, which supports JSON-encoding the objects in a stable manner.

Categories

Resources