Application mobile hybride
Workshop - 22 avril 2020
Mathieu Castets

Application native
- Performances
- Fonctionnalités "natives"
- Mises à jour des SDK
- Nouvelles fonctionnalités
- Corrections de bugs
- Native look and feel / UX

Application native
- Langages différents
- Objective-C / Swift
- Java / Kotlkin
- SDK différents
- IDE différents
- Xcode
- Android Studio





Application multiplateforme
Write once, run anywhere
- Fin 2018
- Dart
- Présence faible sur les stores
- Écosystème indépendant

- 2011
- Microsoft
- .NET / C#
- ~8% du marché des app
- Visual Studio Community
- Accès limité aux bibliothèques open source
- 2015
- Telerik
- JS / TS / Angular / Vue.js mais...
- ... pas de DOM (XML seulement)
- CSS limité
- Popularité faible

- 2015
- React / JSX / Virtual DOM
- ~8 à 13% du marché des app
- Native look and feel
- Pas de HTML
- "Learn once, write anywhere"

Équipes pluridisciplinaires web
- HTML
- CSS / SASS
- JS / TS
- Protocole HTTP
- Frameworks (AngularJS / Angular)
- Bibliothèques (npm / yarn)
- Web app existante : réutilisation de code ?
Application hybride

Une application multiplateforme mais avec les technologies du web
- 2008
- Fondation Apache
- Open source
- WebView
- config.xml
- Fonctionnalités natives via plugins
- 25 à 40% du marché des app

Fonctionnement

Un peu d'histoire
- 2008 : Nitobi (CA) créé PhoneGap
- 2011 : Adobe rachète Nitobi et PhoneGap
- Code source transféré à la foundation Apache (open source) en "Cordova"
- PhoneGap reste un produit commercial Adobe : une distribution de Cordova

👍
- Langages / frameworks web
- Communauté
- Partage de code
- Design
- Outils de développement communs
- Fonctionnalités "natives" via plugins Cordova
👎
- Performances
- (Fonctionnalités "natives" via plugins Cordova)
- (Native look and feel)
Application hybride
Cordova + Framework JS
Fonctionnalités "natives" via plugins Cordova

La relève de Cordova
Le roi est mort, vive le roi !

- 2019
- Ionic
- Open source
- WebView
- 20+ core plugins
- iOS / Android / PWA / Electron
- Rétrocompatibilité plugins Cordova
- Philosophie différente



Cordova
- 👴 12 ans
- Open Source
- WebView
- 📚 3000+ plugins
- 🛶 Communication JS
- 👕 Communauté
- 🌱 Mauvais support Browser
- 🌍 CLI global
- Abstraction "config.xml"
Capacitor
- 👦 1 an
- Open Source
- WebView
- 📕 20+ core plugins
- 🛥 Interface TS
- 👔 Ionic + communauté
- 🌳 Built-in PWA & web support
- 🏙️ CLI locale
- Sources natives
- Support Electron
- Rétrocompatibilité Cordova
- ⚡ Live reload
- ⏰ Suppression event deviceready


Native look and feel


Ionic à la rescousse

Open source
- Framework
- Ionic Native
- Capacitor
- Ionic Animations
- Ionicons
- Stencil
Entreprise
- Support
- Revue de code
- Audit de sécurité
- IDE : Ionic Studio
- CI/CD : Ionic AppFlow
- Solutions clé en main

Ionic
- 2013
- 10 à 16% du marché des app
- Angular / React / Vue
- 100+ composants UI
- Native look and feel
- Mises à jour (plateform guidelines)
- Animations
- Navigation
- Touch gestures
- Standards du web
- Web Component
- Shadow DOM
- Variables CSS

(framework)
Aperçu :
composants & patterns

ion-refresher
<ion-content>
<ion-refresher slot="fixed" pullFactor="0.5" pullMin="100" pullMax="200">
<ion-refresher-content
pullingIcon="arrow-dropdown"
pullingText="Pull to refresh"
refreshingSpinner="circles"
refreshingText="Refreshing...">
</ion-refresher-content>
</ion-refresher>
</ion-content>

ion-skeleton-text
skeleton screens are perceived as being shorter in duration when compared against a blank screen and a spinner
<ion-item *ngIf="data">
<h3>
{{ data.heading }}
</h3>
<p>
{{ data.body }}
</p>
</ion-item>
<ion-item *ngIf="!data">
<h3>
<ion-skeleton-text animated style="width: 50%"></ion-skeleton-text>
</h3>
<p>
<ion-skeleton-text animated style="width: 80%"></ion-skeleton-text>
</p>
</ion-item>

ion-infinite-scroll
<ion-content>
<ion-list>
...
</ion-list>
<ion-infinite-scroll threshold="100px" (ionInfinite)="loadData($event)">
<ion-infinite-scroll-content
loadingSpinner="bubbles"
loadingText="Loading more data...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
</ion-content>

ion-img
lazily load an image when ever the tag is in the viewport
<ion-list>
<ion-item *ngFor="let item of items">
<ion-thumbnail slot="start">
<ion-img [src]="item.src"></ion-img>
</ion-thumbnail>
<ion-label>{{item.text}}</ion-label>
</ion-item>
</ion-list>

Ionic Native
- TypeScript wrapper
- 200+ plugins

Exemple
import { ScreenOrientation } from '@ionic-native/screen-orientation/ngx';
constructor(private screenOrientation: ScreenOrientation) { }
...
// set to landscape
this.screenOrientation.lock(this.screenOrientation.ORIENTATIONS.LANDSCAPE);
// allow user rotate
this.screenOrientation.unlock();
// detect orientation changes
this.screenOrientation.onChange()
.subscribe(() => console.log(`Orientation changed to ${this.screenOrientation.type}`));
$ cordova plugin add cordova-plugin-screen-orientation
$ npm install @ionic-native/screen-orientation
Installation
Usage
Application hybride moderne

Application hybride, un monde idéal ?

Apparition de nouvelles problématiques

ll ne faut pas crier victoire trop tôt.
First Meaningful Paint
- 📶 Connectivité limitée
- ⚠️ Serveur down
- 🐢 Assets distants
- 🔥 Exceptions
- Appels réseaux strictement nécessaires
- Payload serveur optimisée
- Splash screen
- Assets locales
- Gestion des exceptions
- Retry policy
👎
👍
Stockage local
🌱 Local Storage
🌿 @ionic/storage
- Taille limitée
- Non persistent
- Clé / valeur
- Basé sur LocalForage
🌳 @ionic-native/sqlite
$ cordova plugin add cordova-sqlite-storage
$ yarn add @ionic/storage
$ cordova plugin add cordova-sqlite-storage
$ yarn add @ionic-native/sqlite
- Moteur de BDD
- Transactions, tables, ...
Cookies 🤯





CORS
Cross-Origin Resource Sharing

http://localhost

ionic://localhost
https://api.example.com
XMLHttpRequest cannot load https://api.example.com.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'ionic://localhost' is therefore not allowed access.
Deux solutions
- Accès serveur requis
- Simple
- Aucune action serveur
- Complexifie les devs et tests
Access-Control-Allow-Origin: origin | *
$ cordova plugin add cordova-plugin-advanced-http
$ yarn add @ionic-native/http
CSP
Content Security Policy

ionic://localhost
⚠️ frame-ancestors * ne s'appliquent pas aux custom schemes comme ionic://
Content-Security-Policy "default-src * 'unsafe-inline'; frame-ancestors *; img-src * data:";
Solution sur payment.example.com
Content-Security-Policy "default-src * 'unsafe-inline'; frame-ancestors * ionic:; img-src * data:";
<iframe src="https://payment.example.com"></iframe>
Refused to load https://payment.example.com because it does not appear in the frame-ancestors directive of the Content Security Policy.
APIs externes
- Danger sur l'exposition des clés d'API
- Header "Referer" absent : protection caduque
- Préférer une solution native si possible
Build et release
Fastlane - App automation done right
Sources
- Parts de marché : https://appfigures.com/top-sdks/development/apps
- Composants Ionic : https://ionicframework.com/docs/components
- Ionic Native : https://ionicframework.com/docs/native
- Capacitor : https://capacitor.ionicframework.com/docs/getting-started/
- Fastlane : https://docs.fastlane.tools/
Questions ?

Application mobile hybride
By Mathieu Castets
Application mobile hybride
- 130