OS Native Funktionen in Xamarin.Forms

Xamarin.Forms bietet schon einen gewissen Satz an Cross-Platform implementierte Funktionen. Diese sind bereits im Framework enthalten und können “Out-of-Box” genutzt werden. Dennoch gibt es gewisse Funktionalitäten, welche ohne Eigen-Programmierung nicht erreichbar sind.

Das Xamarin-Framework bietet hier eine wunderbare Möglichkeit, um solche selbst geschriebenen Methoden von der richtigen Plattform “abzuholen”. Das Stichwort lautet DependencyService.

Beispiel

Nehmen wir das klassische Beispiel des File-Zugriffs. iOS und Android verarbeiten den Zugriff auf Files individuell, also müssen wir hier jeweils eine plattformspezifische Variante implementieren, und diese anschließend von außen zugreifbar machen.

Interface erstellen

Wir legen uns zuerst ein Interface mit den Methoden an, welche wir später nutzen möchten. Das Interface wird an einem beliebigen Ort im Cross-Platform-Code erstellt:

public interface IFileHelper
{
  string GetLocalFilePath(string fileName);
  string GetExternalFilePath(string path);
}

Das ist recht trivial. Hier kann rein, was auch immer gewünscht wird.

Interface im Plattform-Projekt implementieren

Jetzt implementieren wir das Ganze in unserer gewünschten Ziel-Plattform. Ich nehme hier Android als Beispiel, da ich die iOS-Seite bisher nicht programmiert habe:

[assembly: Dependency(typeof(FileHelper))]
namespace PSM.Droid
{
  public class FileHelper : IFileHelper
  {
    public string GetLocalFilePath(string filename)
    {
      string path = System.Environment
                      .GetFolderPath(System
                        .Environment
                        .SpecialFolder
                        .Personal
                      );
      return Path.Combine(path, filename);
    }

    public string GetExternalFilePath(string path)
    {
      string extPath = Android.OS
                              .Environment
                              .ExternalStorageDirectory
                              .AbsolutePath;
      return Path.Combine(extPath, path);
    }
  }
}

Wir legen uns also in unserem Android-Projekt eine neue Klasse an und schreiben eine Annotation über dem Namespace [assembly: Dependency(typeof(FileHelper))]. Hiermit weiß anschließend Xamarin, dass unsere Klasse für eine mögliche Dependency Injection zur Verfügung steht. (typeof(FileHelper) ist in diesem Falle die Klasse selbst, in der wir uns gerade befinden).

Nun implementieren wir noch das Interface in unsere Klasse (wir erinnern uns, es hieß IFileHelper), und schon sind wir fertig. Die Methoden müssen dann natürlich ausprogrammiert werden, je nachdem, was wir vor haben (in unserem Beispiel möchten wir den Pfad des externen Speichers zurückgeben).

Funktion im Cross-Platformcode nutzen

Wenn wir das jetzt aufrufen möchten, müssen wir unser Interface zu allererst registrieren. Dies geschieht am besten beim starten der App, beispielsweise in der App.xaml.cs:

DependencyService.Register<IFileHelper>();

Jetzt können wir unser Interface ganz einfach überall im Code aufrufen:

string path = DependencyService.Get<IFileHelper>()
              .GetExternalFilePath("/my/custom/path");

Durch das Modul DependencyService sucht sich Xamarin nun abhängig von der aktuellen Plattform die Implementation raus und liefert uns eine Instanz der entsprechenden Klasse zurück. Auf dieser können wir nun unsere Funktion aufrufen.

Fertig!

Troubleshooting

Ich kam dem Problem entgegen, das die Get()-Funktion des DependencyServices manchmal null zurückliefert. Es war nicht zu erkennen, in welchem Falle dies auftrat. Normalerweise schafft Xamarin es selbst, die Implementation zu finden. Sollte dies bei euch ebenfalls vorkommen, bietet sich an, die Implementierung direkt zu registrieren.
Die Register-Methode bietet hier eine Überladung, mit der ihr als ersten Typ das Interface und als zweiten Typ die Implementation übergebt. Somit weiß Xamarin direkt, was es nehmen muss.

DependencyService.Register<IFileHelper, FileHelper>();

Dies muss dann natürlich im Plattform-Spezifischen Projekt erledigt werden, also im Falle von Android in der MainActivity.