App Tracking Transparency Permission in Xamarin.Forms für iOS

Bereits seit längerer Zeit hat sich Apple die Privatsphäre der Daten der Nutzer auf die Fahnen geschrieben. Daher ist es kein Wunder, dass man als App-Entwickler den Nutzer nun darauf hinweisen muss, dass die eigene App Nutzerdaten erfasst. Damit jedoch noch nicht genug, denn der Nutzer hat auch die Option dieses abzulehnen, so dass die App dann keinerlei nutzerspezifischen Daten erfassen darf. Dafür hat Apple nun das AppTrackingTransparency-Framework veröffentlicht, mit dessen Hilfe man dieses Szenario umsetzen kann. Luis Matos hat bereits einen Artikel in seinem Blog, sowohl auf Englisch als auch Spanisch veröffentlicht, welchen ich als Grundlage für die Umsetzung nutzen werden.

Ausgangslage bildet eine bestehende oder entsprechend leere Xamarin.Forms App. Ich habe alle NuGet-Pakete aktualisiert. Wir müssen die Datei Info.plist im iOS-Projekt anpassen. Hierzu klicken wir mit der rechten Maustaste auf die Datei und wählen aus dem Kontextmenü den Eintrag View Code aus. Oberhalb des schließenden dict-Eintrags fügen wir nun den NSUserTrackingUsageDescription-Key mit einer passenden Beschreibung hinzu.

<key>NSUserTrackingUsageDescription</key>
<string>Add some description, why you need to track data.</string>

Ihr solltet hier eine aussagekräftige Nachricht schreiben, denn diese wird später dem Nutzer angezeigt.

Im nächsten Schritt habe ich einen Ordner Interfaces im plattform-unabhängigen Projekt erstellt und darin ein Interface mit den Namen IAppTrackingTransparencyService. Da es sich technisch um eine Permission handelt und das Permission-Handling bereits in Xamarin.Essentials umgesetzt wurde und Xamarin.Essentials auch Teil eines jeden neuen Xamarin.Forms Projekts ist, nutzen wir die Klasse PermissionStatus aus diesem Package. Das Interface stellt insgesamt zwei Methoden zur Verfügung. Zum einen wird der aktuelle Status abgefragt und zum anderen gibt es eine Methode im die entsprechende Permission zu requesten.

public interface IAppTrackingTransparencyService
{
    Task<PermissionStatus> CheckStatusAsync();

    void Request(Action<PermissionStatus> completionAction);
}

Wir benötigen nun eine passende Implementierung. Dafür habe ich einen Ordner Services im iOS-Projekt angelegt und darin die Klasse AppTrackingTransparencyService. Diese Klasse implementiert neben IAppTrackingTransparencyService auch BasePlatformPermission aus dem Xamarin.Essentials Package. Außerdem verfügt die Klasse über eine Methode ConvertStatus, welche den internen iOS-Status in einen Xamarin.Essentials-Status konvertiert.

public class AppTrackingTransparencyService : BasePlatformPermission, 
        IAppTrackingTransparencyService
{
    protected override Func<IEnumerable<string>> RequiredInfoPlistKeys 
        => () => new string[] { "NSUserTrackingUsageDescription" };

    public void Request(Action<PermissionStatus> completionAction)
    {
        ATTrackingManager.RequestTrackingAuthorization(
            (result) => completionAction(ConvertStatus(result)));
    }

    public override Task<PermissionStatus> CheckStatusAsync()
    {
        return Task.FromResult(
            ConvertStatus(ATTrackingManager.TrackingAuthorizationStatus));
    }

    private PermissionStatus ConvertStatus(
        ATTrackingManagerAuthorizationStatus status)
    {
        switch (status)
        {
            case ATTrackingManagerAuthorizationStatus.NotDetermined:
                return PermissionStatus.Disabled;
            case ATTrackingManagerAuthorizationStatus.Restricted:
                return PermissionStatus.Restricted;
            case ATTrackingManagerAuthorizationStatus.Denied:
                return PermissionStatus.Denied;
            case ATTrackingManagerAuthorizationStatus.Authorized:
                return PermissionStatus.Granted;
            default:
                return PermissionStatus.Unknown;
        }
    }
}

Nun öffnen wir die Datei AppDelegate.cs und registrieren über den Xamarin.Forms DependencyService unsere Implementierung in der Methode FinishedLaunching.

public override bool FinishedLaunching(UIApplication app, 
    NSDictionary options)
{
    global::Xamarin.Forms.Forms.Init();

    DependencyService.Register<IAppTrackingTransparencyService, 
        AppTrackingTransparencyService>();

    LoadApplication(new App());

    return base.FinishedLaunching(app, options);
}

Nun können wir die App.xaml.cs öffnen und die OnStart-Methode anpassen. Hier überprüfen wir zunächst, ob wir unter iOS laufen. Wenn dies der Fall ist, holen wir uns über den DependencyService die Instanz von unserem AppTrackingTransparencyService. Anschließend überprüfen wir den Status und übergeben das ganze an eine neue Methode, welche ich HandleTracking genannt habe. Hier könnt ihr jetzt euer Tracking-Framework, wie zum Beispiel AppCenter initialisieren, sofern der Nutzer dem Tracking zugestimmt hat.

protected override async void OnStart()
{
    if (DeviceInfo.Platform == DevicePlatform.iOS)
    {
        var appTrackingTransparencyService = DependencyService.
                   Get<IAppTrackingTransparencyService>();
        var status = await 
                   appTrackingTransparencyService.CheckStatusAsync();

        if (status != PermissionStatus.Granted)
            appTrackingTransparencyService.
                        Request(s => HandleTracking(s));
        else
            HandleTracking(status);
    }
}

private void HandleTracking(PermissionStatus status)
{
    if (status != PermissionStatus.Granted)
        return;

    // enable tracking
}

Wenn wir die App nun einmal unter iOS ausführen, so wird uns beim ersten Starten der App nun der folgende Dialog angezeigt.

Der Nutzer hat nun die Möglichkeit die Anfrage zu bestätigen oder entsprechend abzulehnen. Diese Informationen können wir dann in der App nutzen, um das Erfassen der Daten zu starten bzw. zu beenden.

Damit können wir jetzt ganz einfach die gewünschten Anforderungen von Apple umsetzen und den Nutzer nach einer Erlaubnis zum Erfassen der Daten fragen. Ich habe euch das Projekt noch einmal auf GitHub abgelegt.

Animation in Xamarin.Forms-Apps DataTemplateSelector: Verschiedene DataTemplates für eine Liste Apple AirTags – Nie wieder etwas kostbares verlieren