Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

FOR NASH PATIENT MADE WITH REACT NATIVE now, not available for the public

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

WHAT IS THE BRIDGE? connector between JavaScript and native worlds collecting native modules loading bundled JavaScript calling native functions and calling JavaScript functions back

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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!!

Slide 14

Slide 14 text

(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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

#import "RCTBridgeModule.h" @interface CalendarManager : NSObject @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

Slide 18

Slide 18 text

@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

Slide 19

Slide 19 text

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 + (NSString *)moduleName; @optional // snip @end A protocol to be implemented in native modules

Slide 20

Slide 20 text

@“”, @[]? http://clang.llvm.org/docs/ObjectiveCLiterals.html Literals • @“” for NSString • @[] for NSArray / NSDictionary

Slide 21

Slide 21 text

#import "RCTBridgeModule.h" @interface CalendarManager : NSObject @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

Slide 22

Slide 22 text

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); }

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

+? 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

Slide 25

Slide 25 text

@#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”

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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); }

Slide 29

Slide 29 text

INITIALIZING NATIVE MODULES NSMutableDictionary *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

Slide 30

Slide 30 text

#import "RCTBridgeModule.h" @interface CalendarManager : NSObject @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

Slide 31

Slide 31 text

RCT_EXPORT_METHOD + (NSArray *)__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

Slide 32

Slide 32 text

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 *)RCT_CONCAT(__rct_export__, \ RCT_CONCAT(js_name, RCT_CONCAT(__LINE__, __COUNTER__))) { \ return @[@#js_name, @#method]; \ }

Slide 33

Slide 33 text

-, __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

Slide 34

Slide 34 text

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 *entries = ((NSArray *(*)(id, SEL))imp)(_moduleClass, selector); id moduleMethod = [[RCTModuleMethod alloc] initWithMethodSignature:entries[1] JSMethodName:entries[0] moduleClass:_moduleClass]; [moduleMethods addObject:moduleMethod]; } }

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

PREFACE React Native apps have • some main threads • GCD queues for each modules setup by the bridge

Slide 37

Slide 37 text

CALLING NATIVE FUNCTIONS import { NativeModules, } from 'react-native' const { CalendarManager } = NativeModules CalendarManager.addEvent( 'talk at React Native Meetup #4', '21cafe' )

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

ENQUEUE, THEN? The bridge processes enqueued callings after the end of JS execution https://tadeuzagallo.com/blog/react-native-bridge/

Slide 40

Slide 40 text

TYPE CONVERSION FOR ARGUMENTS The bridge converts queued NSArray data by use of • type information from native module definitions • RCTConvert + (NSArray *)__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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

DISPATCH FROM NATIVE

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

BY CALLBACK RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback) { NSArray *events = @[@"talk in 21cafe", @"write slides"]; callback(@[[NSNull null], events]); } error information arguments

Slide 47

Slide 47 text

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); } }

Slide 48

Slide 48 text

BY SENDING EVENT #import "RCTEventEmitter.h" @interface CalendarManager : RCTEventEmitter @end @implementation - (NSArray *)supportedEvents { return @[@"progress"]; } @end

Slide 49

Slide 49 text

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]]; } } ); }

Slide 50

Slide 50 text

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.

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

THANK YOU !!