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

discussion about the bridge

januswel
January 26, 2017

discussion about the bridge

discussion about the bridge of React Native

januswel

January 26, 2017
Tweet

More Decks by januswel

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. WHAT IS THE BRIDGE? connector between JavaScript and native worlds

    collecting native modules loading bundled JavaScript calling native functions and calling JavaScript functions back
  4. 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
  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. 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
  7. 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
  8. 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
  9. 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!!
  10. (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
  11. 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
  12. 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
  13. #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
  14. @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>
  15. 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
  16. #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
  17. 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); }
  18. 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
  19. +? 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
  20. @#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”
  21. 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
  22. 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
  23. 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); }
  24. 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
  25. #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
  26. 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
  27. 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]; \ }
  28. -, __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
  29. 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]; } }
  30. 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
  31. PREFACE React Native apps have • some main threads •

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

    { CalendarManager } = NativeModules CalendarManager.addEvent( 'talk at React Native Meetup #4', '21cafe' )
  33. 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
  34. ENQUEUE, THEN? The bridge processes enqueued callings after the end

    of JS execution https://tadeuzagallo.com/blog/react-native-bridge/
  35. 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
  36. 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
  37. 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]];
  38. 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
  39. 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
  40. BY CALLBACK RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback) { NSArray *events = @[@"talk in 21cafe",

    @"write slides"]; callback(@[[NSNull null], events]); } error information arguments
  41. 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); } }
  42. BY SENDING EVENT #import "RCTEventEmitter.h" @interface CalendarManager : RCTEventEmitter<RCTBridgeModule> @end

    @implementation - (NSArray<NSString *> *)supportedEvents { return @[@"progress"]; } @end
  43. 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]]; } } ); }
  44. 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.