workshop 26: react native - the native side

Post on 14-Jan-2017

943 Views

Category:

Software

6 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Front End WorkshopsReact Native Part III:

The Native Side

Alberto IruruetaEnrique Oriol

airurueta@visual-engin.comeoriol@naradarobotics.com

React Native short Recap

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

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

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

The Native Side

We have been playing on the JS side, but how does it work on 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

Building Native Modules

What is this?

A native module

can be thought as a

library

running in the

native side

Native Modules

iOS

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)

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

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

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

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

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

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

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

Android

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

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

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)

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

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

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

...

Native Modules on Android

Callbacks

JavaScript

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

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

...

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

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

Building Native Components

iOS

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

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

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

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

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

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

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

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

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

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}

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

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

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

Android

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

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

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

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

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

Hands on mode

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

top related