Alle Beiträge von kerry

Unified API: 64 bit für Xamarin.iOS

Alle iOS-Entwickler stehen vor einer Deadline: Ab 1. Februar 2015 müssen alle neuen Apps und App-Updates, die an den App-Store übermittelt werden, gegen das iOS 8 SDK gebaut sein und müssen 64 bit unterstützen. Für Objective-C-Entwickler ist dieses Update hauptsächlich Routine wie bei anderen SDK-Updates. Für Xamarin.iOS-Entwickler bedeutet dies eine Umstellung.

Das ursprüngliche Design der MonoTouch-API verwendete einige der eingebauten .NET-Typen, z.B. System.Int32 (int), System.Single (float) und RectangleF. Leider sind diese Typen alle entweder 32-bit-Typen oder bestehen aus 32-bit-Typen. Was jedoch benötigt wird, sind Typen, die auf 32-bit-Plattformen 32 bit groß und auf 64-bit-Plattformen 64 bit groß sind.

Aus diesem Grund hat Xamarin eine Änderung umgesetzt, die alle Xamarin.iOS-Entwickler betrifft. Da diese Änderungen bestehenden Code sowieso brechen wird, hat Xamarin die Gelegenheit genutzt, einige Unstimmigkeiten in der alten API zu beseitigen. Wer sich für alle Änderungen im Detail interessiert sieht sich am besten die Evolve session von Miguel de Icaza zu dem Thema an.

Die neue Unified API ist in der Assembly Xamarin.iOS.dll enthalten, statt wie bisher in monotouch.dll. In dieser werden die bisherigen numerischen Typen ersetzt, wie dieser Tabelle zu entnehmen ist:

alt neu
System.Int32 (int) System.nint
System.UInt32 (uint) System.nuint
System.Single (float) System.nfloat
System.Drawing.PointF CoreGraphics.CGPoint
System.Drawing.SizeF CoreGraphics.CGSize
System.Drawing.RectangleF CoreGraphics.CGRect

Projekte, die die neue Unified API verwenden, müssen einen neuen Projekttyp verwenden.

Unified API

Innerhalb von Projekten mit dem neuen Projekttypen können keine Projekte, die noch die bisherige (Classic) API verwenden, referenziert werden.

Das Wort “Unified” (vereinheitlicht) in Unified API bezieht sich Xamarins Versuch, gemeinsamen Code für iOS- und Mac-Projekte zu ermöglichen. Aus diesem Grund wurde Auch der Namespace MonoTouch ersatzlos gestrichen. Hierdurch können Teile des Apple-spezifischen Codes zwischen den beiden Plattformen geteilt werden, für die bisher plattformspezifische Implementierungen notwendig waren, z.B. über Shared Projects.

Bestehende Projekte können von Hand oder halbautomatisiert von der Classic API auf die Unified API umgestellt werden. Xamarin hat hierfür eine Anleitung für sowohl die manuellen Schritte als auch für eine Update via Skript oder das in Xamarin Studio eingebaute Updatefeature veröffentlicht.

Ich habe den iOS-Code in MvvmCross auf die neue Unified API umgestellt. Hier sind einige Erkenntnisse aus der Umstellung:

  • Einige Methodennamen wurden verschönert (z.B. UINavigationController.PopViewControllerAnimated() wurde zu UINavigationController.PopViewController(), CGContext.SetRGBFillColor() wurde zu CGContext.SetFillColor(), NetworkReachability.SetCallback() wurde zu NetworkReachability.SetNotification()).
  • Einige Methoden mit Callbacks wurden durch awaitable-Methoden ersetzt.
  • Einige als deprecated markierte Methoden wurden entfernt.
  • Die neuen numerischen Typen sehen aus wie eingebaute Typen, verhalten sich aber nichts so (sie können z.B. nicht const sein, man kann sie nicht mit System.Math.Max() verwenden, etc.).
  • MonoTouch.Foundation.NSAction scheint entfernt worden und durch System.Action ersetzt worden zu sein.

Im Stable-Kanal findet man aktuell eine Vorschau der Unified API. Die finale Version sollte bis Anfang Dezember 2014 in den Alpha- und Beta-Kanälen auftauchen. Jedes bestehende Xamarin.iOS-Projekt wird auf die neue Unified API umgestellt worden sein, wenn es nach dem 1. Februar 2015 an den App Store übermittelt wird. Es ist an der Zeit, sich mit dem Aktualisieren der eigenen Projekte zu befassen.

Update 18. Januar 2015:

  • Die Unified API für iOS ist jetzt im Stable-Kanal verfügbar.
  • In der Zwischenzeit hat Apple die Ankündigung bezüglich der Einreichungsfrist genauer spezifiziert: Die Deadline vom 1. Februar gilt nur für neue Apps. Updates für bestehende Apps können bis einschließlich 31. Mai 2015 noch als reine 32-bit-Anwendungen eingereicht werden.
  • Zwischen der Vorabversion Xamarin.iOS 8.4 und der finalen Version 8.6 gab es leider Änderungen an der API.
  • Xamarin hat die Klasse System.NMath für arithmetische Operationen auf den neuen Datentypen nint, nuint, nfloat zur Verfügung gestellt.
  • Um eine App beim Store einzureichen, muss diese app zusätzlich für die ARM64-Architektur kompiliert worden sein. Dies kann bisher nur in Xamarin Studio oder für Visual-Studio-Anwender direkt in der .csproj-Datei durchgeführt werden.

Kein RT!

2009 hat Twitter eine neue Art eingeführt, um einen Tweet eines anderen mit den eigenen Followern zu teilen (Retweet). Bis dahin sahen alle Retweets so ähnlich aus wie diese:

Der damals eingeführte neue Retweet-Stil erscheint so in der Timeline:

retweet

Beim alten Stil kopiert man einfach nur den Text, fügt RT oder Anführungszeichen und den Twitter-Handle des urprünglichen Autors hinzu. Beim neuen Stil lässt man den ursprünglichen Tweet unangetastet und behält die Metainformation für den Retweet außerhalb des Tweets. Abhängig vom verwendeten Twitter-Client erhält man häufig die Wahl, ob man den alten oder den neuen Retweet-Stil verwenden will.

Wer sich Gedanken macht, ob es noch OK ist, den alten Retweet-Stil zu verwenden, sollte die Vorteile des neuen Stils bedenken:

  1. Der Autor des Ursprungstweets erhält für seinen Tweet eine höhere Wertschätzung. Sein Foto taucht in der Timeline der eigenen Follower auf.
  2. Man kann alle 140 Zeichen retweeten.
  3. Die Follower, die sowohl mir als auch dem Autor des Urspungstweets folgen, sehen den Tweet nur einmal.
  4. Der eigenen Retweet erscheint in der Retweet-Statistik des Urspungstweets. (In den Beispielen oben sehen wir z.B. nur, wie oft der nach altem Retweet-Stil geschriebene Tweet nach neuem Retweet-Stil geteilt wurde.)
  5. Diskussionen zum Ursprungstweet und zum Retweet passieren an der gleichen Stelle.
  6. Der Zeitstempel des Ursprungstweets bleibt erhalten.
  7. Follower, die an meinen Tweets interessiert sind, meine Retweets aber nicht sehen möchten, können Twitter so konfigurieren, dass sie meine Retweets nicht sehen.

Manchmal hat man jedoch das Bedürfnis, im Zuge des Retweets noch einen Kommentar hinzuzufügen. Hier scheint ein RT u.U. in Ordnung, allerdings sollte man auch die Alternative in Erwägung ziehen: Man kann den Urspungstweet referenzieren, indem man einen Link auf den Ursprungstweet seinem Tweet hinzufügt. Einige Twitter-Clients (z.B. der iPhone-Client von Twitter) zeigen den Ursprungstweet dann innerhalb des eigenen Tweets an, wodurch man einige Vorteile der obigen Liste erhält.

quote

Wie man an den Link auf den Urpsungstweet gelangt, ist nicht immer ganz offensichtlich. In Mobil-Apps hält man den Tweet meistens lange gedrückt und wählt dann die Kopierfunktion aus dem Kontextmenü. Auf der Twitter-Webseite versteckt sich der Link hinter dem Datum oder Zeitstempel des Tweets. Durch einen Rechtsklick erscheint die Option, den Link in die Zwischenablage zu kopieren.

tweetlink

Also bitte kein RT! Es gibt bessere Alternativen.

async/await Teil 2: Das await-Schlüsselwort verstehen

In Teil 1 dieser Serie haben wir uns mit dem async-Schlüsselwort auseinandergesetzt. async funktioniert auch ohne await (auch wenn das nicht wirklich sinnvoll ist), aber await funktioniert ohne async überhaupt nicht. Um await innerhalb einer Methode zu verwenden, muss sie als async gekennzeichnet sein.

Das await-Schlüsselwort

Hier ist ein Beispiel einer Methode, die await verwendet:

Bis auf die Schlüsselwörter async und await und den seltsamen Rückgabewert sieht das genauso aus, wie eine synchrone Methode aussehen würde: Eine Webseite herunterladen und im Anschluss überprüfen, ob der angegebene reguläre Ausdruck auf den Inhalt der Webseite anzuwenden ist.

Und genau darum geht ist. Obwohl die Methode asynchron ist, sieht sie aus wie eine synchrone Methode. Nur dass sie im Gegensatz zur synchronen Version nicht den Aufrufer blockiert, bis sie fertig ist, nicht im Mouse-Click-Handler hängenbleibt und am wichtigsten: nicht den Rest der Anwendung einfriert.

Wie funktioniert das?

Beim Aufruf der Methode wird die Methode genauso ausgeführt wie jede andere Methode auch, bis der erste await-Aufruf erreicht wird. An dieser Stelle gibt die Methode dem Aufrufer einen Task zurück (nennen wir ihn den Methoden-Task), der Informationen über den weiteren Verlauf enthält. Sobald der von GetStringAsync() zurückgegebene Task abgeschlossen ist, wird der Rest der Methode ausgeführt. Hierbei sind alle lokalen Variablen wie gewohnt in ihrem vorherigen Zustand und verfügbar, genau wie in nicht-async-Methoden. Wenn die Methode den Rest ihrer Arbei erledigt hat (das Anwenden des regulären Ausdrucks), setzt sie auf magische Weise das Result des Methoden-Tasks auf den Wert, der im return-Ausdruck steht und markiert den Task als IsCompleted.

Üblicherwerise werden async-Methoden von anderen async-Methoden via await aufgerufen. Dadurch muss der Aufrufer der DoesWebContentMatchPatternAsync()-Methode nichts ungewöhnliches machen, um an den Rückgabewert zu gelangen. Es reicht, await vor den Aufruf zu schreiben.

Dies hat einen Kompilierungsfehler zur Folge, wenn die aufrufende Methode nicht ebenfalls async ist.

Wie führe ich await ein?

In der Praxis sieht passiert dies öfter beim Einführen von async/await. Das Schema ist immer das gleiche:

  1. Ein erstes await in die Methode schreiben.
  2. Feststellen, dass die Methode nicht mehr kompiliert, weil sie nicht async ist
  3. Die Methode selbst async machen.
    1. Wenn der Rückgabewert void: Rückgabewert in Task ändern.
    2. Wenn der Rückgabewert irgendein Typ T war: Rückgabewert in Task<T> ändern.
  4. Mit dem Lieblings-Refactoring-Tool das Suffix Async zum Methodennamen hinzufügen.
  5. Ab Schritt 1 für den Aufrufer wiederholen.

Hierdurch breitet sich typischerweise async/await schnell im gesamten Projekt aus, was jedoch positiv zu sehen ist.

Aber was passiert mit Exceptions?

Die Magie von await beinhaltet noch mehr. Ein weiterer aufwändiger Teil asynchroner Programmierung ist das Fangen und behandeln von Fehlern, die im Code auftreten, der im Hintergrund läuft. async/await löst dieses Problem auf elegante Weise durch weiterreichen von Exceptions über die Eigenschaften IsFaulted and Exception in der Task-Klasse. Das bedeutet, dass man Exceptions genauso behandeln kann wie in synchronem Code.

Wird eine Exception nicht gefangen, wird sie, wie bei synchronem Code, an die aufrufende Methode weitergereicht. Solange der Code durchgängig async/await verwendet, muss nicht weiter geändert werden.

Das leidige Thema mit dem UI-Thread

Das letzte Stück Magie nennt sich Kontextsynchronisation. Ein beliebter Stolperstein asynchroner Entwicklung rührt aus der Tatsache, dass die meisten UI-Technologien einen einzelnen UI-Thread besitzen, der alle Änderungen am UI durchführen muss. Ändert man ein UI-Element aus einem anderen Thread, führt dies typischerweise zu einer Exception. Hierzu ein weiteres Beispiel, das den obigen Code verwendet:

Dieser Code zeigt einen Event-Handler eines Button-Click-Events im Code-Behind unter WPF (also dort, wo man solche Funktionalität typischerweise nicht umsetzen sollte). Die Zeile, die resultBox.Text einen neuen Wert zuweist, ist eine Zeile, die unter WPF aus dem UI-Thread aufgerufen werden muss. Trotzdem steht hier kein Code, der diesen Aufruf an den UI-Kontext delegiert. Das liegt daran, dass das Standardverhalten von async/await so ist, dass es versucht, den Code hinter dem await wieder im den Ursprungskontext auszuführen.

Dieses Standardverhalten wird typischerweise nur in UI-Code benötigt und sollte für alle andern Fälle deaktiviert werden, da es zusätzlichen Aufwand bedeutet und Deadlocks verursachen kann. Es lässt sich durch einen Aufruf der von ConfigureAwait(false) auf den jeweiligen Task deaktivieren:

Hierdurch wird dem Compiler mitgeteilt, dass es OK ist, den Rest der Methode (also alles hinter dem await) im gleichen Kontext auszuführen, in dem auch der asynchrone Code hinter dem Schlüsselwort await ausgeführt wurde.

Einfach ausprobieren!

Probiere es einfach mal async/await aus, wenn Dein Projekt schon .NET 4.5 unterstützt. Es wird Deine Wahrnehmung asynchroner Entwicklung nachhaltig verändern.

async/await Teil 1: Das async-Schlüsselwort verstehen

Mit Abstand mein Lieblingsfeature in .NET 4.5 ist async/await oder, wie Microsoft es nennt, das Task-based Asynchronous Pattern (TAP). Ich wusste nicht wirklich, dass ich das all die Jahre hätte brauchen können, bis ich eine Aufzeichnung von Anders Hejlsbergs Build-Vortrag zu dem Thema sah. Kurz danach fand ich mich in einem stark asynchronen C++-Embedded-Projekt vor, das über ein Jahr dauerte und in dem ich mich nicht wohl fühlte, eine State Machine nach der anderen zu bauen um das inherente Problem aller asynchroner Anwendungen zu lösen: Was tun, wenn eine asynchrone Operation abgeschlossen ist?

Diese Blogserie wendet sich an C#-Entwickler, die sich für async/await interessieren. Ein Verständnis der mit .NET 4.0 eingeführten Task-Klasse ist von Vorteil. Im ersten Teil erkläre ich das async aus async/await.

Was ist async?

Mit dem async-Schlüsselwort kann eine Method oder ein Lambda dekoriert werden.

Hier sollte erwähnt werden, dass async nicht Teil der Signatur der Methode ist, daher kann man beim Implementieren eines Interfaces oder beim Überschreiben einer virtuellen oder abstrakten Methode entscheiden, ob man async verwendet oder nicht.

Rückgabewerte

Eine async-Method darf nur void, Task oder Task<T> für einen konkreten Typen T zurückgeben.

void sollte als Rückgabewert soweit wie möglich vermieden werden und wird fast ausschließlich in Eventhandlern gebraucht, wodurch die Methode eine Fire-and-Forget-Methode wird, bei der der Aufrufer keine Möglichkeit hat, zu erkennen, wann die Methode fertig oder fehlgeschlagen ist.

Bei einer Methode, die Task oder Task<T> zurückgibt, sollte man der Konvention nach das Suffix Async verwenden, um hervorzuheben, dass die Methode awaitable ist (unabhängig davon, ob die Implementierung async verwendet oder nicht.

Task als Rückgabewert sollte verwendet werden für Methoden, die, wenn sie synchron wären, void zurückgeben würden. Task<T> sollte verwendet werden für Methoden, die sonst einen Typen T zurückgeben würden (d.h. alles außer void). Den Task kann man sich als das Objekt vorstellen, das der Aufrufer verwenden kann, um mitzubekommen, was denn aus der asynchronen Methode geworden ist, die er angestoßen hat.

Welche Auswirkung hat async?

Durch das Schreiben von async passieren zwei Dinge mit der Methode oder dem Lambdaausdruck:

  1. Es erlaubt die Verwendung von await innerhalb der Methode (siehe meinen nächsten Blogbeitrag in dieser Serie).
  2. Wenn der Rückgabewert nicht void ist, übersetzt der Kompiler auf magische Weise die return-Anweisung (oder die fehlende return-Anweisung am Ende der Methode) in einen Task<T> oder Task.

Für eine Methode, die keine await-Aufrufe beinhaltet, bedeutet das, dass eine abgeschlossener Task zurückgegeben wird, ohne dass dies explizit angegeben werden muss. Für Das Beispiel oben heißt das, dass es sich genauso verhält, wie diese nicht-async-Version:

Eine Methode, die ein await durchläuft, gibt ein Task-Objekt zurück, dessen Zustand auf IsCompleted wechselt, sobald der letzte Aufruf, auf den await aufgerufen wurde, abgeschlossen ist und der darauf folgende synchrone Code (falls vorhanden) anschließend ebenfalls abgeschlossen ist. (Mehr hierzu in meinem nächsten Blogbeitrag in dieser Serie zum await-Schlüsselwort.)

Brauche ich async?

Methoden, die nur ein await als allerletzte Anweisung beinhalten, können grundsätzlich auch ohne das async-Schlüsselwort implementiert werden. Die Methode

ist z.B. äquivalent zu

Obwohl diese Methoden das gleiche Ergebnis liefern, wirkt die async-Version besser lesbarer, auch wenn sie leicht langsamer ist. Der andere Unterschied an dieser Stelle ist, dass, sollte die Methode stream.FlushAsync() eine Exception werfen, die Methode FlushTheStreamAsync() nicht im Call Stack der Exception auftaucht (mehr hierzu im nächsten Blogbeitrag).

Wie hilft mir das weiter?

Wie bereits erwähnt, kann das zurückgegebene Task-Objekt verwendet werden, um den Zustand des asynchronen Aufrufs zu analysieren (Läuft er noch? Ist er fertig? Ist er fehlgeschlagen? Wurde er abgebrochen?). Auch wenn man diese Untersuchungen über die diversen Methoden und Eigenschaften der Task-Klasse möglich ist, ist es meistens deutlich einfacher, hierzu das await-Schlüsselwort zu verwenden, das im nächsten Blogbeitrag erläutert wird.

DWX14-Nachtrag: Einführung in Xamarin und MvvmCross


Bei der diesjährigen Developer Week in Nürnberg durfte ich einen Vortrag zu Cross-Platform-Entwicklung mit Xamarin und MvvmCross halten. Das hat mir großen Spaß mit Live-Coding und den Fragen aus dem Publikum.

Die Folien zum Vortrag sind auf Slidshare auf deutsch und englisch verfügbar. Der Code aus dem Live-Coding, den ich mit Hilfe von Ninja Coder erstellt habe, ist auf Github verfügbar.

Eine App-interne Statusleiste für Xamarin.iOS und MvvmCross

Für eine iPad-App für unseren Kunden Smart Enterprise Solutions brauchten wir die Möglichkeit, dem Anwender mehrere Nachrichten anzuzeigen. Diese Nachrichten waren hauptsächlich Rückmeldungen der Business-Logik-Schicht, die mit dem Backend kommuniziert. Die Nachrichten sollten innerhalb der App sichtbar sein. The messages should be visible inside the app. Einige der Nachrichten sollten vom Anwender bestätigt werden müssen während andere nach einer bestimmten Zeit wieder verschwinden sollten.

Die finale Statusleiste sieht so aus:

StatusBar

Der Code hierzu ist unter https://github.com/lothrop/StatusBar.iOS frei zugänglich.

Die Statusleiste heißt StatusView. Um die StatusView zu verwenden, sollte man sie mit Hilfe von Auto Layouts an den linken, rechten und unteren Rand andocken. StatusView wird durch das ViewModel MessageViewModel mit Daten befüllt. MessageViewModel beinhaltet eine ObservableCollection<IMessageItem>. Man kann MessageItems an jeder der Stelle der Collection hinzufügen oder entfernen oder auch innerhalb der Collection verschieben.

Ein Anwendungsbeispiel steht in der Datei MainViewController.cs.

Vielen Dank an Smart Enterprise Solutions, die mir erlaubt haben, diese Code zu veröffentlichen.

DWX13-Nachtrag: Sprünge mit Kinect und Reactive Extensions erkennen

Bei der Developer Week 2013 habe ich einen Vortrag über Reactive Extensions gehalten. Die Folien sind auf Slideshare zu finden. Im Laufe des Vortrags habe ich einen Teil eines konsolenbasierten Jump-and-Run-Spiels live kodiert, das eine Kinect als Eingabemedium verwendet. Die erste Aufgabe war es, zu erkennen, wenn ein Spieler (der Freiwillige auf der Bühne) in die Luft springt. Hier ist der Code aus der Demo:

Dieser Code beinhaltet viele Vereinfachungen zu Demonstrationszwecken, kann aber trotzdem gut verwendet werden, um einige Konzepte, die hinter Reactive Extensions stecken, zu erklären. Ich werde die einzelnen Teile erklären und die var durch die spezifischen Typen ersetzen:

Diese Zeile greift auf die eine Kinect zu, die an den Rechner angeschlossen ist (und wirft eine Exception, wenn nicht genau eine Kinect angeschlossen ist).

Diese Anweisung erzeugt aus einem klassischen .NET-Event SkeletonFrameReady ein IObservable, indem es Rx beschreibt, wie man sich von dem Event an- und abmeldet.

Interessanterweise beinhaltet die Klasse SkeletonFrameReadyEventArgs überhaupt keine Eigenschaften sondern nur eine Methode public SkeletonFrame OpenSkeletonFrame();, über die man an die Skelettdaten gelangt. Die Instanz von SkeletonFrame muss daraufhin innerhalb von 1/30 Sekunde wieder über Dispose() zerstört werden.

Jetzt haben wir ein IObservable, das eine Liste an Skeletten von sich gibt, sobald sich Personen vor dem Kinect-Sensor befinden.

Dieser Code extrahiert die Gelenke aus dem einen Skelett, wenn es den Zustand SkeletonTrackingState.Tracked hat.

Hierdurch wird die durschnittliche vertikale Position des linken und rechten Fußes ermittelt. Dies ist eine Vereinfachung, da es auch möglich wäre, den Algorithmus zu überlisten, indem man einen Fuß doppelt so hoch hebt als man eigentlich mit zwei Füßen hätte springen sollen. Als Alternative könnte man im Select() beide Füße extrahieren.

Hier wird der eigentliche Sprung detektiert. Das Idee ist die folgende: Um gesprungen zu sein, müsste ein Spieler beide Füße in einer kurzen Zeitspanne erst tief, dann hoch und dann wieder tief haben. Um das zu ermitteln, analysieren wir eine Zeitspanne von einer Sekunde. Nach dieser Analyse bewegen wir und im Zeitstrahl 200 Millisekunden weiter und analysieren wieder. Diese Magie liefert und die Methode Buffer(), die eine Extension Method von IObservable ist. Innerhalb dieser Sekunde ermitteln wir den Maximalwert und bilden die Differenz zum ersten und letzten Wert des Zeitfensters. Zur Vereinfachung wird die Differenz mit einem hartkodierten Wert verglichen. Wenn der Algorithmus zuschlägt, enthält das resultierende IObservable einen Strom an entweder “jumped” oder “didn’t jump”.

Wenn man an dieser Stelle ein jumped.Subscribe(Console.WriteLine); einfügt, sieht man eine lange Folge von “didn’t jump” unterbrochen durch ein paar Vorkommnisse von “didn’t jump” an den Stellen, an denen der Spieler in die Luft gesprungen ist.

Durch den Aufruf von DistinctUntilChanged() gibt das IObservable nur einen Wert aus, wenn er sich vom Vorgängerwert unterscheidet. Die nächste Zeile filter dieses noch auf den Wert “jumped”, d.h. es wird immer einmal “jumped” ausgegeben, wenn der Spieler in die Luft springt.

Diese Zeile gibt das resultierende IObservable auf der Konsole aus.

Hierdurch wird der Kinect-Sensor gestartet.

Die Präsentation hat viel Spaß gemacht. Vielen Dank an den Freiwilligen! Dieses Jahr werde ich wieder auf der Developer Week sprechen, mit einem Vortrag über Cross-Plattform Mobile mit C#.

Animationen mit UIView.AnimateAsync()

Xamarin.iOS 6.4 brachte async/await-Unterstützung für iOS-Geräte. Neben der Unterstützung des Idioms im Mono-3.0-Compiler hat Xamarin auch viel Arbeit investiert, um awaitable-Versionen aller lang andauernden Methoden in Apples iOS API zu erstellen. Eine meiner liebsten ist

UIView.AnimateAsync()

die eine Action oder Lambda zum Animieren bekommt, ohne den UI-Thread zu blockieren.

Richtig Spaß macht das ganze, wenn man die Animationen verkettet. Hier ist ein Beispiel einer horizontalen Schüttel-Animation, die z.B. verwendet werden könnte, um eine ungültige Eingabe zu signalisieren: