I'm looking for a solution to setup a swift SDK (e.g. framework) that access shared business logic from a javascript bundle (shared with for example an android app or SDK). I don't want to use webviews etc...
shared business logic in javascript bundle
I have a Javascript bundle containing the shared logic of a project, available as an api, e.g. myBundle.js which exports several modules/functions/objects.
I want to build a native Swift app, that uses the above mentioned Swift SDK and as such can access the business logic from myBundle.js:
// module "myModule.js"
function cube(x) {
return x * x * x;
}
const foo = Math.PI + 1.0;
export { cube, foo };
Step 1: In my Swift project I would like to
import Foundation
public class MySwiftClass {
func doSomeStuffBasedOnJavascriptCode() {
// access MyBundle.js, and run 'cube' method on 'foo' from MyModule
let x = cube(foo);
NSLog("result: %l", x);
}
}
Step 2: In addition, accessing the Swift code from JS would also be very useful!
I tried: ...using Nativescript with iOS, and was able to setup a Swift app that runs the V8 runtime. This setup allows me to access the native Swift code from the javascript bundle (Nativescript with swift). So step 2 is covered by that. However, Nativescript setups the runtime internally and 'runs the main context of the bundle' (expecting it to contain an application 'run'?). It wraps the logic of the underlying V8 runtime with a very simple Nativescript interface:
Config* config = [[Config alloc] init];
config.IsDebug = isDebug;
config.LogToSystemConsole = isDebug;
config.MetadataPtr = metadataPtr;
config.BaseDir = baseDir;
config.ArgumentsCount = argc;
config.Arguments = argv;
[NativeScript start:config];
Is there an easy way to access the underlying runtime's context and call functions on modules from the bundle explicitly throughout the Swift code? Just like you can add Swift code throughout the JS code in the bundle...
So, I'm really looking for a bidirectional solution where javascript uses Swift code and Swift code uses JS.
Other solutions are also welcome!
Related
I am currently using the following package where the readme illustrates the following
final bool loaded = await JsIsolatedWorker().importScripts(['test.js']);
I am using the isolate worker package so my code can work on web and outside of web. I would like to generate javascript code from my dart code. I have one file with a top level function and I use
dart compile js -O0 -o test.js test.dart
which I found here
https://dart.dev/tools/dart2js
this is my dart file
void main(List<String> args) {
doComputation('');
}
String doComputation(String input) {
return 'output';
}
I can generate javascript only if I have a main function but this generates a javascript file where the doComutation is not a top level function, so I am not sure if the package can call the function. It looks like it generates an entire program instead of just generating one function.
The generated file is too long to post
So what my question comes down to is this. Is there a way to generate javascript from dart for 1 function with its dependencies included instead of having to generate the entire program? So that I can call this function from dart.
I am not an expert but I also had this problem. Here's what worked for me:
In web_worker.dart:
import 'package:js/js.dart';
main(){
allowInterop(doComputation);
}
#JS('doComputation')
String doComputation(String computationInput) {
// Replace this with your actual computation.
final String computationOutput = "output";
return computationOutput;
}
Compile it using:
$ dart compile js web_worker.dart -o webWorker.js
Manually edit webWorker.js, the JS file generated by the compiler:
Delete this line at the top of the file:
(function dartProgram() {
and the line at the bottom of the file with the corresponding closing brace:
})();
I don't understand what's going on here but I found the Javascript version of the function doComputation() defined in webWorker.js as a property of the object "A".
I defined a wrapper at the top of the file like this:
function doComputation(computationInput) {
return A.doComputation(computationInput)
}
and then I was able to use the file with JsIsolatedWorker like this:
final bool loaded =
await JsIsolatedWorker().importScripts(['../web/webWorker.js']);
if (loaded) {
final String computationResult = await JsIsolatedWorker()
.run(functionName: 'doComputation', arguments: computationInput);
} else {
debugPrint('Web worker is not available');
}
If someone who understands this better can elaborate or improve on this solution, that would be great. I don't really have any idea what I'm doing. I'm just posting this to hopefully help other people save time in the future by sharing what worked for me.
You can use js package to call JavaScript APIs from Dart code, or vice versa. To make a Dart function callable from JavaScript by name, use a setter annotated with #JS().
#JS()
library callable_function;
import 'package:js/js.dart';
/// Allows assigning a function to be callable from `window.functionName()`
#JS('functionName')
external set _functionName(void Function() f);
/// Allows calling the assigned function from Dart as well.
#JS()
external void functionName();
void _someDartFunction() {
print('Hello from Dart!');
}
void main() {
_functionName = allowInterop(_someDartFunction);
}
JavaScript code may now call functionName() or window.functionName().
Check google chartjs https://github.com/google/chartjs.dart/tree/master/example for a complete example.
From dart2js "Helping dart2js generate better code" there is a tip :
Don’t worry about the size of your app’s included libraries. The dart2js compiler performs tree shaking to omit unused classes, functions, methods, and so on. Just import the libraries you need, and let dart2js get rid of what you don’t need.
Related to this post https://stackoverflow.com/a/21124252/3733730.
You can do this instead by using the package dart2js
one make a program for your main.dart dart2js where the function or command you wanted to make is there, then create a separate dart file what function you wanted to call, and it will fix your problem, it is a separate file but will only execute one function that you needed to do so and that is the separate file
I was playing with WebAssembly and so far and I was able to manage emscripten compile my test C++ project to wasm file
em++ provides me 2 files i.e.
mainTest.js mainTest.wasm
When I load mainTest.js in my html page then I get a JavaScript object called
"Module".
I did found how to call C++/wasm methods from javascript i.e. something like:
var myTestInteger = Module._callMyTestMethod();
and read strings from the
Module.wasmMemory.buffer
, but I do NOT understand how to call JavaScript from C++ code.
i.e. I would like to be able to do something like that:
#ifdef __cplusplus
extern "C" {
#endif
extern void testExternJSMethod();
int main()
{
cout << " Hello From my Test1 !" << endl;
testExternJSMethod();
return 0;
}
int EMSCRIPTEN_KEEPALIVE callMyTestMethod(){
return 26;
}
#ifdef __cplusplus
}
#endif
and the my js method testExternMethod that I am loading in another js file called utils.js
function testExternMethod() {
console.log("Hello from testExternMethod!" + )
}
Here I would like to call the JavaScript testExternJSMethod from C++.
When I run the page in Firefox get "-1" in the debugger console.
So what I am missing in this case? Unfortunately The Mozilla documentation is giving only examples in those S-expressions instead of C++.
What am I missing in example? In C++ I have defined the method with the extern keyword i.e.
extern void testExternJSMethod();
but I get the feeling that that is not all I have to do.
I believe that I should somehow link that JavaScript method to the Module somehow but I do not know how.
Module.asm gives me the exports. Which method call should give me the imports? since I believe that this _testExternJSMethod() should be in some imports method I can not figure out how to get to it.
I'm not exactly sure of your use case, but you are missing important steps to be able to use your function testExternalJSMethod. You have two options here:
Option 1 - Library
1 - Define your function in c/c++ .
extern void testExternalJSMethod();
2 - Create a file called myLibrary.js
3 - The JS function needs to be added to the LibraryManager in your library file with the following code:
function makeAlert(text) {
alert(text);
}
if (typeof mergeInto !== 'undefined') mergeInto(LibraryManager.library, {
testExternalJSMethod: function() {
makeAlert("Hello world");
}
});
4 - If testExternalJSMethod depends on anything outside of its own scope (for example, makeAlert above), make sure to include the script in your html page
<script async type="text/javascript" src="myLibrary.js"></script>
5 - Add option --js-library to your emcc command, and immediately after the relative path to myLibrary.js
emcc ... --js-library myLibrary.js
Option 2 - Passing Pointers
1 - Define your javascript function type in c/c++
typedef void testExternalJSMethod()
2 - Wherever you want this function to be used, accept an int param which will be the function pointer, and cast the pointer to your function
void passFnPointer(int ptr) {
((testExternalJSMethod*)ptr)();
}
3 - Use emscripten's addFunction() and store its returned value (the pointer in c/c++)
var fnPtr = Module.addFunction(function () {
alert("You called testExternalJSMethod");
});
4 - Use the stored pointer value from step 3 to pass to our function passFnPointer
var passFnPointer = Module.cwrap('passFnPointer', 'undefined', ['number']);
passFnPointer(fnPtr);
5 - Add option -s RESERVED_FUNCTION_POINTERS to your emcc command
emcc ... -s RESERVED_FUNCTION_POINTERS=10
Have you tried looking at the Emscripten documentation? It has a whole section on interacting with code that details how to expose C / C++ functions to JavaScript and call JavaScript functions from C / C++.
Emscripten provides two main approaches for calling JavaScript from C/C++: running the script using emscripten_run_script() or writing “inline JavaScript”.
It is worth noting that the Mozilla documentation details plain WebAssembly, whereas Emscripten adds a lot more framework and tooling around WebAssembly in order to make it easier to port large C / C++ codebases.
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!
My goal is to use the C libraries to form web apps.
I have chosen the way to do that via using "SWIG" tool.
The Swig tool requires three things:
.c file which defines all the functions.
.i file also called interface file which is creating the
interface to load the APIs wherein I used the extern keyword.
APP written in Javascript extension (.js file).
I used SWIG tool to compile and run this app to verify the .js file has made correctly.
The application is running fine on XMING X11 window.
On compilation it creates _wrap.o, .o file and libFILENAME.so.
Now I want to run this app on browser page.
For this I have used the webkit clutter port which gives us the MxLauncher code.
I'm using webkit_iweb_view_load_uri(WEBKIT_IWEB_VIEW(view), "filename.html"); API to load my html file to run that Javascript on my webpage view.
I'm linking the .so created at the compilation time.
Error Message: JS CONSOLE: file:///filename.js:
ReferenceError: Can't find variable: example
filename.c
int gcd(int x, int y) `enter code here`{
int g;
g = y;
while (x > 0) {
g = x;
x = y % x;
y = g;
}
return g;
}
filename.i
%module example
extern int gcd(int x, int y);
filename.js
x = 42;
y = 105;
g = example.gcd(x,y);
How to get my goal to be achieved?
You also need to tell WebKit/JavaScriptCore at runtime about your bindings (this is in addition to linking with filename_wrap.o).
Specifically you need to bind them to the global JavaScript object (in order to invoke per your .js examples). A callback on the WebKit window can be used to get a timely reference to the global JavaScript context, and then you can register your functions onto it.
Adapting this example of hooking into the window-object-cleared signal the code could look similar to this:
/* the window callback -
fired when the JavaScript window object has been cleared */
static void window_object_cleared_cb(WebKitWebView *web_view,
WebKitWebFrame *frame,
gpointer context,
gpointer window_object,
gpointer user_data)
{
/* Add your classes to JavaScriptCore */
example_init(context); // example_init generated by SWIG
}
/* ... and in your main application set up */
void yourmainfunc()
{
....
g_signal_connect (G_OBJECT (web_view), "window-object-cleared",
G_CALLBACK(window_object_cleared_cb), web_view);
webkit_web_view_load_uri (WEBKIT_WEB_VIEW (web_view), "file://filename.html");
...
}
Depending on which branch of SWIG you are using you may need to generate the example_init function yourself (check filename.cxx); for reference here is what an initializer function to register wrapped C functions would look like in SWIG:
int example_init(JSContextRef context) {
JSObjectRef global = JSContextGetGlobalObject(context);
...
jsc_registerFunction(context, global, "gcd", _wrap_gcd);
...
}
NOTE -- SWIG does not yet officially support JavaScript; the above refers to using the work-in-progress (non-production) SWIG branches.
References:
SWIG-V8 source and its Javascript documentation
swig-jsc source and its example of registering bindings
SWIG JavaScriptCore GSoC project source (Google Summer of Code 2012)
Webkit: Extending JavaScript article-- tutorial / example code
I have a question related to the way of using the RequireJs.
Our app should grow a lot in the near feature, so the major problem is to prepare a skeleton that would be followed by the developers involved in the project.
we tried this kind of wrapper on RequireJS(trying to enforce the OOP approach):
//each file will contains such a definition
//this file should be located under: appNamespace/Client/Widget.js
//attaching the class definition to the namespace
with ({public : appNamespace.Client})
{
//using a Class defined in the file: appNamespace/DomainModel/DataTable.js
var dt = using('appNamespace.DomainModel.DataTable');
//Class definition
public.Widget = function(iName)
{
//private variable
var dSort = new dt.SortableTable();
this.Draw = function(iRegion)
{
//public method implementation
}
}
}
Or, we could use the natural RequireJS model like structure:
<pre><code>
//this module definition should be located in the file: appNamespace/Client/Widget.js
define('appNamespace/DomainModel/DataTable', function(dt)
{
function Widget(iName)
{
//private variable
var dSort = new dt.SortableTable();
this.Draw = function(iRegion)
{
//public method implementation
}
}
return Widget;
})
I would prefer the first example because:
1. it will enforce developers to write scripts in a more OOP style
2. it looks like the C# or Java notation - so it will allow a faster switching between the back-end code and the client code
3. I really don't like the model structure because it allows to write any style of code. More of that, it's not enough to define your class, you should define the API that this file is exposing - so you can actually define an API that has no relation to what that file contains.
So - why would I use the second example - the natural RequireJS model style?
Is there anything that I miss?
Any suggestion is welcome