Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How does calling from JS to Obj-C work? #34

Closed
jlongster opened this issue Feb 3, 2015 · 14 comments
Closed

How does calling from JS to Obj-C work? #34

jlongster opened this issue Feb 3, 2015 · 14 comments
Labels
Resolution: Locked This issue was locked by the bot.

Comments

@jlongster
Copy link

I added a method to my TeapotController object:

- (void)touchHappened {
  RCT_EXPORT();
  NSLog(@"happened");
  CC3Vector pos = teapotNode_.position;
  [teapotNode_ setSizeX: 10];
  [teapotNode_ invalidateGlobalTransformMatrix];
}

Looking through the source, it looks like I can add RCT_EXPORT to export a method. I can then call it like this:

var controller = require('NativeModules').TeapotController;
controller.touchHappened();

If I just log something in touchHappened, this works. But when I try to access an instance variable like teapotNode_, nothing happens... but I don't see how this works anyway. How is it exporting a method from an instance? What I have multiple instances of TeapotController? I don't see how I could require individual instances.

@jlongster
Copy link
Author

Nevermind, I thought that the method wasn't even running for some reason but it's definitely running. For some reason my code isn't updating some state like I thought it would. Still, is that the right way to expose an Obj-C method?

@jlongster
Copy link
Author

Hm, when I do NSLog(@"%x", teapotNode_); it prints 0. I don't know Obj-C too well, but does it not crash when sending messages to a null pointer? Anyway, it does seem to be calling the method as a static method, not as an instance method. How do I expose an instance method?

EDIT: I'm assuming it's possible because inside RCTUIManager.m it exports several instance methods. Some of them even reference self. But I see nothing outside of RCT_EXPORT() that sets the instance for the exporter...

EDIT2: Oh, I see in initWithJavaScriptExecutor that it allocates a single instance of the class, so the class is meant to be a single module instance. Might be nice to have a way to register an existing instance as a module?

@sophiebits
Copy link
Contributor

I don't know Obj-C too well, but does it not crash when sending messages to a null pointer?

No, it doesn't crash; it returns nil.

@jlongster
Copy link
Author

Ok.

I added a getModule method on RCTBridge so now I just do TeapotController *vc = [bridge getModule:@"TeapotController"]; after creating the root view. I'm sure this is not the right way to do it. But I hacked. And it was fun.

@a2
Copy link
Contributor

a2 commented Feb 3, 2015

I think two things are going on here:

A) Not sure if you're already doing this, but your TeapotController needs to conform to the RCTNativeModule protocol in order to be registered with the bridge.

B) If a class conforms to the RCTNativeModule protocol, it will be instantiated by the RCTBridge for use in the RCTRootView. Where do you set the teapotNode_ ivar?

@jlongster
Copy link
Author

The teapotNode_ is just a scene node object that is created in the viewDidLoad controller method. I did add that protocol:

@interface TeapotController : GLViewController<RCTNativeModule> {
    TeapotNode *teapotNode_;
    BOOL tapIsValid_;
    CGPoint lastDragPoints_[2];
    CGPoint touchStartPoint_;
}

And it's all working fine. The problem is that the bridge instantiates the TeapotController class, but I don't see any way to get to it through the RCTRootView class. I had to add a method myself to dig into the internal modules and return the instance.

@jlongster
Copy link
Author

To leave a clearer comment about what is needed: we need to be able to access the modules (that are actually class instances) that the bridge creates to have a generic way of talking between iOS and JS. These classes will have state, and when a callback (say from user input) calls back into the iOS world we need access to it.

@vjeux
Copy link
Contributor

vjeux commented Feb 7, 2015

@jlongster mind updating the title of the issue to reflect what needs to be done here outside of documentation?

@jaygarcia
Copy link
Contributor

Are the discussed items here obsolete? I grep'ed for RCTNativeModule and initWithJavaScriptExecutor across the entire repo and found nothing.

@nicklockwood
Copy link
Contributor

@jaygarcia RCTNativeModule is now called RCTBridgeModule.

@jaygarcia
Copy link
Contributor

Thanks :) I should have replied to my comment. I have been able to grasp things now from the JS <> ObJC side. It wasn't easy, but with enough digging through the ReactKit code, I figured much of what I needed to out.

The Macros are simply genius. I wish the Cordova guys were smart enough to do that.

@frantic
Copy link
Contributor

frantic commented Mar 22, 2015

Hey @jaygarcia , see new guide at https://github.com/facebook/react-native/blob/master/docs/NativeModulesIOS.md. Let us know if you think something should be added there.

@jaygarcia
Copy link
Contributor

Should this be closed?

@frantic , i think the article is pretty air tight for the moment :)

@frantic
Copy link
Contributor

frantic commented Mar 30, 2015

Cool! :)

@frantic frantic closed this as completed Mar 30, 2015
dustturtle added a commit to dustturtle/react-native that referenced this issue Jul 6, 2016
…crash on simulator, on device I got nothing but app freezed)!

My app has an old version of JSONKit which is still using MRC. I think JSONKit is not needed if system version is available. Kicking out of JSONKit will make react native stronger.
Crash stack:
* thread facebook#11: tid = 0xbd672f, 0x000000010a10edeb imobii-waiqin`jk_encode_add_atom_to_buffer(encodeState=0x00007f9b820a1000, objectPtr=22 key/value pairs) + 16971 at JSONKit.m:2807, name = 'com.facebook.React.JavaScript', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x000000010a10edeb imobii-waiqin`jk_encode_add_atom_to_buffer(encodeState=0x00007f9b820a1000, objectPtr=22 key/value pairs) + 16971 at JSONKit.m:2807
    frame facebook#1: 0x000000010a10ef67 imobii-waiqin`jk_encode_add_atom_to_buffer(encodeState=0x00007f9b820a1000, objectPtr=2 key/value pairs) + 17351 at JSONKit.m:2811
    frame facebook#2: 0x000000010a10ef67 imobii-waiqin`jk_encode_add_atom_to_buffer(encodeState=0x00007f9b820a1000, objectPtr=25 key/value pairs) + 17351 at JSONKit.m:2811
    frame facebook#3: 0x000000010a10e768 imobii-waiqin`jk_encode_add_atom_to_buffer(encodeState=0x00007f9b820a1000, objectPtr=@"3 elements") + 15304 at JSONKit.m:2778
  * frame facebook#4: 0x000000010a10a26a imobii-waiqin`-[JKSerializer serializeObject:options:encodeOption:block:delegate:selector:error:](self=0x00007f9b831fbc80, _cmd="serializeObject:options:encodeOption:block:delegate:selector:error:", object=@"3 elements", optionFlags=0, encodeOption=10, block=0x0000000000000000, delegate=0x0000000000000000, selector=<no value available>, error=domain: class name = NSInvocation - code: 0) + 2250 at JSONKit.m:2876
    frame facebook#5: 0x000000010a109992 imobii-waiqin`+[JKSerializer serializeObject:options:encodeOption:block:delegate:selector:error:](self=JKSerializer, _cmd="serializeObject:options:encodeOption:block:delegate:selector:error:", object=@"3 elements", optionFlags=0, encodeOption=10, block=0x0000000000000000, delegate=0x0000000000000000, selector=<no value available>, error=domain: class name = NSInvocation - code: 0) + 178 at JSONKit.m:2831
    frame facebook#6: 0x000000010a10f700 imobii-waiqin`-[NSArray(self=@"3 elements", _cmd="JSONStringWithOptions:error:", serializeOptions=0, error=domain: class name = NSInvocation - code: 0) JSONStringWithOptions:error:] + 112 at JSONKit.m:2985
    frame facebook#7: 0x000000010ac13c02 imobii-waiqin`_RCTJSONStringifyNoRetry(jsonObject=@"3 elements", error=domain: class name = NSInvocation - code: 0) + 338 at RCTUtils.m:49
    frame facebook#8: 0x000000010ac13990 imobii-waiqin`RCTJSONStringify(jsonObject=@"3 elements", error=0x0000000000000000) + 128 at RCTUtils.m:77
    frame facebook#9: 0x000000010ab5fdfa imobii-waiqin`__27-[RCTContextExecutor setUp]_block_invoke_2(.block_descriptor=<unavailable>, moduleName=@"UIManager") + 218 at RCTContextExecutor.m:363
    frame facebook#10: 0x00000001134495cc CoreFoundation`__invoking___ + 140
    frame facebook#11: 0x000000011344941e CoreFoundation`-[NSInvocation invoke] + 286
    frame facebook#12: 0x000000010db13db3 JavaScriptCore`JSC::ObjCCallbackFunctionImpl::call(JSContext*, OpaqueJSValue*, unsigned long, OpaqueJSValue const* const*, OpaqueJSValue const**) + 451
    frame facebook#13: 0x000000010db13926 JavaScriptCore`JSC::objCCallbackFunctionCallAsFunction(OpaqueJSContext const*, OpaqueJSValue*, OpaqueJSValue*, unsigned long, OpaqueJSValue const* const*, OpaqueJSValue const**) + 262
    frame facebook#14: 0x000000010db14bad JavaScriptCore`long long JSC::APICallbackFunction::call<JSC::ObjCCallbackFunction>(JSC::ExecState*) + 573
    frame facebook#15: 0x000000010dade340 JavaScriptCore`JSC::LLInt::setUpCall(JSC::ExecState*, JSC::Instruction*, JSC::CodeSpecializationKind, JSC::JSValue, JSC::LLIntCallLinkInfo*) + 528
    frame facebook#16: 0x000000010dae535d JavaScriptCore`llint_entry + 22900
    frame facebook#17: 0x000000010dadf7d9 JavaScriptCore`vmEntryToJavaScript + 326
    frame facebook#18: 0x000000010d9b1959 JavaScriptCore`JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*) + 169
    frame facebook#19: 0x000000010d9985ad JavaScriptCore`JSC::Interpreter::executeCall(JSC::ExecState*, JSC::JSObject*, JSC::CallType, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) + 493
    frame facebook#20: 0x000000010d76cb7e JavaScriptCore`JSC::call(JSC::ExecState*, JSC::JSValue, JSC::CallType, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) + 62
    frame facebook#21: 0x000000010d929a55 JavaScriptCore`JSC::callGetter(JSC::ExecState*, JSC::JSValue, JSC::JSValue) + 149
    frame facebook#22: 0x000000010dad49fb JavaScriptCore`llint_slow_path_get_by_id + 2203
    frame facebook#23: 0x000000010dae22f0 JavaScriptCore`llint_entry + 10503
    frame facebook#24: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#25: 0x000000010dae52fd JavaScriptCore`llint_entry + 22804
    frame facebook#26: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#27: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#28: 0x000000010dae52fd JavaScriptCore`llint_entry + 22804
    frame facebook#29: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#30: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#31: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#32: 0x000000010dae552a JavaScriptCore`llint_entry + 23361
    frame facebook#33: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#34: 0x000000010dae5368 JavaScriptCore`llint_entry + 22911
    frame facebook#35: 0x000000010dadf7d9 JavaScriptCore`vmEntryToJavaScript + 326
    frame facebook#36: 0x000000010d9b1959 JavaScriptCore`JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*) + 169
    frame facebook#37: 0x000000010d998264 JavaScriptCore`JSC::Interpreter::execute(JSC::ProgramExecutable*, JSC::ExecState*, JSC::JSObject*) + 10404
    frame facebook#38: 0x000000010d7a8786 JavaScriptCore`JSC::evaluate(JSC::ExecState*, JSC::SourceCode const&, JSC::JSValue, WTF::NakedPtr<JSC::Exception>&) + 470
    frame facebook#39: 0x000000010d9f6fb8 JavaScriptCore`JSEvaluateScript + 424
    frame facebook#40: 0x000000010ab6379e imobii-waiqin`__68-[RCTContextExecutor executeApplicationScript:sourceURL:onComplete:]_block_invoke.264(.block_descriptor=<unavailable>) + 414 at RCTContextExecutor.m:589
    frame facebook#41: 0x000000010ab63262 imobii-waiqin`__68-[RCTContextExecutor executeApplicationScript:sourceURL:onComplete:]_block_invoke(.block_descriptor=<unavailable>) + 498 at RCTContextExecutor.m:589
    frame facebook#42: 0x000000010ab63df8 imobii-waiqin`-[RCTContextExecutor executeBlockOnJavaScriptQueue:](self=0x00007f9b832f6040, _cmd="executeBlockOnJavaScriptQueue:", block=0x00007f9b80c92970) + 248 at RCTContextExecutor.m:627
    frame facebook#43: 0x000000010eb1d7a7 Foundation`__NSThreadPerformPerform + 283
    frame facebook#44: 0x0000000113486301 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame facebook#45: 0x000000011347c22c CoreFoundation`__CFRunLoopDoSources0 + 556
    frame facebook#46: 0x000000011347b6e3 CoreFoundation`__CFRunLoopRun + 867
    frame facebook#47: 0x000000011347b0f8 CoreFoundation`CFRunLoopRunSpecific + 488
    frame facebook#48: 0x000000010ab5e41b imobii-waiqin`+[RCTContextExecutor runRunLoopThread](self=RCTContextExecutor, _cmd="runRunLoopThread") + 363 at RCTContextExecutor.m:284
    frame facebook#49: 0x000000010ebc012b Foundation`__NSThread__start__ + 1198
    frame facebook#50: 0x00000001140869b1 libsystem_pthread.dylib`_pthread_body + 131
    frame facebook#51: 0x000000011408692e libsystem_pthread.dylib`_pthread_start + 168
    frame facebook#52: 0x0000000114084385 libsystem_pthread.dylib`thread_start + 13
lelandrichardson pushed a commit that referenced this issue Aug 22, 2017
@facebook facebook locked as resolved and limited conversation to collaborators May 29, 2018
@react-native-bot react-native-bot added the Resolution: Locked This issue was locked by the bot. label Jul 23, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Resolution: Locked This issue was locked by the bot.
Projects
None yet
Development

No branches or pull requests

8 participants