NativeScript / ios-jsc

NativeScript for iOS using JavaScriptCore
http://docs.nativescript.org/runtimes/ios
Apache License 2.0
299 stars 59 forks source link

Integration with other libraries #537

Closed bolismauro closed 8 years ago

bolismauro commented 8 years ago

Hi, I'm trying to create a project that contains both the NativeScript ios-runtime and another library that uses the system JSCore. This second library crashes when we force it to use the NS JSCore with EXC_BAD_ACCESS.

We noticed this message in the readme of the sample-ios-embedded:

Using the built-in JavaScriptCore framework and NativeScript in the same app will likely not work.

Why is it so? Is it possible to workaround this somehow? We were thinking of renaming all the classes/functions of the NS embedded JSCore library in order to have two different JSCore instances at the same time. In this way we would be able to run the NS ios-runtime with the embedded version and the other library with the system JSCore.

Do you think it is possible? Would it work?

Thanks!

jasssonpet commented 8 years ago

I think that this discussion may be related: https://groups.google.com/forum/#!topic/nativescript/6qyNWP65C4A

bolismauro commented 8 years ago

Hi, I've tried to follow exactly what the last post said but it still crashes. I have to say that I'm not knowledgeable at all about this kind of stuff, so I may have done something wrong

Is the way we were thinking (rename everything) doable?

bolismauro commented 8 years ago

So basically I followed the instructions on the blog post and I had compilation errors (even without cocoapods). At this point I've added the NS framework to my project (by dragging it basically) and the result was a duplicate symbols errors.

I removed the .a / headers from the HEADER_SEARCH_PATH and the project started to compile and run. What I don't understand now is

Right now the main.m file references JSCore using #include <JavascriptCore/JavascriptCore.h> and it is not in a namespace in any way

KristinaKoeva commented 8 years ago

Hey,

The duplicate symbols error makes me think that you've linked against both JavaScriptCore frameworks but to make sure you can execute lldb image list command. In order to test it on our side you could share the library that you are trying to use.

bolismauro commented 8 years ago

Basically Nativescript.framework has an embedded version of JavascripCore and nativescript is fine with that.

I need to use another library, which crashes with the NS embedded JSCore but works with the system one.

So basically I'm searching a way to have two JSCore frameworks at the same time. What I'm trying to do is to avoid that the NativeScript.framework exports JSCore symbols. In this way, theoretically, NativeScript uses the embedded JSCore while I can still load the system JSCore outside the NativeScript framework.

Does it make sense? Do you think it is doable?

KristinaKoeva commented 8 years ago

It depends on the scenario that you are trying to support. If you are having a NativeScript application created with NativeScript CLI (through tns create application ) it is perfectly possible and is done in the thread that @jasssonpet shared with you. If you are trying to embed NativeScript in an iOS application this is achievable but with some additional hacky code. So it would be great if you share what library you are trying to reference, are you using an embedded NativeScript in iOS application, or the default NativeScript application created with NativeScript CLI

bolismauro commented 8 years ago

Hi, so basically I have a big chunk of my application written in React Native. I'm migrating it to a new architecture written on top of your ios-runtime but I can't transition the whole app at the same time.

React Native runs on top of the system JSCore and crashes with your version of JSCore.

What I've done is creating a new project and embed the ios runtime (we only need the runtime, not the whole tns stack). At this point I'm trying to have two different JSCore at the same time to support both your ios-runtime and React Native. An additional point is that React Native runs in parallel with ios-runtime, and I don't need to import it in the NativeScript environment (so the first link you posted is basically not what we are trying to achieve).

To summarise:

KristinaKoeva commented 8 years ago

What seems to be working is:

Link against JavaScriptCore.framework and NativeScript.framework ( in that order ). Then when you call JSEvaluateScript method you are going to call the one that is defined in the system JavaScriptCore which will throw an error (at least it does on my machine). To call JSEvaluateScript from the JavaScriptCore.framework that comes with NativeScript you should

void* framework = dlopen([imagePath cString], 0); 
void* symbol = dlsym(framework, "JSEvaluateScript");
NSString *source = @"var alertView = UIAlertView.alloc().init();"
                        "alertView.title = 'A message from NativeScript';"
                        "alertView.message = 'Hello!';"
                        "alertView.addButtonWithTitle('Dismiss');"
                        "alertView.show();";
JSStringRef script = JSStringCreateWithCFString((__bridge CFStringRef)(source));    
JSValueRef error = NULL;
((void (*)(JSContextRef, JSStringRef , JSObjectRef , JSStringRef, int, JSValueRef* )) symbol)(_runtime.globalContext, script, NULL, NULL, 0, &error);

As I previously said this is really hacky solution. You can alternatively use the executeModule https://github.com/NativeScript/ios-runtime/blob/master/src/NativeScript/TNSRuntime.h#L30 with a path to a JavaScript file but this would be cached and subsequent executions will not work. If those doesn't work for you we could think of exposing a method like executeModule that would evaluate a script.

bolismauro commented 8 years ago

It seems to work very well! Thank you so so much! :)

bolismauro commented 8 years ago

One more thing, it seems that the metadata generator isn't adding the symbols of the classes of my project. It doesn't work both for classes that I've added to my project (and they can be used and imported on the native side) and React Native classes (that are added as sub projects). As said before I'm using the sample-ios-embedded as basic template. Those classes aren't part of any libraries.

I'm aware that I can use frameworks and module map to do that, but I wonder if there is a way to tell the metadata generator to also parse files in my project that are not embedded in frameworks.

ivanbuhov commented 8 years ago

@bolismauro It is not required to include your module.modulemap files in a framework to generate metadata for them. The metadata generator will pick up all module.modulemap files which are located somewhere under target's header/framework search path. It performs the following steps:

  1. Collects all module.modulemap files located under any path in the header/framework search paths of the project.
  2. Parses all these files and produces a list with all headers which are part of any of the collected modules.
  3. Generates metadata for all headers in the list. In order to generate metadata for your files, create a module.modulemap file somewhere under one of the HEADER_SEARCH_PATHS and describe your custom module. The custom module should include all needed headers. The metadata generator should do the rest.
bolismauro commented 8 years ago

It works perfectly, thank you so much for your help!