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

Cross-platform apps with Ionic React & Capacitor

Cross-platform apps with Ionic React & Capacitor

JSforWP Conf 2020

Hidetaka Okamoto

July 11, 2020
Tweet

More Decks by Hidetaka Okamoto

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. Hide (Hidetaka Okamoto) • Digitalcube Co. Ltd. • Nishinomiya /

    Kobe • WordPress Core contributor 4.7 / 5.0 / 5.3
  4. h t t p s : / / w w

    w. g e t s h i f t e r. i o /
  5. 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
  6. 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
  7. 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
  8. 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 :)
  9. 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
  10. 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
  11. 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/
  12. 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
  13. 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 }
  14. Accessibility Background task Browser Clipboard Filesystem Toast Geolocation Network Push

    Notification Splash Screen Share Device etc… Core APIs of Capacitor
  15. 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
  16. And more… Single Sign On (FB / Apple etc…) Google

    Admob Apple Game Center Google play QRCode scanner Text to Speech Camera preview etc…
  17. 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
  18. 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
  19. $ 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
  20. { "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
  21. % 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
  22. # 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
  23. 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
  24. 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
  25. 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
  26. import { Plugins, NetworkStatus } from '@capacitor/core'; import { useState,

    useEffect, useCallback } from ‘react'; const { Network, Toast } = Plugins; export const useNetworkHook = () => { const [status, updateStatus] = useState<NetworkStatus>() 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
  27. import { Plugins, NetworkStatus } from '@capacitor/core'; import { useState,

    useEffect, useCallback } from ‘react'; const { Network, Toast } = Plugins; export const useNetworkHook = () => { const [status, updateStatus] = useState<NetworkStatus>() 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
  28. import { Plugins, NetworkStatus } from '@capacitor/core'; import { useState,

    useEffect, useCallback } from ‘react'; const { Network, Toast } = Plugins; export const useNetworkHook = () => { const [status, updateStatus] = useState<NetworkStatus>() 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
  29. import { Plugins, NetworkStatus } from '@capacitor/core'; import { useState,

    useEffect, useCallback } from ‘react'; const { Network, Toast } = Plugins; export const useNetworkHook = () => { const [status, updateStatus] = useState<NetworkStatus>() 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
  30. import React from 'react'; import ReactDOM from 'react-dom'; import {

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

    } from '@ionic/react'; export const ButtonExample: React.FC = () => ( <IonContent> <IonButton>Default</IonButton> </IonContent> ) <template> <!-- Default —> <ion-content> <ion-button>Default</ion-button> </ion-content> </template> <ion-content> <ion-button>Default</ion-button> </ion-content> React Vue Angular
  32. 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
  33. Ionic (Angular) + Capacitor + WordPress.com • UI: Ionic •

    Content (API): WordPress • Native / PWA: Capacitor
  34. Headless WordPress Hosting • Use WordPress as a Content manager

    & API • Call from JS SPA to WP API • Fully managed WordPress
  35. 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
  36. 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(<App />, document.getElementById('root')); // Call the element loader after the app has been rendered the first time defineCustomElements(window);
  37. 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]) }
  38. Lazy load Capacitor component const loadCapComponents = async () =>

    { // Load Component async const { DemoClipboard } = await import('./Clipboard') return <DemoClipboard /> } const Component: FC<Props> = (props) => { // state for handle pwa-elements components const [elem, setSelem] = useState<JSX.Element>( <p>Loading….</p> ) // Load pwa-elements components useEffect(() => { if (typeof window !== 'undefined') { loadCapComponents().then(elements => { setSelem(elements) }) } }, []) return ( <Layout>{elem}</Layout> ) }
  39. 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
  40. 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/