3. Capacitor JS 02.05.2024

3.1. Beschreibung

Capacitor ist eine plattformübergreifende native Laufzeitumgebung, die die Erstellung von mobilen Anwendungen erleichtert. Entwicklern wird es ermöglicht,Webanwendungen in mobile Apps für iOS, Android und andere Plattformen zu konvertieren. Capacitor wurde 2019 von den Gründern des Ionic Frameworks auf den Markt gebracht und stellt die nächste Evolution von Hybrid-Apps dar. Es wurde als Alternative des Cordova Frameworks entwickelt und zielt darauf ab, eine flexiblere, erweiterbarere und zukunftisichere Alternative zu Cordova zu sein. Capacitor Dokumentation Capacitor LinkedIn

3.2. Wie Capacitor funktioniert

Capacitor bietet einen konsistenten, weborientierten API-Baukasten an. Dadurch wird es einer Anwendung ermöglicht, so nah wie möglich an den Webstandards zu bleiben und zudem umfrangreiche Funktionen nativer Geräte zu bieten. Um auf diese Funktionen zugreifen zu können, verwendet man zum Beispiel ein Plugin für Swift auf iOS, Java auf Android oder JavaScript für das Web. Capacitor Dokumentation

In wenigen Schritten kann man Capacitor in seine Anwendung einbinden: - CapacitorJS Startseite

  1. Hinzufügen von Capacitor in eine bestehende Webanwendung

npm install @capacitor/cli @capacitor/core
npx cap init
  1. Installieren der nativen Plattformen auf die man zugreifen möchte

npm install @capacitor/ios @capacitor/android
npx cap add ios
npx cap add android
  1. Zugreifen auf die nativen APIs Durch die nativen Plugin-APIs ist es extrem einfach, auf gemeinsame Gerätefunktionen zuzugreifen und diese plattformübergreifend aufzurufen

  • Benachrichtigungen:

import { LocalNotifications } from '@capacitor/local-notifications';

LocalNotifications.schedule({
  notifications: [
    {
      title: "On sale",
      body: "Widgets are 10% off. Act fast!",
      id: 1,
      schedule: { at: new Date(Date.now() + 1000 * 5) },
      sound: null,
      attachments: null,
      actionTypeId: "",
      extra: null
    }
  ]
});
  • Lokalisierung

import { Geolocation } from '@capacitor/geolocation';

// get the users current position
const position = await Geolocation.getCurrentPosition();

// grab latitude & longitude
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
  • Kamera

import { Camera, CameraResultType } from '@capacitor/camera';

// Take a picture or video, or load from the library
const picture = await Camera.getPicture({
  resultType: CameraResultType.Uri
});

Es gibt noch viele andere Plugins die man einbinden kann, unter anderem kann man mit Hilfe eines Plugins auf das native Dateisystem zugreifen, was es dem Benutzer ermöglicht Dokumente zu lesen und zu speichern.

3.3. Capacitor vs. Cordova

Wie bereits im Abschnitt 3.1 kurz beschrieben, soll Capacitor eine Alternative zu Cordova sein. Cordova wurde 2009 veröffentlicht und ist wie Capacitor ein Open-Source-Projekt, welches darauf ausgelegt ist, Webanwendungen auf verschiedene Plattformen auszuführen. Progressive Web Apps werden von Cordova jedoch nicht unterstützt. Während Cordova in der Vergangenheit eine zuverlässige Lösung für hybride App-Entwicklung war, repräsentiert Capacitor die Zukungt der Framework Anwendungen. Durch Capacitor können Entwickler das volle Potenzial moderner Webtechnologien ausschöpfen und gleichzeitig native Funtkionen nahtlos in ihre Anwendungen integrieren.Damit wird ein angenehmes Benutzererlebnis auf verschiedenen Plattformen geboten. Doch obwohl Capacitor zahlreiche Vorteile für die moderne App-Entwicklung bietet, ist Cordova in bestimmten Kontexten nach wie vor relevant insbesondere für Projekte mit spezifischen Anforderungen, bestehenden Cordova-Abhängigkeiten oder Entwicklern, die mit dem Arbeitsablauf und Cordova bereits vertraut sind. Cordova vs. Capacitor

3.4. Vorteile

  • Capacitor ist plattformunabhängig und kann in jedes bestehende Web-Projekt oder Framework eingebunden werden. Somit kann jedes React, Vue oder Svelte Projekt in eine native Mobil-App konvertiert werden

  • Capacitor ist einfach zu installieren und zu verwenden (siehe Abschnitt 3.2)

  • Capacitor bietet eine umfrangreiche Sammlung von nativen Plugins. Somit wird es dem Entwickler ermöglicht, auf native Funktionen der Geräte zuzugreifen wie z.B. Kamera, GPS oder Push-Benachrichtigungen.

  • gute und umfangreiche Dokumentation

3.5. Verwendung in Todo-Listen Anwendung

Aufbauend auf unserer bereits bestehenden Svelte ToDo-App haben wir Capacitor in die Anwendung eingebunden. Dadurch, dass Capacitor auf der bestehenden Anwendung aufsetzt, konnten wir bestehende Funktionen wiederverwenden.

Um Capacitor auszutesten, haben wir für die einzelnen Todo Cards eine Action Sheet Component eingebaut:

 const showActionSheet = async () => {
    const actionSheetOptions = [
        {
            title: 'Share',
            onTap: async () => {
                const descriptionText = description ? `(${description})` : '';
                void Share.share({
                    text: `${name} ${descriptionText} - Shared from ToDo App`
                });
            }
        },
        {
            title: 'Edit',
            onTap: () => startEditing()
        },
        {
            title: 'Remove',
            style: ActionSheetButtonStyle.Destructive,
            onTap: () => deleteItem()
        },
        {
            title: 'Cancel',
            style: ActionSheetButtonStyle.Destructive,
            onTap: () => {
            }
        }
    ]

    const result = await ActionSheet.showActions({
        title: 'Quick Actions',
        message: 'Select an action to perform',
        options: actionSheetOptions
    });

    actionSheetOptions[result.index].onTap();
};

Im eigens implementierten onTap-Callback wird jeweils die entsprechende Aktion ausgeführt, die auf bereits bestehende Funktionen zurückgreift.

Wie man sieht, ist es sehr einfach, native Features in einer bestehenden Anwendung zu integrieren.

Die Funktionalitäten werden dafür jeweils aus eigenen Packages importiert:

import {ActionSheet, ActionSheetButtonStyle} from '@capacitor/action-sheet';
import {Share} from '@capacitor/share';

3.5.1. SSL Verschlüsselung für Netzwerk-Kommunikation

Android erlaubt Apps Netzwerk-Kommunikation nur über eine verschlüsselte Verbindung mit vertrauenswürdigem SSL Zertifikat. Um Trotzdem mit dem Backend kommunizieren zu können, wurde ein self-signed SSL Zertifikat erstellt.

Das SSL Zertifikat wurde mit folgenden Befehlen generiert:

  • openssl req -new -newkey rsa:4096 -nodes -out todoapp.csr -keyout todoapp.key -extensions v3_ca -subj "/C=DE/O=ToDoApp/OU=ToDoApp/CN=ToDoApp"

  • openssl x509 -signkey todoapp.key -days 7300 -req -in todoapp.csr -set_serial 01 -out todoapp.crt -extfile todoapp.v3.ext

  • openssl pkcs12 -export -out todoapp.pfx -inkey todoapp.key -in todoapp.crt -certfile todoapp.crt

Die daraus resultierende .pfx Datei enthält das Zertifikat, welches das BE verwendet. Über die Environment variable ASPNETCORE_Kestrel__Certificates__Default__Path kann das Zertifikat dem BE bereitgestellt werden, wenn es über ein volume eingebunden wurde.
docker-compose.yml:

environment:
  - ASPNETCORE_URLS=https://+:443
  - ASPNETCORE_Kestrel__Certificates__Default__Password=${TODO_API_SSL_CERT_PASSWORD}
  - ASPNETCORE_Kestrel__Certificates__Default__Path=/ssl/todoapp.pfx
ports:
  - '4433:443'
volumes:
  - ./ssl:/ssl:ro

Weil es sich um ein self-signed Zertifikat handelt, akzeptiert Android dennoch noch keine Verbindungen, weil die CA nicht bekannt ist. Um die Kommunikation zwischen App und BE trotzdem zu ermöglichen, muss dem self-signed Zertifikat explizit vertraut werden. Dazu lässt sich in der Android-App ein xml resource file anlegen.
network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <base-config cleartextTrafficPermitted="false">
    <trust-anchors>
      <certificates src="@raw/todoapp"/>
    </trust-anchors>
  </base-config>
</network-security-config>

Das generierte todoapp.crt Zertifikat muss dann als raw resource file in die App kopiert werden.

Abschließend muss das network_security_config.xml im AndroidManifest.xml referenziert werden:

<application android:networkSecurityConfig="@xml/network_security_config">
  [...]
</application>

3.6. Nachteile

  • es besitzt weniger getestete Plugins als Cordova. Somit können Fehler oder Kompatibilitätsprobleme auftreten.

  • da Capacitor relativ neu ist, hat es noch keine so umfangreiche Dokumentation und Communityunterstützung als Cordova

  • Capacitor benötigt mehr native Abhängigkeiten und Konfigurationen als Cordova, was die Komplexität der App erhöhen kann

Capacitor LinkedIn

3.7. Fazit

Capacitor bietet den Vortel plattformübergreifende Apps zu erstellen. Dies spart Entwicklungskosten und -zeit. Dennoch muss man dazu sagen, dass die Apps dann nicht die gleiche Qualität und Perfomance bieten wie native Anwendungen. Wenn man daher von Anfang an die Absicht hat, eine qualitativ hochwertige App zu entwickeln und über aussreichend Ressourcen und Fachkenntnisse verfügt, sollte man sich eher auf andere Tools konzentrieren, die auf die Entwicklung nativer Anwendungen spezialisiert sind. Ein Beispiel hierfür wäre zum Beispiel das Framework Flutter. Diese Frameworks bieten in der Regel eine größere Qualität, eine bessere Kontrolle über die Benutzererfahrung. Außerdem hat man dann auch die volle Bandbreite der Funktionen und Ressourcen der jeweiligen Plattform.

3.8. Quellen