Mobile Anwendungsentwicklung im Enterprise-Umfeld: NativeScript
Mobile Entwicklung für Enterprise Anwendungen
Bei der mobilen App-Entwicklung gibt es heutzutage viele verschiedene Optionen.
Im letzten Artikel [3] wurde bereits React-Native als ein Weg zum Ziel beschrieben.
Dort wurde die insbesondere für Enterprise-Anwendungen größtenteils fehlende MVC-Trennung angemerkt.
Eine weitere Alternative mit anders umgesetzter Separation of Concerns ist NativeScript [1].
In diesem Artikel wollen wir eine Basis für App-Entwicklung aller Art mit NativeScript legen, daher ist der gesamte Quellcode auf GitHub verfügbar [2]. Er kann direkt ausgechecked und auf die eigenen Bedürfnisse ausgebaut werden. Das spart Zeit beim initialen Setup.
NativeScript
Im letzten Artikel [3] haben wir das Universum der mobilen Entwicklung bereits in Progressive Web Apps und native Anwendungen unterteilt. Während Progressive Web Apps im Browser laufen, werden native Apps direkt auf dem System gestartet. Dadurch können sie auf erweiterte Plattformfunktionen wie Push-Benachrichtigungen und Standortinformationen zugreifen.
Bei den nativen Anwendungen stellt sich die Frage, ob die eigene Anwendung direkt für die entsprechenden Plattformen mit den dazugehörigen spezifischen Tools entwickelt werden soll. Bei iOS-Apps wäre das heutzutage XCode und Swift, bei Android-Apps meist Java und Android Studio. Dies erfordert zwei Teams, welche zwei komplett separate Anwendungen entwickeln und warten müssen. Im Optimalfall sollten zusätzlich beide Anwendungen synchron gehalten werden, was zu Verzögerungen im Featureauslieferungsprozess führen kann. Es kann vorkommen, dass ein Feature, welches auf einer Plattform trivial zu implementieren ist, auf der anderen dann doch nicht ganz so einfach umsetzbar ist. Dann muss unter Umständen das erste Team einige Zeit warten bis das neue Feature auch auf der zweiten Plattform funktioniert.
Als Lösung zu diesem und weiteren Problemen bieten sich die Cross-Platform Frameworks an. Wie auch React Native ist NativeScript ein derartiges Cross-Platform Framework für native Mobilanwendungen.
Cross-Platform-Framworks haben den Vorteil, dass aus einer einzelnen Codebasis native Anwendungen für die unterschiedlichen mobilen Betriebssysteme generiert werden können. Daher müssen Entwickler für die meisten Anwendungen kein allzu tiefes konkretes Plattformwissen besitzen, was den Entwicklungsaufwand und die Time to Market reduziert. Der gesamte Quellcode muss nur einmal entwickelt werden und wird automatisiert auf beide Plattformen transpiliert. Dadurch kann außer in Ausnahmefällen sichergestellt werden, dass ein neues Feature gleich auf beiden Plattformen funktioniert.
Wie schon angesprochen gibt es neben NativeScript auch andere Frameworks wie React Native, dem wir bereits einen eigenen Artikel gewidmet haben [3]. Im Kontrast zu React Native sticht bei NativeScript insbesondere die gute Integrierbarkeit mit Web-Frameworks wie Angular und Vue.js ins Auge. Das tröstet auch gleich über die etwas kleinere Gefolgschaft von NativeScript insbesondere im Vergleich zu React Native hinweg. Auch eine vernünftig funktionierende MVC-Trennung sehen wir als Vorteil im Bezug auf die Entwicklung von Enterprise-Anwendungen.
Da auch NativeScript TypeScipt-Unterstützung von Haus aus mitbringen, möchten wir dieses auch gleich statt JavaScript für unseren kleinen Prototypen verwenden.
Verschwenden wir nun keine weitere Zeit, sondern fangen gleich an!
Setup der Entwicklungsumgebung und erste Anwendung
Wir gehen hier davon aus, dass eine entwicklungsfähige Android-Umgebung mit korrekt gesetzten Umgebungsvariablen bereits vorliegt.
Das Setup einer Android-Entwicklungsumgebung würde für diesen Artikel den Rahmen sprengen.
Sofern es beim Durchführen der Setupschritte für die NativeScript-Anwendung zu Problemen kommt, hilft der Befehl tns doctor
, welcher die Umgebung prüft und detailliert anzeigt, ob und wo ein Problem vorliegen könnte.
Hierbei werden insbesondere die zum Betrieb von NativeScript notwendigen Teile des Android Platform SDK sowie Emulatorunterstützung geprüft.
Als Emulator für ein Android-Smartphone verwenden wir für diesen Artikel erneut die virtuellen Geräte vom AVD-Manager in Android Studio [6].
-
Für die lokale Entwicklung mit NativeScript und Typescript wird zunächst einmal NodeJS und NPM benötigt. NodeJS kann auf der offiziellen Seite [5] als Archiv heruntergeladen werden. Nach dem Herunterladen sollte der
bin
-Ordner zumPATH
oder zu den Benutzerumgebungsvariablen (Windows) hinzugefügt werden. -
Für die Initialisierung einer neuen Projekt-App wird das NativeScript Command Line Interface benötigt. Es kann via Node mit dem Befehl
npm install -g nativescript
installiert werden. -
Nun kann ein neues Projekt mittels
npx tns create <app-name> --template typescript
initialisiert werden. Dadurch wird ein neues App-Projekt mit einfacher Konfiguration und einer Beispielseite sowie Typescriptunterstützung angelegt. -
Als nächstes sollte mit
tns devices
geprüft werden, ob der Emulator in der Geräteliste auftaucht. Sofern dies nicht der Fall hilft es, ihn einmalig zu starten. Sobald ein Emulator einmal gelinked ist, kann er mit dem Befehl in Schritt fünf gestartet werden. -
Mit dem Befehl
tns run android
im Projektverzeichnis (das mit derpackage.json
-Datei, nicht das mit derapp.ts
-Datei) wird die Anwendung auf dem Emulator gestartet. Wer ein passendes Smartphone zur Hand hat kann stattdessen auch den Befehltns preview
verwenden und den daraufhin in der Konsole auftauchenden QR-Code scannen. Dann kann über die beiden Apps NativeScript Playground [4] und NativeScript Preview Apps [5] direkt auf dem Smartphone getestet werden. Diese beiden Apps haben wir natürlich auch getestet und sie funktionieren bisher hervorragend. Allerdings hat vielleicht nicht jeder ein Androidgerät zur Hand, weswegen wir in diesem Artikel auf einen Emulator setzen.
Abschließend sollte sich im Emulator die Demo-App öffnen.
Sehen wir uns mal an, wie es zu dem hübschen Button und dem Zähler in der App kommt.
Die erste NativeScript-App
An dieser Stelle sollte noch einmal darauf hingewiesen werden, dass für besonders zeitbewusste Lesende alle Codebeispiele als git-Repository verfügbar sind [2]. Dort können dann nacheinander die Commits ausgecheckt werden, um die Kapitel in dieses Tutorial Schritt für Schritt nachzuvollziehen.
Wie auch bei React Native werfen wir als erstes einen Blick auf die Highlights der erzeugten Verzeichnisstruktur.
ShowcaseApp/
├── app/
: Quellcode für die Cross Platform App.
│ ├── `App_Resources/
: Resourcen wie Bilder oder plattformspezifische Konfiguration (z.B. AndroidManifest.xml
).
│ ├── `app.ts
: Haupteinstiegspunkt der Typescript Anwendung.
│ ├── `main-page.ts
: Managementcode der zur Hauptseitenlogik und oberflächenbeschreibende XML-Datei mapped.
│ ├── `main-view-model.ts
: Enthält die Logik assoziiert zum Hauptseitenmodul.
│ └── `main-page.xml
: Beschreibt den Oberflächenaufbau und das Styling der Hauptseite
├── node_modules/
: Abhängigkeiten für dieses Projekt.
├── platforms/
: Quellcode für die nativen Apps für Android und iOS.
├── package.json
: Definition der Abhängigkeiten für dieses Projekt.
└── tsconfig.json
: Compiler- und Linterkonfigurationen für Typescript.
Ändern wir nun im ersten Schritt den angezeigten Text und die Hintergrundfarbe.
Kosmetische Anpassungen des Prototypen
Dazu müssen wir sowohl die primäre View, als auch die Logik dahinter bearbeiten. Ein bisschen wie bei WPF. Da bei NativeScript eine strenge MVC-Trennung vorliegt, müssen dafür mehrere Dateien bearbeitet werden.
Um die Hintergrundfarbe anzupassen, werden zunächst zwei CSS-Styles in die app.css
eingefügt:
.green{
background-color: #d3fccf;
}
.dark-green{
background-color: #819c7e;
}
Anschließend wird die Datei main-page.xml
angepasst, welche das Layout definiert:
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="green">
<ActionBar title="My App" icon="" class="dark-green"></ActionBar>
<StackLayout class="p-20">
<Label text="Schaltfläche antippen" class="h1 text-center"/>
<Button text="ANTIPPEN" tap="{{ onTap }}" class="-primary"/>
<Label text="{{ message }}" class="h2 text-center" textWrap="true"/>
</StackLayout>
</Page>
In dieser Datei gibt es ein Page
-Objekt zu sehen, welches eine ActionBar
und ein StackLayout
mit zwei Labels
und einem Button
dazwischen definiert.
Hier wurde im Vergleich zur Orginaldatei auf dem Page
-Objekt auf oberster Ebene eine CSS-Klassendefinition class="green"
eingefügt.
Außerdem wurde auf dem ActionBar
-Element die zweite CSS-Klasse dark-green
eingefügt.
Um den statischen Text anzupassen, wurde in Zeile 4 und 5 jeweils die text
-Eigenschaft der Label
- und Button
-Elemente geändert.
Als letztes soll noch der dynamische Text angepasst werden. Dazu müssen wir an die Business Logik der Anwendung.
Diese ist in der Datei main-view-model.ts
zu finden.
import { Observable } from "tns-core-modules/data/observable";
export class HelloWorldModel extends Observable {
private _counter: number;
private _message: string;
constructor() {
super();
this._counter = 42;
this.updateMessage();
}
...
private updateMessage() {
if (this._counter <= 0) {
this.message = "Hoorraaay! You unlocked the NativeScript clicker achievement!";
} else {
this.message = `${this._counter} taps left`;
}
}
}
Dort sehen wir einen Import Observable
und eine Klassendefinition mit Konstruktor und den zwei privaten Variablen _counter
und message
.
Um auch den dynamischen Text auf Deutch zu übersetzen, wird im Listing in Zeilen 14 und 16 die message
übersetzt:
this.message = "Hoorraaay! You unlocked the NativeScript clicker achievement!";
🡆 this.message = "Yuy! NativeScript-Klicker Errungenschaft freigeschaltet
sowie
this.message =
🡆 ${this._counter} taps left
;this.message =
Noch ${this._counter} Mal antippen
;
Das Resultat sieht wie folgt aus:
Der Hintergrund ist jetzt grün und der Text auf Deutsch.
Anzeige dynamisch aktualisieren mittels Observables
Wer genau hingesehen hat, dem ist aufgefallen, dass wir oben im Snippet ein Stück Code ausgelassen haben.
Und zwar die Variablendefinition von _message
sowie deren Getter und Setter:
get message(): string {
return this._message;
}
set message(value: string) {
if (this._message !== value) {
this._message = value;
this.notifyPropertyChange("message", value);
}
}
Während der Getter relativ trivial ist, wird beim Setter neben einem Änderungscheck (this._message !== value
) die ominöse Methode notifyPropertyChange
mit dem Attribute message
und dem neuen Wert aufgerufen.
Hier kommt das Observer-Pattern ins Spiel.
Denn diese Methode sorgt dafür, dass alle Listener, welche auf HelloWorldModel.message
registriert sind, automatisch über die Änderung des Wertes informiert werden.
Ohne diesen Methodenaufruf würde zwar die Variable geändert, aber sich beispielsweise der angezeigte Status in der UI nicht ändern.
Ein Beispiel für diese Listenerregistrierung ist in der Datei main-page.xml
(oben gelisted) in Kombination mit der main-page.ts
zu finden.
In der Datei main-page.ts
sehen wir nämlich, dass an die aktuelle Page
eine neue Instanz des HelloWorldModel
gebunden wird:
page.bindingContext = new HelloWorldModel();
Relativ weit unten in der Datei zur Oberflächenbeschreibung main-page.xml
findet sich außerdem die Definition unseres Label
:
<Label text="{{ message }}" class="h2 text-center" textWrap="true"/>
Das referenzierte Feld message
geht hierbei auf den an das Page
-Objekt gebundenen Kontext, der in der main-page.ts
ja auf HelloWorldModel
geht.
Als Resultat wird also ein Objekt vom Typ Label
erzeugt und gerendert, welches die message
-Variable in unserer HelloWorldModel
-Klasse observed und von der oben genannten notifyPropertyChange
- Methode informiert wird.
So viel zu den Observern. Unabhängig davon wäre die bisherige Anwendung allerdings auch als Progressive Web App leicht realisierbar gewesen. Was bringt eine native Anwendung ohne native Funktionalität? Daher fügen wir nun mal eine nur für native Geräte vorbehaltene Funktion ein: GPS Geolocation.
Verarbeitung von Standortinformationen
Um Standortinformationen, etwa vom GPS-Chip eines Smartphones, aus NativeScript heraus abrufen zu können, wird zunächst das offizielle NativeScript-Plugin nativescript-geolocation
benötigt [7].
Es wird mittels npx tns plugin add nativescript-geolocation
, ausgeführt im Projektverzeichnis mit der package.json
-Datei, zum Projekt hinzugefügt.
Öffnen wir als nächsten Schritt unsere Typescript-Klasse mit der Business-Logik, main-view-model.ts
.
Dort importieren wir nun ganz oben die Geolocation
-Klasse des gerade eben hinzugefügten nativescript-geolcation
-Plugins.
import * as Geolocation from 'nativescript-geolocation';
Anschließend passen wir die private Methode updateMessage
im Body der HelloWorldModel
-Klasse in der main-view-model.ts
wie folgt an:
private updateMessage() {
Geolocation.enableLocationRequest(true)
.then(() => {
Geolocation.isEnabled().then(locationEnabled => {
if (locationEnabled) {
Geolocation.getCurrentLocation({ desiredAccuracy: Accuracy.high, maximumAge: 5000, timeout: 20000 })
.then(result => {
this.message = `${result.latitude} / ${result.longitude}`;
});
}
});
});
}
Etwas verschachtelt.
Zunächst wird in Zeile zwei eine Standortanfrage abgeschickt.
Im resultierenden Promise
(Zeile 3) wird dann, sofern der Standort überhaupt aktiviert ist, ein weiterer Promise
zurückgegeben (Zeile 4).
Im Kontext dieses weiteren Promise
wird dann der Standort mittels GeoLocation.getCurrentLocation(…)
(Zeile 6) abgerufen.
Im daraus resultierenden, dritten Promise
(Zeile 7) wird schließlich das lokale Feld message
mit den empfangenen Standortinformationen aktualisiert.
Die Anzeige in der App auf dem Emulator wird danach automatisch aktualisiert.
Das result
beinhaltet neben Latitude und Longitude weitere Felder wie Höhe, Geschwindigkeit oder Präzision. Im Detail sind diese der Dokumentation [7] zu entnehmen.
Nach dem Speichern der Codeänderung aktualisiert sich die App und fragt gleich nach Berechtigungen für den Standortzugriff (Abbildung 4). Nach einem Klick auf die Schaltfläche wird umgehend der aktuelle Standort ausgegeben (siehe Abbildung 5).
Ein weiteres häufig verwendetes Feature nativer Anwendungen sind Push-Benachrichtigungen. Da eine vollständige Firebase- oder Apple-PNS-Konfiguration den Scope dieses Artikels sprengen würde, beschränken wir uns hier auf lokale Push-Benachrichtigungen.
Push Benachrichtigungen mit NativeScript
Für die lokalen Push-Benachrichtigungen benötigen wir erneut ein Plugin.
Da es hier zum aktuellen Zeitpunkt kein offizielles Plugin gibt, wird ein häufig verwendetes von Eddy Verbruggen verwendet [8].
Installiert wird es wie alle unserer Plugins mittels npx tns plugin add nativescript-local-notifications
.
Neben der Importanweisung import { LocalNotifications } from "nativescript-local-notifications";
fügen wir nun eine neue Methode zum Versand von Push-Benachrichtigungen in der HelloWorldModel
-Klasse in der main-view-model.ts
ein:
private sendPushNotification(title: string, body: string){
LocalNotifications.requestPermission().then(granted => {
if(granted) {
LocalNotifications.schedule([{
id: 1337,
title: title,
body: body
}]);
}
});
}
Sofern also die Berechtigung für Push-Benachrichtigungen gegeben ist (Check Zeile 2), wird eine neue Push-Benachrichtigung mittels LocalNotifications.schedule
(Zeile 4ff) geplant.
Sofern der Parameter at
undefiniert bleibt, wird die Push-Benachrichtigung sofort versandt.
Während Titel und Body der Push-Notification selbsterklärend sein sollten, identifiziert die ID eine einzelne, geplante oder angezeigte Benachrichtigung.
Mit einem erneuten Aufruf der LocalNotifications.schedule
-Methode mit gleicher ID wird die bestehende Benachrichtigung aktualisiert, statt eine neue zu erzeugen.
Daher bleibt es in unserem Fall bei einer, auch wenn die Schaltfläche mehrmals gedrückt wird.
Nur der Popup wird erneut angezeigt.
In der Liste oben bleibt es aber bei einer Notification.
Abbildung 6 unten zeigt, wie das ganze aussieht, sobald die neue Methode von der updateMessage
-Methode vom letzten Promise
aus aufgerufen wird (this.sendPushNotification("Standort aktualisiert", this.message);
):
Mit LocalNotifications.addOnMessageReceivedCallback(…)
kann im Übrigen ein Listener hinzugefügt werden, der aufgerufen wird, sobald der Benutzer aus dem Hintergrund heraus auf die Push-Notification klickt und damit die App wieder in den Vordergrund holt.
Für unseren kleinen Prototypen haben wir nun neben einer grundlegenden UI auch native Features wie Geolocation und Push-Notifications implementiert. Und nun?
Fazit
Nun ist erst einmal aufgefallen, dass NativeScript insbesondere im Vergleich zu React Native eine gut funktionierende MVC-Trennung umsetzt und damit sehr enterprise-ready daher kommt. Auch die Entwicklung geht sehr rapide von statten. So funktioniert die Hot-Reloading-Funktionalität sowohl auf dem Emulator als auch auf der Playground App, zumindest für Android, hervorragend. Änderungen am Quellcode sind nach einmaligem Speichern direkt in der Anwendung verfügbar. Keine ewigen Wartezeiten auf den Compiler und das Uploading. Wie auch bei React Native handelt es sich bei NativeScript um eine sehr leichtgewichtige Platform, die für Geolocation und Push-Benachrichtigungen bereits externe Plugins vom selben Hersteller oder der Community benötigen. Dies hält die Kernfunktionalität schlank und reduziert unnötige Komplexität.
Aber was soll man mit einer App die die Geolocation als Push-Benachrichtigung ausgibt? Jetzt ist es Zeit, dass Sie sich ans Werk machen und auf das in diesem Artikel Erlernte aufbauen!
Wie wäre es mit einem kleinen Messeprototypen oder einem privaten Miniprojekt?
Auch das GitHub-Projekt einfach mal auszuchecken und ein wenig am Quellcode [2] herumschrauben kann durchaus interessant sein.
Nur so kann man sich ein wirklich eigenes Bild von NativeScript machen.
Wenn es noch Fragen konkret zum Artikel oder zu NativeScript oder mobiler Entwicklung allgemein gibt, stehen wir natürlich gerne zur Verfügung.
Referenzen
-
[2] https://github.com/trion-development/nativescript-enterprise-application/
-
[3] React Native
-
[4] https://play.google.com/store/apps/details?id=org.nativescript.play
-
[5] https://play.google.com/store/apps/details?id=org.nativescript.preview
-
[7] https://github.com/NativeScript/nativescript-geolocation
-
[8] https://github.com/eddyverbruggen/nativescript-local-notifications
Zu den Themen React, NativeScript, Vue und Angular bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:
Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.
Über Steffen Jacobs
Steffen Jacobs (M.Sc) hat Wirtschaftsinformatik an der Universität Mannheim studiert und ist als Consultant und Software-Entwickler tätig. In seiner Freizeit engagiert er sich im OpenSource Umfeld.