Slide 1

Slide 1 text

Cross-platform apps with Ionic React & Capacitor J S f o rW P C o n f 2 0 2 0

Slide 2

Slide 2 text

Native

Slide 3

Slide 3 text

Agenda • About Capacitor / Ionic • Why should we use Capacitor • How can we use Capacitor • Create a Native PWA using Capacitor and Ionic • Integrating Capacitor with a Jamstack application

Slide 4

Slide 4 text

Example: Ionic React with Capacitor https://github.com/getshifter/example-ionic- react-capacitor Example: Gatsby WordPress with Capacitor https://github.com/getshifter/example-gatsby- capacitor-wordpress

Slide 5

Slide 5 text

Hide (Hidetaka Okamoto) • Digitalcube Co. Ltd. • Nishinomiya / Kobe • WordPress Core contributor 4.7 / 5.0 / 5.3

Slide 6

Slide 6 text

h t t p s : / / w w w. g e t s h i f t e r. i o /

Slide 7

Slide 7 text

Agenda • About Capacitor / Ionic • Why should we use Capacitor • How can we use Capacitor • Create a Native PWA using Capacitor and Ionic • Integrating Capacitor with a Jamstack application

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Capacitor: Application runtime for Native PWA • Similar to Apache Cordova or Adobe PhoneGap • Easy to convert your application to Native App • iOS / Android / Electron / and PWA • Access the native API from JavaScript

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Ionic React: Mobile UI toolkit for React • Simply and framework friendly toolkit • Angular / React / Vue • Single codebase, running web and native application • Useful and various UI components

Slide 12

Slide 12 text

Ionic & Capacitor Great for cross-platform apps • Ionic provides cross-platform friendly UI components • Capacitor helps us to access the device native API. • Capacitor build application for the native and web. • Single codebase, running everywhere :)

Slide 13

Slide 13 text

Agenda • About Capacitor / Ionic • Why should we use Capacitor • How can we use Capacitor • Create a Native PWA using Capacitor and Ionic • Integrating Capacitor with a Jamstack application

Slide 14

Slide 14 text

Native W H AT ? !

Slide 15

Slide 15 text

PWA is good, but NOT a perfect solution • PWA provides an installable, app-like experience via the web • PWA can make a multi-device support web application • Many API can use from JavaScript (Web push / Camera / etc..) • But, still not able to access several native APIs. • If we want to access these APIs, we need to create a Native App

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

Declined Web Apis in Safari (June 28, 2020) • Web Bluetooth • Web MIDI API • Magnetometer API • Web NFC API - • Device Memory API • Network Information API - • Battery Status API • • Serial API • Web USB Geolocation Sensor (background geolocation) • User Idle Detection • Proximity Sensor Web Bluetooth Scanning • Ambient Light Sensor - • WebHID https://www.zdnet.com/article/apple-declined-to-implement-16-web-apis-in-safari-due-to-privacy-concerns/

Slide 18

Slide 18 text

PWA or Native Native PWA • Capacitor provides wrapper API for PWA and native • Single Capacitor API, calling optimized API • And Ionic provides a device optimized UI component • Build PWA and Native App by JavaScript

Slide 19

Slide 19 text

Capacitor has check Available method import { Capacitor } from '@capacitor/core'; if (Capacitor.isPluginAvailable('Camera')) { // make the call: Camera.getPhoto() } else { // Have the user upload a file instead }

Slide 20

Slide 20 text

Accessibility Background task Browser Clipboard Filesystem Toast Geolocation Network Push Notification Splash Screen Share Device etc… Core APIs of Capacitor

Slide 21

Slide 21 text

Capacitor has Plugins

Slide 22

Slide 22 text

Examples of Community Plugins

Slide 23

Slide 23 text

Access Phone Contacts npm install -S @capacitor-community/contacts

Slide 24

Slide 24 text

Authenticate Face ID & Touch ID npm install -S \ capacitor-biometric-auth

Slide 25

Slide 25 text

NativeAudio.preloadSimple({ assetPath: "audio/chime.mp3", assetId: "chime_audio", }); NativeAudio.preloadComplex({ assetPath: "audio/inception.mp3", assetId: "inception_audio", volume: 1.0, audioChannelNum: 1, }); NativeAudio.play({ assetId: "chime_audio", }); Audio Control npm install -S
 @capacitor-community/native- audio

Slide 26

Slide 26 text

And more… Single Sign On (FB / Apple etc…) Google Admob Apple Game Center Google play QRCode scanner Text to Speech Camera preview etc…

Slide 27

Slide 27 text

Explore your favorite plugins ! • Plugin registry: Search by "@capacitor-community" https://www.npmjs.com/search?q=%40capacitor-community • Common plugin: Search by ”capacitor plugin” https://www.npmjs.com/search?q=capacitor%20plugin

Slide 28

Slide 28 text

Agenda • About Capacitor / Ionic • Why should we use Capacitor • How can we use Capacitor • Create a Native PWA using Capacitor and Ionic • Integrating Capacitor with a Jamstack application

Slide 29

Slide 29 text

$ cd /PATH/TO/YOUR/APP # Install packages $ npm i -S @capacitor/core # Initialize $ npx cap init - ? App name YOUR_APPLICATION_NAME - ? App Package ID com.example.app - ? Which npm client would you like to use? › npm yarn Your Capacitor project is ready to go! Simplest setup for existing app

Slide 30

Slide 30 text

{ "appId": "com.example.app", "appName": "YOUR_APPLICATION_NAME", "bundledWebRuntime": false, "npmClient": "npm", "webDir": "www", "cordova": {} } ↓ Should replace webDir to your app build destination. { "appId": "com.example.app", "appName": "YOUR_APPLICATION_NAME", "bundledWebRuntime": false, "npmClient": "npm", "webDir": "public", "cordova": {} } Update capacitor.config.json

Slide 31

Slide 31 text

% npx cap add ios ℹ Installing iOS dependencies – Skipping: already installed ✔ Adding native xcode project in: /Users/development/ionic-react/ios in 17.76ms ✔ add in 19.83ms ✔ Copying web assets from public to ios/App/public in 4.21ms ✔ Copying native bridge in 2.36ms ✔ Copying capacitor.config.json in 1.12ms ✔ copy in 23.29ms ✔ Updating iOS plugins in 4.67ms Found 0 Capacitor plugins for ios: ✔ Updating iOS native dependencies with "pod install" (may take several minutes) in 7.46s ✔ update ios in 7.48s Now you can run npx cap open ios to launch Xcode % npx cap add android % npx cap add electron % npx cap add web Add Supported Platform

Slide 32

Slide 32 text

$ npx cap open electron

Slide 33

Slide 33 text

$ npx cap open ios

Slide 34

Slide 34 text

# Build your SPA $ npm run build # Sync Capacitor % npx cap sync ✔ Copying web assets from public to android/app/src/main/assets/public in 9.31ms ✔ Copying native bridge in 1.01ms ✔ Copying capacitor.config.json in 766.90μp ✔ copy in 23.18ms ✔ Updating Android plugins in 3.58ms Found 0 Capacitor plugins for android: ✔ update android in 19.35ms ✔ Copying web assets from public to ios/App/public in 4.78ms ✔ Copying native bridge in 912.98μp ✔ Copying capacitor.config.json in 671.34μp ✔ copy in 14.16ms ✔ Updating iOS plugins in 2.32ms Found 0 Capacitor plugins for ios: ✔ Updating iOS native dependencies with "pod install" (may take several minutes) in 6.33s ✔ update ios in 6.34s ✔ copy in 237.76μp ✔ update web in 5.83μp Sync finished in 6.415s Update & Sync

Slide 35

Slide 35 text

S i m p l e s t u s a g e = J u s t c o m p i l e • Capacitor converts your SPA to a Native App • We can publish Native apps just using build artifacts by Capacitor • iOS app: /ios • Android app: /android • Electron app: /electron

Slide 36

Slide 36 text

Agenda • About Capacitor / Ionic • Why should we use Capacitor • How can we use Capacitor • Create a Native PWA using Capacitor and Ionic • Integrating Capacitor with a Jamstack application

Slide 37

Slide 37 text

Capacitor provides a Native API wrapper

Slide 38

Slide 38 text

import { Plugins } from ‘@capacitor/core'; const shareTweet = async () => { const { Share } = Plugins; const share = await Share.share({ title: 'Share Capacitor', text: 'I deployed a native mobile app', url: 'https://capacitorjs.com', }); } Social Share Example

Slide 39

Slide 39 text

Show Native share UI

Slide 40

Slide 40 text

import { Plugins, NetworkStatus } from '@capacitor/core'; import { useState, useEffect, useCallback } from ‘react'; const { Network, Toast } = Plugins; export const useNetworkHook = () => { const [status, updateStatus] = useState() const checkNetworkStatus = () => { Network.getStatus() .then(data => updateStatus(data)) } useEffect(() => { checkNetworkStatus() const handler = Network.addListener('networkStatusChange', (status) => { checkNetworkStatus() }); return () => handler.remove(); }, []) const connected = status ? status.connected: undefined useEffect(() => { if (connected === undefined || connected) return Toast.show({ text: "Network is offline" }) }, [connected]) return status } Example of Network status Check hook

Slide 41

Slide 41 text

import { Plugins, NetworkStatus } from '@capacitor/core'; import { useState, useEffect, useCallback } from ‘react'; const { Network, Toast } = Plugins; export const useNetworkHook = () => { const [status, updateStatus] = useState() const checkNetworkStatus = () => { Network.getStatus() .then(data => updateStatus(data)) } useEffect(() => { checkNetworkStatus() const handler = Network.addListener('networkStatusChange', (status) => { checkNetworkStatus() }); return () => handler.remove(); }, []) const connected = status ? status.connected: undefined useEffect(() => { if (connected === undefined || connected) return Toast.show({ text: "Network is offline" }) }, [connected]) return status } Access Native network API from Capacitor

Slide 42

Slide 42 text

import { Plugins, NetworkStatus } from '@capacitor/core'; import { useState, useEffect, useCallback } from ‘react'; const { Network, Toast } = Plugins; export const useNetworkHook = () => { const [status, updateStatus] = useState() const checkNetworkStatus = () => { Network.getStatus() .then(data => updateStatus(data)) } useEffect(() => { checkNetworkStatus() const handler = Network.addListener('networkStatusChange', (status) => { checkNetworkStatus() }); return () => handler.remove(); }, []) const connected = status ? status.connected: undefined useEffect(() => { if (connected === undefined || connected) return Toast.show({ text: "Network is offline" }) }, [connected]) return status } Add event listener to check network status

Slide 43

Slide 43 text

import { Plugins, NetworkStatus } from '@capacitor/core'; import { useState, useEffect, useCallback } from ‘react'; const { Network, Toast } = Plugins; export const useNetworkHook = () => { const [status, updateStatus] = useState() const checkNetworkStatus = () => { Network.getStatus() .then(data => updateStatus(data)) } useEffect(() => { checkNetworkStatus() const handler = Network.addListener('networkStatusChange', (status) => { checkNetworkStatus() }); return () => handler.remove(); }, []) const connected = status ? status.connected: undefined useEffect(() => { if (connected === undefined || connected) return Toast.show({ text: "Network is offline" }) }, [connected]) return status } Toast network offline messages

Slide 44

Slide 44 text

Show offline notifications

Slide 45

Slide 45 text

For PWA

Slide 46

Slide 46 text

Several APIs can’t use from web

Slide 47

Slide 47 text

$ npm install -S
 pwa-elements

Slide 48

Slide 48 text

import React from 'react'; import ReactDOM from 'react-dom'; import { defineCustomElements } from '@ionic/pwa-elements/loader'; import App from './App'; ReactDOM.render(, document.getElementById('root')); // Call the element loader after the app has been rendered the first time defineCustomElements(window); Add element loader after render function

Slide 49

Slide 49 text

Show Camera element in browser

Slide 50

Slide 50 text

Capacitor Access Native API pwa-element Use it from PWA Native (and) PWA

Slide 51

Slide 51 text

Use Ionic React with Capacitor

Slide 52

Slide 52 text

Capacitor DOES NOT provide UI components

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

Mutli-framework support import React from 'react'; import { IonButton, IonContent } from '@ionic/react'; export const ButtonExample: React.FC = () => ( Default )

Slide 55

Slide 55 text

Platform styled component

Slide 56

Slide 56 text

Support Darkmode

Slide 57

Slide 57 text

Ionic can create a new app with Capacitor % npx ionic start NativePWAApp my-first-app --type=react --capacitor % tree NativePWAApp -I node_modules -L 2 ᵓ── android ᵓ── capacitor.config.json ᵓ── ionic.config.json ᵓ── ios ᵓ── package.json ᵓ── public │ ᵓ── index.html │ └── manifest.json ᵓ── src │ ᵓ── App.test.tsx │ ᵓ── App.tsx │ ᵓ── index.tsx

Slide 58

Slide 58 text

Blogged how to use Ionic & Capacitor with WP https://bit.ly/323kv6y

Slide 59

Slide 59 text

Use case: Japanese web magazine Areaia https://air.areaia.jp/

Slide 60

Slide 60 text

Ionic (Angular) + Capacitor + WordPress.com • UI: Ionic • Content (API): WordPress • Native / PWA: Capacitor

Slide 61

Slide 61 text

Headless WordPress Hosting • Use WordPress as a Content manager & API • Call from JS SPA to WP API • Fully managed WordPress

Slide 62

Slide 62 text

Agenda • About Capacitor / Ionic • Why should we use Capacitor • How can we use Capacitor • Create a Native PWA using Capacitor and Ionic • Integrating Capacitor with a Jamstack application

Slide 63

Slide 63 text

Error: ReferenceError: window is not defined.

Slide 64

Slide 64 text

Some tool need to use the Window property import React from 'react'; import ReactDOM from 'react-dom'; import { defineCustomElements } from '@ionic/pwa-elements/loader'; import App from './App'; ReactDOM.render(, document.getElementById('root')); // Call the element loader after the app has been rendered the first time defineCustomElements(window);

Slide 65

Slide 65 text

But, Node has no Window obj

Slide 66

Slide 66 text

Solution: use lazy load to ignore SSR const useLoadPWAElements = () => { const windowGlobal = typeof window !== 'undefined' && window useEffect(() => { if (typeof window !== 'undefined') { import('@ionic/pwa-elements/loader') .then((module) => { module.defineCustomElements(windowGlobal) }) } }, [windowGlobal]) }

Slide 67

Slide 67 text

Lazy load Capacitor component const loadCapComponents = async () => { // Load Component async const { DemoClipboard } = await import('./Clipboard') return } const Component: FC = (props) => { // state for handle pwa-elements components const [elem, setSelem] = useState(

Loading….

) // Load pwa-elements components useEffect(() => { if (typeof window !== 'undefined') { loadCapComponents().then(elements => { setSelem(elements) }) } }, []) return ( {elem} ) }

Slide 68

Slide 68 text

$ gatsby build $ npx cap sync $ npx cap open ios

Slide 69

Slide 69 text

Create Native PWA with JavaScript • Capacitor unifies the API calling between Native and web • Ionic unifies the UI style too • Single codebase (Use JavaScript / TypeScript) multi build • We can provide our own content from many approaches

Slide 70

Slide 70 text

Thanks you! • Capacitor unifies the API calling between Native and web • Ionic unifies the UI style too • Single codebase (Use JavaScript / TypeScript) multi build • We can provide our own content from many approaches Blog post about Ionic & Capacitor https://bit.ly/323kv6y Shifter https://getshifter.io Twitter @motchi0214 GitHub https://github.com/hideokamoto/