• Noser.com
facebook
linkedin
twitter
youtube
  • NOSERmobile
    • Android
    • HTML 5
    • Hybrid Apps
    • iOS
    • Windows Phone
  • NOSERembedded
    • Medizintechnik
  • NOSERprojektmanagement
  • NOSERtesting
  • NOSERlifecycle
    • .NET Allgemein
    • Application Lifecylce Management
    • Apps
    • Architektur
    • ASP.NET
    • Azure
    • Cleancode
    • Cloud
    • Silverlight
    • Visual Studio / Team Foundation Server
    • Windows 8
    • Windows Presentation Foundation
  • NOSERinnovation
    • Big Data
    • Cloud
    • IoT
    • Operations Research
    • Augmented Reality
    • RFID, NFC, Bluetooth LE

ReactiveUI und Xamarin.Forms – Ein Beispiel

12. Dezember 2017
Rehmann Christoph
0
Reactive Programming, Rx, Xamarin, Xamarin.Forms
Banner Xamarin und ReactiveUI

ReactiveUI ist ein cross-platform Mvvm-Framework. Wie der Name vermuten lässt, setzt ReactiveUI auf den Reactive Extensions (Rx) auf. Es bietet neben WPF und UWP auch eine erstklassige Unterstützung für Xamarin Native und natürlich Xamarin Forms. Desweiteren ist es Open-Source und wurde mittlerweile in die .NET Foundation aufgenommen.

Beispiel Projekt

Anhand folgendem Beispiel möchte ich zeige, wie mit ReactiveUI und Xamarin.Forms eine Live-Suche implementiert werden kann. Die App ist einfach gestaltet. Man kann per Eingabefeld nach Ortschaften bzw. ÖV Stationen suchen. Die Suchanfragen werden jeweils an eine REST-Schnittstelle von opendata.ch abgesetzt. Anschliessend werden die Resultate in Form einer Liste dargestellt. Wer den kompletten Source Code einsehen will, findet diesen auf Github.

Anforderungen

  • Die App soll aus nur einer Page bestehen und folgende Elemente beinhalten:
    • Suchfeld,
    • Button
    • Liste für das Anzeigen von Suchresulateten
  • Eine ProgressBar zeigt an, wenn eine Suche am Laufen ist.
  • Der Such-Button soll deaktiviert sein, falls eine Suche am Laufen ist oder wenn das Suchfeld leer ist.
  • Sobald der Benutzer den Button betätigt oder den Text im Suchfeld ändert (Live Suche), soll eine Suchanfrage abgesetzt werden.

Damit der Server jedoch nicht mit unnötigen Suchanfragen bombadiert wird, soll die Anzahl der Aufrufe begrenzt werden. Wenn der Benutzer schnell tippt, macht es wenig Sinn nach jedem Buchstaben sofort eine neue Suche abzusetzen. Die Suche soll erst starten, wenn man für eine bestimmte Zeit keine weitere Eingabe gemacht wird. Wählt man einen kurzen Grenzwert, so fallt dies dem Benutzer auch nicht negativ auf. In diesem Beispiel wurden 500ms verwendet:

live search demo

Der Aufbau

Die Solution ist in drei Projekte aufgeteilt. Jeweils ein Plattformprojekt (iOS/Android) sowie ein Projekt mit einer .Net Standard Klassenbibliothek. In dieser befindet sich die Xamarin.Forms Teil mit View und ViewModel. Es kann somit die komplette Logik zwischen den beiden Apps geteilt werden.

Das ViewModel

Das SearchViewModel beinhaltet alle relevanten Properties. Weiter findet man darin auch die Logik für die Suche. Die basis ViewModel Implementation bei ReactiveUI heisst ReactiveObject. Diese Klasse implementiert bereits INotifyPropertyChanged. Somit kann man Properties direkt wie folgt definieren:

public string SearchQuery
{
	get { return _searchQuery; }
	set { this.RaiseAndSetIfChanged(ref _searchQuery, value); }
}

public ReactiveCommand<string, List<Station>> Search
{
	get { return _searchCommand; }
	private set { this.RaiseAndSetIfChanged(ref _searchCommand, value); }
}

Im Konstruktor des ViewModels werden die einzelnen Properties initialisiert. Für den Search Command kann man die statische Hilfsmethode CreateFromTask von ReactiveUI verwenden:
  Search = ReactiveCommand.CreateFromTask<string, List<Station>>(SearchAsync, CanSearch());

Der erste Parameter ist dabei die Methode, welche einen Suchtext entgegennimmt und eine Liste von Stationen zurück liefert. Beim zweiten Parameter handelt es sich um ein IObservable<bool>. Dieses Observable definiert, ob der Command ausgeführt werden darf oder nicht. Dabei wird auf folgende Bedingungen geachtet:

  • SearchQuery darf nicht leer sein
  • Die Suche darf nicht bereits ausgeführt werden.

Mit Rx ausgedrückt, kann dies wie folgt aussehen:

Observable.CombineLatest(
         this.WhenAnyValue(vm => vm.SearchQuery)
             .Select(searchQuery => !string.IsNullOrEmpty(searchQuery))
             .DistinctUntilChanged(),
         this.WhenAnyObservable(x => x.Search.IsExecuting)
             .DistinctUntilChanged(),
         (hasSearchQuery, isExecuting) => hasSearchQuery && !isExecuting)
     .Do(cps => System.Diagnostics.Debug.WriteLine($"Can Perform Search: {cps}"))
     .DistinctUntilChanged()

CombineLatest führt dabei zwei Observables so zusammen, dass jeweils immer nur der letzte Wert berücksichtigt wird. Der dritte Parameter ist eine Funktion, welche die beiden letzten Werte zu einem einzigen Boolean zusammenführt.

Live Suche (Throttling)

Die Live Suche kann man ebenfalls relativ einfach mit Rx und den Hilfsmethoden von ReactiveUI implementieren. Mit Hilfe der Throttle Funktion können schnell aufeinanderfolgende Eingaben ausgefiltert werden. Dies lässt sich gut mit einem sogenannten Marble Diagramm erklären.

Marble Diagramm für Throttle/Debounce

Die obere Zeitachse stellt den Input Stream dar und die untere Zeitachse den Output Stream. Die Implementation im ViewModel sieht dann wie folg aus:

// erstellt ein IObservable<string> von SearchQuery
this.WhenAnyValue(x => x.SearchQuery)
    // drosselt Änderungen von SearchQuery, sodas diese erst weitergereicht warden,
    // wenn für 500ms keine weiteren Änderungen passieren
    .Throttle(TimeSpan.FromMilliseconds(500), TaskPoolScheduler.Default)
    // Callback soll auf dem UI Thread stattfinden
    .ObserveOn(RxApp.MainThreadScheduler)
    // SearchCommand soll ausgeführt warden (sofern CanExecute == true)
    .InvokeCommand(Search)

An diesem Beispiel sieht man gut, wie ein komplexer Ablauf mit Hilfe von Rx auf einfache Weise umsätzen lässt. Würde man die gleiche Funktionalität in einem imperativen Still implementieren wollen, müsste man zusätzliche Zustands-Variablen und einen Timer verwenden. Rx erlaut jedoch eine deklartive Schreibweise. Diese führt meiner Meinung nach zu verständlicherem Code.

Die View

Nachdem wir nun das ViewModel implementiert haben, muss noch die View umgesetzt werden. Diese kann man entweder mit XAML oder per Code aufbauen. Bindings werden bei ReactiveUI jedoch bewusst im Code-Behind als Expressions geschrieben:

private void InitializeBindings()
{
    // Search Query
    this.Bind(ViewModel, x => x.SearchQuery, c => c.SearchEntry.Text)
        .DisposeWith(_bindingsDisposable);

    // Search Command
    this.BindCommand(ViewModel, x => x.Search, c => c.SearchButton, vm => vm.SearchQuery)
        .DisposeWith(_bindingsDisposable);

    // Activity Indicator
    this.WhenAnyObservable(x => x.ViewModel.Search.IsExecuting)
        .BindTo(ActivityIndicator, c => c.IsRunning)
        .DisposeWith(_bindingsDisposable);

    // Results
    this.OneWayBind(ViewModel, x => x.SearchResults, c => c.SearchResults.ItemsSource)
        .DisposeWith(_bindingsDisposable);
}

Das Command-Binding führt dabei nicht nur den Command bei einem Klick aus sonder kümmer sich auch darum den Button zu aktiviren und deaktivieren.

Fazit

Bisher hatte ich zwar noch nicht die Gelegenheit ReactiveUI in grösseren Projekten einzusetzen. Tortzdem macht das Framework für mich auf den ersten Blick einen guten Eindruck. Es bringt alle Werkzeuge mit, welche ich von einem Mvvm-Framework erwarte. Darüberhinaus liefert es viele nütziche Extensions. Diese erlauben es Bindings und Commands mit Rx zu verknüpfen.

Wer allerdings noch nie mit Rx gearbeitet hat, wird zu beginn einen etwas schweren Einstieg habe, da ReactiveUI sehr stark darauf aufbaut. In diesem Fall sollte man nebst der Dokumentation auch unbedingt die Beispiele genau anschauen.

Native Cross Plattform Apps mit Xamarin

22. Juni 2017
Rehmann Christoph
0

Nach der Idee eine mobile Applikation zu entwickeln stellt sich schnell die Frage, wie man diese auf die unterschiedlichen Plattformen zur Verfügung stellen kann. Dabei gibt es unterschiedliche Lösungsansätze. Unter anderem:

  • Native Entwicklung bzw. eine App pro Plattform
  • Hybrid: eine App für alle Plattformen mit HTML und JavaScript
  • Xamarin

Falls man eine App für mehrere Plattformen entwickeln möchte, ist die native Variante ist mit viel Aufwand verbunden. Der Grund dafür liegt auf der Hand – pro Plattform muss man die App komplett neu entwickeln. Dabei braucht es pro Plattform spezifisches Knowhow. Dies bedeutet oft, dass auch mehrere Entwicklungsteams benötigt werden.

Hybride Apps locken mit dem versprechen die App nur einmal schreiben zu müssen und diese dann auf all den Plattformen zur Verfügung zu haben. Hybride Apps setzen hierfür auf eine hohe Abstraktion der Plattformen. Native Features einer Plattform kann man zwar einbinden, dafür wird jedoch jeweils ein Plugin benötig. Dies kann zu Performance Problemen führen. Hybride Plattformen werden deshalb oft für Apps verwendet, welche nicht oder nur bedingt native Eigenschaften benötigen.

Mit Xamarin hat man die Möglichkeiten sich vom Besten der beiden Welten zu bedienen. Zum einen werden Apps mit einer einzigen Programmiersprache für die jeweiligen Plattformen implementiert. Zusätzlich handelt es sich aber um vollwertige native Applikationen. So ist es möglich auf alle Bibliotheken und Features der jeweiligen Plattformen zuzugreifen. Dabei kann man grosse Teile der Business Logik zwischen den einzelnen Plattformen teilen und wiederverwenden.

Schneller Einstieg mit Xamarin.Forms

Mit Xamarin.Forms ist es möglich, dass man neben der Business Logik auch die UI Implementation zwischen den Apps teilen kann. Erreicht wird dies, indem man das UI in einer XAML-basierten Sprache entwickelt. Anschliessend wird es automatisch in native UI Komponenten übersetzt. Das bedeutet noch schnellere Entwicklungszeit und geringere Kosten. Besonders UWP/WPF Entwickler können dabei von einem schnellen Einstieg profitieren, da sie die Konzepte hinter XAML bereits kennen. Aber auch Entwickler, welche noch keine Erfahrung in der Entwicklung von Apps haben, werden sich schnell zurechtfinden. Sie müssen nämlich nicht noch zuerst die unterschiedlichen UI Ansätze der verschiedenen Plattformen erlernen, sondern können direkt mit Xamarin.Forms loslegen.

Vergleich von Xamarin Native zu Xamarin.Forms

Die Abbildung zeigt auf, wie mit Hilfe von Xamarin.Forms noch mehr Code geteilt werden kann und der plattformspezifische Code weiter reduziert wird.

 

Xamarin.Forms glänzt dabei vor allem bei der Implementierung von einfachen Apps oder Prototypen. Es ist beeindruckend wie schnell man eine App damit erstellen kann.

Volle Kontrolle mit Xamarin Native

Die Vorteile von Xamarin.Forms gegenüber Xamarin Native schwinden, sobald man ein aufwändiges UI implementieren möchte. Denn durch die zusätzliche Abstraktion von Xamarin.Forms kann es besonders bei komplexen Layouts zu Performance-Einbussen kommen. Mit Xamarin Native hat man volle Kontrolle über die spezifischen UI Eigenheiten und SDK Funktionalitäten der verschiedenen Plattformen. Weiter lassen sich die gleichen Tools für die Entwicklung des UI einsetzen, wie bei den nativen Apps.

Xamarin iOS, Android und Forms

Unabhängig ob man Xamarin Native oder Forms verwendet, kann man C# für die Entwicklung einsetzen. Zwar sind Objective-C, Swift und Java grossartige Programmiersprache, jedoch hat sich C# in den letzten Jahren bemerkenswert weiterentwickelt. So ist es zum Beispiel Dank LINQ, Async/Await oder etwa Closures möglich, grossen Boilerplate Code zu vermeiden und saubereren und leserlicheren Code zu schreiben.

Weiter ermöglicht Xamarin auch den Einsatz von nativen Third-Party Bibliotheken. So kann man bereits bekannte Java oder Objective-C Bibliotheken wiederverwenden. Dies bewerkstelligt man mit Hilfe von sogenannten Binding Projekten. Oftmals findet man im Xamarin Component Store oder auf Github bereits entsprechende Bindings und es bleibt einem erspart, diese selber zu generieren.

Fazit

Xamarin ermöglicht es native Apps für iOS und Android zu entwickeln und dabei alle Funktionaltäten der Plattformen zu nutzen. Durch die Verwendung einer Programmiersprache (C#) ist es ausserdem möglich grosse Teile der Business Logik zwischen den Apps zu teilen. Dadurch verringert sich die Entwicklungszeit und man kann eine kürzere Time-to-Market erreichen. Aber nicht nur die Entwicklungszeit und die Kosten kann man durch effizientes Code Sharing minimieren, sondern auch die Anzahl möglicher Fehler. Denn generell kann die Aussage gemachen werden, dass die Anzahl von Fehlern im Code sinkt, wenn weniger Code geschrieben werden muss.

1234567

Tag Cloud

.NET android ANgular Angular Js AngularJs Arduino ASP.Net automated testing Azure Big Data C# C++ Cloud continuous integration Elm Embedded gRPC Internet of Things IoT Java Javascript M2M OWASP Projektmanagement protobuf Python Raspberry Pi Reactive Programming REST Scrum Security Softwarequalität SPA Testen testing Testmanagement Teststrategie Visual Studio WebAPI windows windows phone 8 WPF Xamarin Xamarin.Android Xamarin.Forms

Archive

Current Posts

  • Lasttests mit NBomber
  • Kreative Interaktion mit Xamarin.Forms Touch-Actions
  • Angular vs. React: Ein pragmatischer Vergleich
  • Eine Alternative zu Azure Time Series Insights, bitte?
  • Focus on the Secure Storage service of Trusted Firmware (TFM)

Last Comments

  • Noser Blog Touch-Actions in Xamarin.Forms - Noser Blog bei Mach mehr aus Animationen in Xamarin.Forms mit SkiaSharp
  • Noser Blog Focus on the Secure Storage service of Trusted Firmware (TFM) - Noser Blog bei First run of the Trusted Firmware (TFM) application
  • Noser Blog First run of the Trusted Firmware (TFM) application - Noser Blog bei Focus on the Secure Storage service of Trusted Firmware (TFM)
  • Noser Blog Focus on the Secure Storage service of Trusted Firmware (TFM) - Noser Blog bei Security management with Trusted Firmware
  • Noser Blog ZeroMQ / NetMQ mit Protocobuf - Noser Blog bei Tutorial: Protocol Buffers in ASP.NET Core

Popular Posts

Xamarin.Android Code Obfuscation

6 Comments

ManuScripts: Wenn jemand eine Reise tut... Funktionale Programmierung mit Elm - Teil 1 - Aufbruch

5 Comments

ManuScripts: Wenn jemand eine Reise tut... Funktionale Programmierung mit Elm - Teil 2 - Kein Picknick

4 Comments

Contact us

  1. Name *
    * Please enter your name
  2. Email *
    * Please enter a valid email address
  3. Message *
    * Please enter message
© 2013 NOSER ENGINEERING AG. All rights reserved. Datenschutz | Cookie-Richtlinie