54
Front End Workshops React Native Part III: The Native Side Alberto Irurueta Enrique Oriol [email protected] [email protected]

Workshop 26: React Native - The Native Side

Embed Size (px)

Citation preview

Page 1: Workshop 26: React Native - The Native Side

Front End WorkshopsReact Native Part III:

The Native Side

Alberto IruruetaEnrique Oriol

[email protected]@naradarobotics.com

Page 2: Workshop 26: React Native - The Native Side

React Native short Recap

Page 3: Workshop 26: React Native - The Native Side

React Native: is a library to generate native apps for iOS and Android mobile devices capable, programmed in javascript.

React Native uses the actual native components of each platform (currently iOS and Android).

Useful Native APIs

React Native Part I

Page 4: Workshop 26: React Native - The Native Side

The Native Side

Page 5: Workshop 26: React Native - The Native Side

NAVIGATIONNavigator component handles the transition between different scenes in your app, available on both iOS and Android.

React Native Part II

TABSSeveral options to provide tabbed navigation. Scrollable-Tab-View is a nice iOS/Android component that lets you swipe between tabs.

LISTSUse ListView to render a list of components.

DRAWERBUTTONVIDEOCAMERA

SWIPER

Page 6: Workshop 26: React Native - The Native Side

NAVIGATIONNavigator component handles the transition between different scenes in your app, available on both iOS and Android.

React Native Part II

TABSSeveral options to provide tabbed navigation. Scrollable-Tab-View is a nice iOS/Android component that lets you swipe between tabs.

LISTSUse ListView to render a list of components.

DRAWERBUTTONVIDEOCAMERA

SWIPER

Page 7: Workshop 26: React Native - The Native Side

The Native Side

We have been playing on the JS side, but how does it work on the native side?

Page 8: Workshop 26: React Native - The Native Side

The Native Side

Not really magic, it’s more like this...

Native UI View Native logic

Native Bridge

Javascript Bridge

Javascript

JS Method

Invoke Native Method

Queue callback

Native callback

Invoke callback

Execute callback

Page 9: Workshop 26: React Native - The Native Side

Building Native Modules

Page 10: Workshop 26: React Native - The Native Side

What is this?

A native module

can be thought as a

library

running in the

native side

Native Modules

Page 11: Workshop 26: React Native - The Native Side

iOS

Page 12: Workshop 26: React Native - The Native Side

Basics

Native Modules on iOS

// CalendarManager.m@implementation CalendarManager

RCT_EXPORT_MODULE();@end

2 . Include RCT_MODULE_EXPORT() macro (on .m file)

// CalendarManager.h#import "RCTBridgeModule.h"

@interface CalendarManager : NSObject <RCTBridgeModule>@end

1. Implement RCTBridgeModule protocol (on .h file)

Page 13: Workshop 26: React Native - The Native Side

Native Modules on iOS

Expose method

// CalendarManager.mRCT_EXPORT_METHOD( addEvent:(NSString *)name location:(NSString *)location ){ RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);}

Implement RCT_EXPORT_METHOD() macro (on .m file)

import { NativeModules } from 'react-native';var CalendarManager = NativeModules.CalendarManager;

CalendarManager.addEvent('Hackathon Party', 'Pier 01, BCN');

Use it on Javascript, same name up to first colon

Page 14: Workshop 26: React Native - The Native Side

Native Modules on iOS

Expose method with custom name

Several methods starting with same name?

RCT_REMAP_METHOD( methodName, prop1:(float)prop1 prop2:(NSString *)prop2 ...){ // Do some stuff}

Use RCT_REMAP_METHOD() macro (on .m file)

Only 2 arguments!!!

Page 15: Workshop 26: React Native - The Native Side

Native Modules on iOS

Supported arguments

string (NSString)

number (NSInteger, float, double, CGFloat, NSNumber)

boolean (BOOL, NSNumber)

array (NSArray) of any types from this list

object (NSDictionary) with string keys and values of any type from this list

function (RCTResponseSenderBlock)

¿More formats? - Check RCTConvert

Page 16: Workshop 26: React Native - The Native Side

Native Modules on iOS

Example with NSDictionary

// CalendarManager.mRCT_EXPORT_METHOD( addEvent:(NSString *)name details:(NSDictionary *)details ){ NSString *location = [RCTConvert NSString:details[@"location"]]; NSDate *time = [RCTConvert NSDate:details[@"time"]];

...}

//JSCalendarManager.addEvent('Birthday Party',

{ location: '4 Privet Drive, Surrey', time: date.getTime(), description: '...'

})

Getting data on iOS

Sending data from JS

Page 17: Workshop 26: React Native - The Native Side

Native Modules on iOS

Promises

// CalendarManager.mRCT_REMAP_METHOD( asyncMethod, resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject ){ //...some stuff…

resolve(@"some data");//reject(@"no_events", @"There were no events", error);

}

Use RCT_REMAP_METHOD() macro (on .m file)

CalendarManager.asyncMethod().then( ()=>{console.log(‘success’)}, ... )

It simply returns a promise, use it as usual

Page 18: Workshop 26: React Native - The Native Side

Native Modules on iOS

Other stuff

Exporting constants

Callbacks

Threading

Sending events to JS

Swift

You’ll find more info at: https://facebook.github.io/react-native/docs/native-modules-ios.html

Page 19: Workshop 26: React Native - The Native Side

iOS1. Implement RCTBridgeModule2. Use RCT_MODULE_EXPORT()

3. Method: Use RCT_EXPORT_METHOD()4. Promises: Use RCT_REMAP_METHOD() with

RCTPromiseResolveBlock

Native Modules on iOS

CheatSeet

You’ll find more info at: https://facebook.github.io/react-native/docs/native-modules-ios.html

JS1. Import NativeModules from ‘react-native’

2. Use NativeModules.YourModule

Page 20: Workshop 26: React Native - The Native Side

Android

Page 21: Workshop 26: React Native - The Native Side

Native Modules on Android

Create module1. Extend ReactContextBaseJavaModule on native Java file2. Implement String getName() method with module name

import com.facebook.react.bridge.NativeModule;import com.facebook.react.bridge.ReactApplicationContext;import com.facebook.react.bridge.ReactContext;import com.facebook.react.bridge.ReactContextBaseJavaModule;import com.facebook.react.bridge.ReactMethod;

import java.util.Map;

public class ToastModule extends ReactContextBaseJavaModule {

private static final String DURATION_SHORT_KEY = "SHORT"; private static final String DURATION_LONG_KEY = "LONG";

public ToastModule(ReactApplicationContext reactContext) { super(reactContext); }

@Override public String getName() { return "ToastAndroid"; }}

Page 22: Workshop 26: React Native - The Native Side

Native Modules on Android

Expose constants and methods1. Override Map<Strings, Object> getConstants() with defined constants

(optional)2. Annotate available methods with @ReactMethod

...

public class ToastModule extends ReactContextBaseJavaModule {

...

@Override public Map<String, Object> getConstants() { final Map<String, Object> constants = new HashMap<>(); constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT); constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG); return constants; }

@ReactMethod public void show(String message, int duration) { Toast.makeText(getReactApplicationContext(), message, duration).show(); } }

Page 23: Workshop 26: React Native - The Native Side

Native Modules on Android

Supported arguments

string (String)

number (Integer, Float, Double)

boolean (Boolean)

array (ReadableArray) of any types from this list

object (ReadableMap) with string keys and values of any type from this list

function (Callback)

Page 24: Workshop 26: React Native - The Native Side

Native Modules on Android

Register the Module

1. Add module to createNativeModules in app package

class AnExampleReactPackage implements ReactPackage {

@Override public List<Class<? extends JavaScriptModule>> createJSModules() { return Collections.emptyList(); }

@Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); }

@Override public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>();

modules.add(new ToastModule(reactContext));

return modules; }

Page 25: Workshop 26: React Native - The Native Side

Native Modules on Android

Register the Module

2. Add application package to MainActivity.java

Calling it from JS:

protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new AnExampleReactPackage()); // <-- Add this line with your package name.}

import ToastAndroid from './ToastAndroid';

ToastAndroid.show('Awesome', ToastAndroid.SHORT);

Page 26: Workshop 26: React Native - The Native Side

Native Modules on Android

Callbacks

Java public class UIManagerModule extends ReactContextBaseJavaModule {...

@ReactMethod public void measureLayout( int tag, int ancestorTag, Callback errorCallback, Callback successCallback) { try { measureLayout(tag, ancestorTag, mMeasureBuffer); float relativeX = PixelUtil.toDIPFromPixel(mMeasureBuffer[0]); float relativeY = PixelUtil.toDIPFromPixel(mMeasureBuffer[1]); float width = PixelUtil.toDIPFromPixel(mMeasureBuffer[2]); float height = PixelUtil.toDIPFromPixel(mMeasureBuffer[3]); successCallback.invoke(relativeX, relativeY, width, height); } catch (IllegalViewOperationException e) { errorCallback.invoke(e.getMessage()); } }

...

Page 27: Workshop 26: React Native - The Native Side

Native Modules on Android

Callbacks

JavaScript

UIManager.measureLayout( 100, 100, (msg) => { console.log(msg); }, (x, y, width, height) => { console.log(x + ':' + y + ':' + width + ':' + height); });

Page 28: Workshop 26: React Native - The Native Side

Native Modules on Android

Promises

Java public class UIManagerModule extends ReactContextBaseJavaModule {...

@ReactMethod public void measureLayout( int tag, int ancestorTag, Promise promise) { try { measureLayout(tag, ancestorTag, mMeasureBuffer);

WritableMap map = Arguments.createMap();

map.putDouble("relativeX", PixelUtil.toDIPFromPixel(mMeasureBuffer[0])); map.putDouble("relativeY", PixelUtil.toDIPFromPixel(mMeasureBuffer[1])); map.putDouble("width", PixelUtil.toDIPFromPixel(mMeasureBuffer[2])); map.putDouble("height", PixelUtil.toDIPFromPixel(mMeasureBuffer[3]));

promise.resolve(map); } catch (IllegalViewOperationException e) { promise.reject(e); } }

...

Page 29: Workshop 26: React Native - The Native Side

Native Modules on Android

Promises

JavaScript

async function measureLayout() { try { var { relativeX, relativeY, width, height, } = await UIManager.measureLayout(100, 100);

console.log(relativeX + ':' + relativeY + ':' + width + ':' + height); } catch (e) { console.error(e); }}

measureLayout();

Page 30: Workshop 26: React Native - The Native Side

Native Modules on Android

Other stuff

Threading

Sending events to JS

Getting activity results

Listening to LifeCycle events

You’ll find more info at: https://facebook.github.io/react-native/docs/native-modules-android.html

Page 31: Workshop 26: React Native - The Native Side

Building Native Components

Page 32: Workshop 26: React Native - The Native Side

iOS

Page 33: Workshop 26: React Native - The Native Side

// MyViewManager.h#import "MyView.h"#import "RCTViewManager.h"

@interface RCTMapManager : RCTViewManager@end

Basics

Native Components on iOS

- (UIView *)view{ return [[MyView alloc] init];}

2 . Implement method - (UIView *) view in MyViewManager.m

1. Subclass RCTViewManager and export it with RCT_EXPORT_METHOD()

Consider we already have an iOS custom view, for example, MyView

// MyViewManager.m#import "MyViewManager.h"

@implementation RCTMapManager RCT_EXPORT_MODULE()@end

Page 34: Workshop 26: React Native - The Native Side

Native Components on iOS

Use view from JS

// components/myView.jsimport { requireNativeComponent } from 'react-native';

// requireNativeComponent automatically resolves this to "RCTMapManager"export default requireNativeComponent('MyView', null);

Export component in JS file

// app.jsimport MyView from './components/myView';

// ...some stuff…render(){return (

<MyView />);}

Use it as any other component

Page 35: Workshop 26: React Native - The Native Side

// MyViewManager.m

@implementation MyViewManager //...some stuff… RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL)@end

Component Input properties

Native Components on iOS

1. Export property with RCT_EXPORT_VIEW_PROPERTY

// app.js<MyView enabled={false} />

// MyView.h@interface MyView : UIView @property (nonatomic) BOOL enabled;@end

Page 36: Workshop 26: React Native - The Native Side

Component Input properties

Native Components on iOS

// components/myView.jsimport React, { Component, PropTypes } from 'react';import { requireNativeComponent } from 'react-native';

class MyWrapperView extends Component { render() { return <MyView {...this.props} />; }}

MyWrapperView.propTypes = { enabled: PropTypes.bool,};

var MyView = requireNativeComponent('MyView', MyWrapperView);export default MyWrapperView;

2 . Document the interface in JS with React PropTypes

Page 37: Workshop 26: React Native - The Native Side

Component Input properties

Native Components on iOS

Want to do something complex (call method on prop update, …)? - Use RCT_CUSTOM_VIEW_PROPERTY

Want to relate with IBOutlet element (UILabel.text)?- Use custom setter on Obj-C View

Types allowed?- string, number, boolean, object, array of any of those

More info at https://facebook.github.io/react-native/docs/native-components-ios.html

Page 38: Workshop 26: React Native - The Native Side

Component Events

// MyView.h@class MyView

@protocol MyViewDelegate <NSObject>- (void) someMethod:(MyView*)view;@end

@interface MyView : UIView @property (weak, nonatomic)id <UserInputDelegate> delegate;@end

Native Components on iOS

1. Create a delegate of MyView, with the method that launch the event

Page 39: Workshop 26: React Native - The Native Side

Component Events

Native Components on iOS

// MyViewManager@interface MyViewManager : UIView<MyViewDelegate>//...stuff

@implementation MyViewManager- (void) someMethod:(MyView*)view{ /*...some stuff…*/

//send event to componentview.onSomething( @{ @"someData":@"someValue" } );

}@end

2. Update ViewManager to implement the view delegate, and return a dictionary in the event callback

Page 40: Workshop 26: React Native - The Native Side

Component Events

// MyViewManager- (UIView *)view{ MyView* view = [[MyView alloc] init]; view.delegate = self; Return view;}

Native Components on iOS

3. Update ViewManager (UIView*) view method to assign delegate

4. Export RCTBubblingEventBlock property on ViewManager

// MyViewManager@implementation MyViewManager //...stuff... RCT_EXPORT_VIEW_PROPERTY(onSomething, RCTBubblingEventBlock)@end

Page 41: Workshop 26: React Native - The Native Side

// MyView.h@property(nonatomic, copy) RCTBubblingEventBlock onSomething;

Component Events

Native Components on iOS

5. Declare same RTCBubblingEventBlock property on view

6. Set event function in component wrapper

// components/myView.jsclass MyWrapperView extends Component {

constructor(props) { super(props); this._onSomething = this._onSomething.bind(this); }

_onSomething(event){ if (!this.props.onSomething) {return;} this.props.onSomething(event.nativeEvent.someData); }

Page 42: Workshop 26: React Native - The Native Side

Component Events

Native Components on iOS

7. Use event function in component wrapper// components/myView.jsclass MyWrapperView extends Component { // ...stuff… _onSomething(event){/* ...stuff…*/}

render() { return <MyView {...this.props} onSomething={this._onSomething} />; }}

8. Add event callback property to PropTypes

// components/myView.jsMyWrapperView.propTypes = { enabled: PropTypes.bool, onSomething: PropTypes.func}

Page 43: Workshop 26: React Native - The Native Side

Component Events

Native Components on iOS

9. Finally, use the component event as usual

// app.jsimport React, { Component } from "react";

import MyView from "./components/myView.js";

class App extends Component { // ...stuff… doSomething(data){

console.log(data) }

render() { return <MyView enabled=true onSomething={this.doSomething} />; }}

Page 44: Workshop 26: React Native - The Native Side

iOS1. Subclass RCTViewManager2. Use RCT_MODULE_EXPORT()3. Implement -(UIView*) view method

4. Property: Use RCT_EXPORT_VIEW_PROPERTY()5. Events:

a. Set ViewManager as View delegateb. Export callback as RCTBubblingEventBlock

Native Components on iOS

CheatSeet

You’ll find more info at: https://facebook.github.io/react-native/docs/native-modules-ios.html

Page 45: Workshop 26: React Native - The Native Side

Native Components on iOS

CheatSeet

JS

1. Import requireNativeComponent from ‘react-native’

2. Create ViewWrapper with propTypes

3. Implement event callback

4. Get NativeView with requireNativeComponent('MyView',

ViewWrapper);

5. Export ViewWrapper

You’ll find more info at: https://facebook.github.io/react-native/docs/native-modules-ios.html

Page 46: Workshop 26: React Native - The Native Side

Android

Page 47: Workshop 26: React Native - The Native Side

Native Components on Android

1. Implement ViewManager subclass (singleton instance in charge of instantiating native views)

2. Implement createViewInstance method

...

public class ReactImageManager extends SimpleViewManager<ReactImageView> {

public static final String REACT_CLASS = "RCTImageView";

@Override public String getName() { return REACT_CLASS; }

@Override public ReactImageView createViewInstance(ThemedReactContext context) { return new ReactImageView(context, Fresco.newDraweeControllerBuilder(), mCallerContext); }

Page 48: Workshop 26: React Native - The Native Side

Native Components on AndroidProperties

- Annotate setters using @ReactProp or @ReactPropGroup

- Supported types: boolean, int, float, double, String, Boolean, Integer, ReadableArray or ReadableMap.

- Default values can be provided (defaultFloat, defaultBoolean, etc)

@ReactProp(name = "src") public void setSrc(ReactImageView view, @Nullable String src) { view.setSource(src); }

@ReactProp(name = "borderRadius", defaultFloat = 0f) public void setBorderRadius(ReactImageView view, float borderRadius) { view.setBorderRadius(borderRadius); }

@ReactProp(name = ViewProps.RESIZE_MODE) public void setResizeMode(ReactImageView view, @Nullable String resizeMode) { view.setScaleType(ImageResizeMode.toScaleType(resizeMode)); }

Page 49: Workshop 26: React Native - The Native Side

Native Components on Android

3. Register the ViewManager

Calling it from JS

@Override public List<ViewManager> createViewManagers( ReactApplicationContext reactContext) { return Arrays.<ViewManager>asList( new ReactImageManager() ); }

// ImageView.js

import { PropTypes } from 'react';import { requireNativeComponent, View } from 'react-native';

var iface = { name: 'ImageView', propTypes: { src: PropTypes.string, borderRadius: PropTypes.number, resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch']), ...View.propTypes // include the default view properties },};

module.exports = requireNativeComponent('RCTImageView', iface);

Page 50: Workshop 26: React Native - The Native Side

Native Components on Android

Events

class MyCustomView extends View { ... public void onReceiveNativeEvent() { WritableMap event = Arguments.createMap(); event.putString( "message", "MyMessage"); ReactContext reactContext = (ReactContext)getContext(); reactContext.getJSModule(RCTEventEmitter.class).receiveEvent( getId(), "topChange", event); }}

Page 51: Workshop 26: React Native - The Native Side

Native Components on Android

Events from JS// MyCustomView.js

class MyCustomView extends React.Component { constructor(props) { super(props); this._onChange = this._onChange.bind(this); } _onChange(event: Event) { if (!this.props.onChangeMessage) { return; } this.props.onChangeMessage(event.nativeEvent.message); } render() { return <RCTMyCustomView {...this.props} onChange={this._onChange} />; }}MyCustomView.propTypes = { /** * Callback that is called continuously when the user is dragging the map. */ onChangeMessage: React.PropTypes.func, ...};

var RCTMyCustomView = requireNativeComponent(`RCTMyCustomView`, MyCustomView, { nativeOnly: {onChange: true}});

Page 52: Workshop 26: React Native - The Native Side

Hands on mode

Page 53: Workshop 26: React Native - The Native Side

Hands on mode

Idea

Greeting message

First name

Last name

Submit

React Component with user field- Display a greeting message- Uses native module to

buildrandom greeting messages

Native Component- Get firstName / lastName props- Send event with updated fields

Page 54: Workshop 26: React Native - The Native Side