Upgrade to Pro — share decks privately, control downloads, hide ads and more …

discussion about the bridge

E86b46be0f4e61db6b28becc5493bbab?s=47 januswel
January 26, 2017

discussion about the bridge

discussion about the bridge of React Native

E86b46be0f4e61db6b28becc5493bbab?s=128

januswel

January 26, 2017
Tweet

Transcript

  1. DISCUSSION ABOUT THE BRIDGE 2017/01/26 React Native Meetup #4 by

    januswel
  2. SPEAKER janus_wel CTO@CureApp have developed on native iOS/Android janus_wel januswel

  3. CUREAPP, INC ΞϓϦͰපؾΛ ࣏ྍ͢ΔະདྷΛ ૑଄͢Δ

  4. FOR NASH PATIENT MADE WITH REACT NATIVE now, not available

    for the public
  5. OVERVIEW What is the bridge? What is native modules? Why

    the bridge is needed? advantages for users advantages for developers scripting native with React How does the bridge work? collecting native modules calling native functions calling JavaScript functions back
  6. OVERVIEW What is the bridge? What is native modules? Why

    the bridge is needed? advantages for users advantages for developers scripting native with React How does the bridge work? collecting native modules calling native functions calling JavaScript functions back
  7. WHAT IS THE BRIDGE? connector between JavaScript and native worlds

    collecting native modules loading bundled JavaScript calling native functions and calling JavaScript functions back
  8. WHAT IS NATIVE MODULES? modules implemented in native platforms to

    use platform-specific features to process heavy tasks with multi-thread React Native have lots of native modules and theirs bindings bridge native module native module native module … bundled JavaScript
  9. OVERVIEW What is the bridge? What is native modules? Why

    the bridge is needed? advantages for users advantages for developers scripting native with React How does the bridge work? collecting native modules calling native functions calling JavaScript functions back
  10. ADVANTAGES FOR USERS It's possible to reimplement these components on

    the web, but our reimplementations never feel exactly like their native counterparts, and they also don't get updated automatically with changes to the platform https://code.facebook.com/posts/1014532261909640/react-native-bringing-modern-web-techniques-to-mobile/ Why native is necessary Native behaviors help users to work with your app Apache Cordova reimplements components
  11. ADVANTAGES FOR DEVELOPERS it's harder to lay things out on

    the screen, and we often have to manually compute the size and position of all our views https://code.facebook.com/posts/1014532261909640/react-native-bringing-modern-web-techniques-to-mobile/ On native, however, we need to recompile after every change, even if we just want to shift text a few pixels over on the screen. Why native is difficult Appcelerator Titanium needs recompile
  12. SCRIPTING NATIVE IS TRICKY our UI thread could end up

    being blocked on JavaScript execution. To make this efficient, we know we want to execute our JavaScript off the main thread, but doing so is hard. The first reason it's hard is resource contention. The second reason this is tough is that there's some fixed amount of overhead associated with every round trip between the native environment and the JavaScript virtual machine. https://code.facebook.com/posts/1014532261909640/react-native-bringing-modern-web-techniques-to-mobile/ Scripting native is tricky
  13. SCRIPTING NATIVE IS TRICKY We need to fundamentally change the

    programming model and ensure that our system always passes messages across the thread boundary asynchronously and that we can batch up as many of these messages per frame as possible, minimizing cross-thread communication overhead. https://code.facebook.com/posts/1014532261909640/react-native-bringing-modern-web-techniques-to-mobile/ Scripting native is tricky In order to this goal, the bridge is needed!!
  14. (REACT PROGRAMMING MODEL) Luckily, React gives us the perfect programming

    model to do this correctly. Since React components are just pure, side-effect-free functions that return what our views look like at any point in time, we never need to read from our underlying rendered view implementation in order to write to it. https://code.facebook.com/posts/1014532261909640/react-native-bringing-modern-web-techniques-to-mobile/ Introducing React Native
  15. OVERVIEW What is the bridge? What is native modules? Why

    the bridge is needed? advantages for users advantages for developers scripting native with React How does the bridge work? collecting native modules calling native functions calling JavaScript functions back
  16. 1. loading bundled JavaScript source 2. setting JavaScript executor up

    3. collecting native modules HOW DOES THE BRIDGE WORK? AppDelegate RCTRootView : UIView RCTBridge : NSObject RCTBatchedBridge : RCTBridge * AppDelegate: implementations about app lifecycle * UIView: base class for all views * NSObject: base class for all objects * RCT: abbr. ReaCT
  17. #import "RCTBridgeModule.h" @interface CalendarManager : NSObject<RCTBridgeModule> @end @implementation CalendarManager RCT_EXPORT_MODULE()

    RCT_EXPORT_METHOD(addEvent:(NSString *)name at:(NSString *)location) { RCTLogInfo(@"Create an event: %@ at %@", name, location); } RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback) { NSArray *events = @[@"talk in 21cafe", @"write slides"]; callback(@[[NSNull null], events]); } @end NATIVE MODULE EXAMPLE
  18. @interface, @implementation? https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/ DefiningClasses/DefiningClasses.html The public properties and behavior are

    defined inside the @interface declaration. Once you’ve defined the interface for a class, including the properties and methods intended for public access, you need to write the code to implement the class behavior. @interface CalendarManager : NSObject<RCTBridgeModule>
  19. RCTBridgeModule https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/ WorkingwithProtocols/WorkingwithProtocols.html Objective-C allows you to define protocols, which

    declare the methods expected to be used for a particular situation. @protocol RCTBridgeModule <NSObject> + (NSString *)moduleName; @optional // snip @end A protocol to be implemented in native modules
  20. @“”, @[]? http://clang.llvm.org/docs/ObjectiveCLiterals.html Literals • @“” for NSString • @[]

    for NSArray / NSDictionary
  21. #import "RCTBridgeModule.h" @interface CalendarManager : NSObject<RCTBridgeModule> @end @implementation CalendarManager RCT_EXPORT_MODULE()

    RCT_EXPORT_METHOD(addEvent:(NSString *)name at:(NSString *)location) { RCTLogInfo(@"Create an event: %@ at %@", name, location); } RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback) { NSArray *events = @[@"talk in 21cafe", @"write slides"]; callback(@[[NSNull null], events]); } @end NATIVE MODULE EXAMPLE This is needed to be collected as a native module by the bridge
  22. RCT_EXPORT_MODULE RCT_EXPORT_MODULE() expanded to #define RCT_EXPORT_MODULE(js_name) \ RCT_EXTERN void RCTRegisterModule(Class);

    \ + (NSString *)moduleName { return @#js_name; } \ + (void)load { RCTRegisterModule(self); } definitions by C-lang macro extern __attribute__((visibility("default"))) void RCTRegisterModule(Class); + (NSString *)moduleName { return @""; } + (void)load { RCTRegisterModule(self); }
  23. VISIBILITY? https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/CppRuntimeEnv/Articles/ SymbolVisibility.html Visibility attributes override the value specified with

    the -fvisibility flag at compile-time. Thus, adding the default visibility attribute causes a symbol to be exported in all cases, whereas adding the hidden visibility attribute hides it. extern __attribute__((visibility("default"))) void RCTRegisterModule(Class); + (NSString *)moduleName { return @""; } + (void)load { RCTRegisterModule(self); } Visibility “default” specifies exporting symbols to out of file scope
  24. +? A class method is a method that operates on

    class objects rather than instances of the class. In Objective-C, a class method is denoted by a plus (+) sign at the beginning of the method declaration and implementation extern __attribute__((visibility("default"))) void RCTRegisterModule(Class); + (NSString *)moduleName { return @""; } + (void)load { RCTRegisterModule(self); } Plus (+) sign to declare / implement class methods https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/ ClassMethod.html
  25. @#js_name? https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification When a macro parameter is used with a

    leading ‘#’, the preprocessor replaces it with the literal text of the actual argument, converted to a string constant. Unlike normal parameter replacement, the argument is not macro-expanded first. This is called stringification. #define RCT_EXPORT_MODULE(js_name) \ RCT_EXTERN void RCTRegisterModule(Class); \ + (NSString *)moduleName { return @#js_name; } \ A combination of • string “@“ • stringification feature in C-lang macro “#js_name”
  26. LOAD? Invoked whenever a class or category is added to

    the Objective-C runtime; implement this method to perform class- specific behavior upon loading. https://developer.apple.com/reference/objectivec/nsobject/1418815-load?language=objc extern __attribute__((visibility("default"))) void RCTRegisterModule(Class); + (NSString *)moduleName { return @""; } + (void)load { RCTRegisterModule(self); } A method to set up conditions for the class
  27. RCTRegisterModule? void RCTRegisterModule(Class moduleClass) { // initialization RCTAssert( [moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],

    @"%@ does not conform to the RCTBridgeModule protocol", moduleClass); // Register module [RCTModuleClasses addObject:moduleClass]; } https://developer.apple.com/reference/objectivec/nsobject/1418893-conformstoprotocol Returns a Boolean value that indicates whether the receiver conforms to a given protocol. conformsToProtocol
  28. COLLECTING NATIVE MODULES https://developer.apple.com/reference/objectivec/nsobject/1418815-load?language=objc Run App Load Classes Register self

    as native module RCT_EXPORT_MODULE definitions extern __attribute__((visibility("default"))) void RCTRegisterModule(Class); + (NSString *)moduleName { return @""; } + (void)load { RCTRegisterModule(self); }
  29. INITIALIZING NATIVE MODULES NSMutableDictionary<NSString *, RCTModuleData *> *moduleDataByName = [NSMutableDictionary

    new]; for (Class moduleClass in RCTGetModuleClasses()) { NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass); // snip moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self]; moduleDataByName[moduleName] = moduleData; // snip } Native modules can call JavaScript through the bridge by this
  30. #import "RCTBridgeModule.h" @interface CalendarManager : NSObject<RCTBridgeModule> @end @implementation CalendarManager RCT_EXPORT_MODULE()

    RCT_EXPORT_METHOD(addEvent:(NSString *)name at:(NSString *)location) { RCTLogInfo(@"Create an event: %@ at %@", name, location); } RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback) { NSArray *events = @[@"talk in 21cafe", @"write slides"]; callback(@[[NSNull null], events]); } @end NATIVE MODULE EXAMPLE definitions for native functions
  31. RCT_EXPORT_METHOD + (NSArray<NSString *> *)__rct_export__142 { return @[@"", @"addEvent:(NSString *)name

    at:(NSString *)location"]; } - (void)addEvent:(NSString *)name at:(NSString *)location { RCTLogInfo(@"Create an event: %@ at %@", name, location); } RCT_EXPORT_METHOD(addEvent:(NSString *)name at:(NSString *)location) { RCTLogInfo(@"Create an event: %@ at %@", name, location); } expanded to
  32. MACRO DEFINITIONS #define RCT_EXPORT_METHOD(method) \ RCT_REMAP_METHOD(, method) #define RCT_REMAP_METHOD(js_name, method)

    \ RCT_EXTERN_REMAP_METHOD(js_name, method) \ - (void)method #define RCT_EXTERN_REMAP_METHOD(js_name, method) \ + (NSArray<NSString *> *)RCT_CONCAT(__rct_export__, \ RCT_CONCAT(js_name, RCT_CONCAT(__LINE__, __COUNTER__))) { \ return @[@#js_name, @#method]; \ }
  33. -, __LINE__, __COUNTER__? https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html This macro expands to the

    current input line number, in the form of a decimal integer constant. __LINE__ This macro expands to sequential integral values starting from 0. __COUNTER__ https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/ DefiningClasses/DefiningClasses.html - The minus sign (-) at the front of the method name indicates that it is an instance method
  34. COLLECTING METHODS Method *methods = class_copyMethodList(object_getClass(cls), &methodCount); for (unsigned int

    i = 0; i < methodCount; i++) { Method method = methods[i]; SEL selector = method_getName(method); if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) { IMP imp = method_getImplementation(method); NSArray<NSString *> *entries = ((NSArray<NSString *> *(*)(id, SEL))imp)(_moduleClass, selector); id<RCTBridgeMethod> moduleMethod = [[RCTModuleMethod alloc] initWithMethodSignature:entries[1] JSMethodName:entries[0] moduleClass:_moduleClass]; [moduleMethods addObject:moduleMethod]; } }
  35. OVERVIEW What is the bridge? What is native modules? Why

    the bridge is needed? advantages for users advantages for developers scripting native with React How does the bridge work? collecting native modules calling native functions calling JavaScript functions back
  36. PREFACE React Native apps have • some main threads •

    GCD queues for each modules setup by the bridge
  37. CALLING NATIVE FUNCTIONS import { NativeModules, } from 'react-native' const

    { CalendarManager } = NativeModules CalendarManager.addEvent( 'talk at React Native Meetup #4', '21cafe' )
  38. NativeModules? • Facade for native modules • Calling functions through

    NativeModules enqueue JSON data for the calling MessageQueue.enqueueNativeCall NativeModules[aModule][aMethod] https://github.com/facebook/react-native/blob/master/Libraries/BatchedBridge/NativeModules.js
  39. ENQUEUE, THEN? The bridge processes enqueued callings after the end

    of JS execution https://tadeuzagallo.com/blog/react-native-bridge/
  40. TYPE CONVERSION FOR ARGUMENTS The bridge converts queued NSArray data

    by use of • type information from native module definitions • RCTConvert + (NSArray<NSString *> *)__rct_export__142 { return @[@"", @"addEvent:(NSString *)name at:(NSString *)location"]; } - (void)addEvent:(NSString *)name at:(NSString *)location { RCTLogInfo(@"Create an event: %@ at %@", name, location); } type information https://github.com/facebook/react-native/blob/master/React/Base/RCTModuleMethod.m processed by the bridge
  41. RCTConvert? converter for JSON <-> native value • JSON support

    • NSString • NSNumber • BOOL • NSArray • NSDictionary and more And you can define converter for your type like Enumerations https://github.com/facebook/react-native/search?q=RCTConvert
  42. EXAMPLE @[ @[ @0 ], // module IDs @[ @1

    ], // method IDs @[ // arguments @[ @"talk at React Native Meetup", @"21cafe", ] ] ]; CalendarManager.addEvent('talk at React Native Meetup #4', '21cafe') NSArray<NSNumber *> *moduleIDs = [RCTConvert NSNumberArray:requestsArray[RCTBridgeFieldRequestModuleIDs]]; NSArray<NSNumber *> *methodIDs = [RCTConvert NSNumberArray:requestsArray[RCTBridgeFieldMethodIDs]]; NSArray<NSArray *> *paramsArrays = [RCTConvert NSArrayArray:requestsArray[RCTBridgeFieldParams]]; // snip [self callNativeModule:[moduleIDs[index] integerValue] method:[methodIDs[index] integerValue] params:paramsArrays[index]];
  43. DISPATCH FROM NATIVE

  44. OVERVIEW What is the bridge? What is native modules? Why

    the bridge is needed? advantages for users advantages for developers scripting native with React How does the bridge work? collecting native modules calling native functions calling JavaScript functions back
  45. CALLING JAVASCRIPT FUNCTIONS BACK • by callback • by Promise

    • by sending event 3 ways to call JavaScript functions back BatchedBridge enqueueCallback BatchedBridge enqueueJSCall https://github.com/facebook/react-native/blob/master/React/Base/RCTBatchedBridge.m arguments are converted to JSON
  46. BY CALLBACK RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback) { NSArray *events = @[@"talk in 21cafe",

    @"write slides"]; callback(@[[NSNull null], events]); } error information arguments
  47. BY PROMISE RCT_EXPORT_METHOD(findEvents:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { NSArray *events = @[@"talk in

    21cafe", @"write slides"]; if (events) { resolve(events); } else { NSError *error = nil; reject(@"no events", @"no events", error); } }
  48. BY SENDING EVENT #import "RCTEventEmitter.h" @interface CalendarManager : RCTEventEmitter<RCTBridgeModule> @end

    @implementation - (NSArray<NSString *> *)supportedEvents { return @[@"progress"]; } @end
  49. BY SENDING EVENT RCT_EXPORT_METHOD(heavy) { dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ int

    i = 0, j = 0; while (i < 10) { while (j < 10000) { ++j; } ++i; [self sendEventWithName:@"progress" body:[NSNumber numberWithInt:i]]; } } ); }
  50. SUMMARY • The approach “scripting native” is extremely powerful way

    for Web / native engineers. • “Learn once, write anywhere” for Web engineers • compile-less development for native engineers • Building native modules is simple enough. • Don’t be afraid, Let’s try.
  51. REFERENCES • https://code.facebook.com/posts/1014532261909640/react- native-bringing-modern-web-techniques-to-mobile/ • http://facebook.github.io/react-native/docs/native-modules- ios.html • https://tadeuzagallo.com/blog/react-native-bridge/ •

    http://qiita.com/janus_wel/items/93de843cc2243370fce4 • https://github.com/facebook/react-native
  52. THANK YOU !!