Push-Notifications gehören zu den wichtigsten Engagement-Mechanismen moderner Mobile Apps – und gleichzeitig zu den fehleranfälligsten Features bei der Implementierung. Ehrlich gesagt: Ich habe in den letzten zwei Jahren mehr Stunden mit "warum kommt die Notification nicht an?"-Debugging verbracht, als mir lieb ist. Wer von Xamarin.Forms auf .NET MAUI 10 migriert oder eine neue App startet, stößt schnell auf veraltete Tutorials, abgelaufene Plugins und plattformspezifische Stolperfallen.
Dieser Praxisleitfaden zeigt, wie du 2026 zuverlässige Push-Notifications in .NET MAUI 10 mit Firebase Cloud Messaging (FCM) für Android und Apple Push Notification Service (APNs) für iOS umsetzt – inklusive Token-Verwaltung, Berechtigungs-Flow, Backend-Anbindung und Troubleshooting. Also, los geht's.
Warum Push-Notifications in .NET MAUI 10 anders funktionieren
Mit .NET MAUI 10 (Release November 2025) hat sich das Notification-Setup deutlich vereinfacht. Trotzdem gibt's ein paar wichtige Änderungen, die man kennen muss, sonst läuft man direkt in die nächste Wand:
- FCM HTTP v1 API ist Pflicht: Die alte Legacy-API wurde von Google im Juni 2024 endgültig abgeschaltet. Backends müssen OAuth 2.0 mit Service Account verwenden – kein Weg drumherum.
- Android 13+ braucht Runtime-Permissions:
POST_NOTIFICATIONSmuss explizit zur Laufzeit angefragt werden. Wer das vergisst, wundert sich, warum auf neueren Geräten plötzlich nichts mehr ankommt. - iOS Provisioning Profile mit Push Capability: Ohne korrekt aktivierte Capability bekommst du nur stille Fehlermeldungen wie "no valid aps-environment entitlement" – ein Klassiker.
- .NET 10 Trimming: Reflection-basierte JSON-Deserialisierung im Notification-Payload kann beim AOT-Compile einfach weggeschnitten werden.
Architektur-Überblick: Wie Push-Notifications technisch ablaufen
Bevor wir Code schreiben, lohnt sich ein klarer Blick auf den End-to-End-Flow. Push-Notifications sind nämlich kein direkter Server-zu-Gerät-Kanal (auch wenn das viele am Anfang denken), sondern laufen immer über die Provider-Cloud:
- Die App registriert sich beim Plattform-Provider (FCM oder APNs) und erhält einen Device Token.
- Die App sendet diesen Token an dein Backend (z. B. ASP.NET Core Web API).
- Das Backend speichert den Token mit User-Bezug und Plattform.
- Beim Senden erstellt das Backend eine Notification-Payload und schickt sie an FCM bzw. APNs.
- FCM/APNs liefern die Nachricht über persistente Verbindungen ans Gerät aus.
- Die App reagiert – im Vorder- oder Hintergrund – auf die Nachricht.
Wichtig zu wissen: iOS-Geräte können seit iOS 13 auch über FCM erreicht werden, aber Firebase leitet die Nachricht intern an APNs weiter. Du kommst also nicht um den Apple-Push-Service herum, selbst wenn du nur ein FCM-SDK einbindest. Das ist eine der Sachen, die ich anfangs nicht wahrhaben wollte – bringt aber nichts, ist halt so.
Schritt 1: Firebase-Projekt einrichten
Erstelle unter console.firebase.google.com ein neues Projekt. Achte darauf, das richtige Google-Cloud-Projekt zuzuordnen, falls du bereits eines hast (z. B. für BigQuery- oder Auth-Integration). Klingt banal, aber ich habe schon mal versehentlich ein zweites Projekt angelegt und mich dann gewundert, warum mein Auth-Token nirgendwo hinpasste.
Android-App in Firebase hinzufügen
- Klicke auf "Android-App hinzufügen".
- Gib den exakten Package Name aus deiner
Platforms/Android/AndroidManifest.xmlein (z. B.com.mobiletechlead.demo). - Lade die
google-services.jsonherunter und leg sie unterPlatforms/Android/google-services.jsonab. - Setz die Build Action der Datei auf
GoogleServicesJson:
<ItemGroup Condition="$(TargetFramework.Contains('-android'))">
<GoogleServicesJson Include="Platforms\Android\google-services.json" />
</ItemGroup>
iOS-App in Firebase hinzufügen
- Wähl "iOS-App hinzufügen" und trag den Bundle Identifier aus
Info.plistein. - Lade
GoogleService-Info.plistherunter und leg sie unterPlatforms/iOS/GoogleService-Info.plistab. - Setz die Build Action auf
BundleResource.
APNs Authentication Key in Firebase hochladen
Damit Firebase Nachrichten an iOS-Geräte über APNs ausliefern kann, brauchst du einen APNs-Schlüssel. Erstelle unter developer.apple.com → Keys einen neuen Key mit der "Apple Push Notifications service (APNs)"-Capability. Notiere dir Key ID und Team ID, lade die .p8-Datei herunter (die kannst du übrigens nur einmal downloaden – also gut sichern!) und trag alles in den Firebase-Projekteinstellungen unter "Cloud Messaging → Apple App Configuration" ein.
Schritt 2: NuGet-Pakete und MauiProgram-Konfiguration
2026 ist Plugin.Firebase.CloudMessaging die stabilste Wahl für plattformübergreifende FCM-Integration in .NET MAUI 10. Alternativ funktioniert auch Shiny.Push mit eigenem Provider-Modell – meine persönliche Empfehlung ist aber das Plugin.Firebase-Ökosystem, einfach weil die Doku konsistenter ist.
dotnet add package Plugin.Firebase.CloudMessaging --version 3.1.0
dotnet add package Plugin.Firebase.Core --version 3.1.0
In MauiProgram.cs registrierst du das Plugin in der Builder-Pipeline:
using Plugin.Firebase.CloudMessaging;
using Plugin.Firebase.Core;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.RegisterFirebaseServices()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
builder.Services.AddSingleton<IPushNotificationService, PushNotificationService>();
return builder.Build();
}
private static MauiAppBuilder RegisterFirebaseServices(this MauiAppBuilder builder)
{
builder.ConfigureLifecycleEvents(events =>
{
#if IOS
events.AddiOS(ios => ios.FinishedLaunching((app, launchOptions) =>
{
CrossFirebase.Initialize();
FirebaseCloudMessagingImplementation.Initialize();
return false;
}));
#elif ANDROID
events.AddAndroid(android => android.OnCreate((activity, _) =>
CrossFirebase.Initialize(activity)));
#endif
});
return builder;
}
}
Schritt 3: Plattformspezifische Konfiguration
Android: AndroidManifest.xml und Notification-Channel
Füge in Platforms/Android/AndroidManifest.xml die nötigen Permissions ein:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
</manifest>
Ab Android 8.0 (API 26) musst du einen Notification-Channel erzeugen. Sonst zeigt das System gar nichts an – komplett kommentarlos. Leg das in MainApplication.cs an:
[Application]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership) { }
public override void OnCreate()
{
base.OnCreate();
CreateNotificationChannel();
}
private void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O) return;
var channel = new NotificationChannel(
"default_channel",
"Allgemeine Benachrichtigungen",
NotificationImportance.High)
{
Description = "Standardkanal für App-Benachrichtigungen"
};
var manager = (NotificationManager)GetSystemService(NotificationService)!;
manager.CreateNotificationChannel(channel);
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
iOS: Info.plist, Entitlements und AppDelegate
In Platforms/iOS/Info.plist aktivierst du Background-Modes für Remote-Notifications:
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
<string>fetch</string>
</array>
Erstelle eine Entitlements.plist mit der APNs-Capability:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>
Im .csproj verweist du auf die Entitlements:
<PropertyGroup Condition="$(TargetFramework.Contains('-ios'))">
<CodesignEntitlements>Platforms/iOS/Entitlements.plist</CodesignEntitlements>
</PropertyGroup>
Für Release-Builds wird aps-environment automatisch auf production gesetzt – sofern du das richtige Provisioning Profile verwendest.
Schritt 4: Berechtigungen zur Laufzeit anfragen
Auf iOS und Android 13+ ist eine explizite User-Zustimmung Pflicht. Implementier einen plattformübergreifenden Service:
public interface IPushNotificationService
{
Task<bool> RequestPermissionAsync();
Task<string?> GetTokenAsync();
event EventHandler<PushNotificationReceivedEventArgs>? NotificationReceived;
}
public class PushNotificationService : IPushNotificationService
{
public event EventHandler<PushNotificationReceivedEventArgs>? NotificationReceived;
public async Task<bool> RequestPermissionAsync()
{
var granted = await CrossFirebaseCloudMessaging
.Current
.CheckIfValidAsync();
return granted;
}
public Task<string?> GetTokenAsync() =>
CrossFirebaseCloudMessaging.Current.GetTokenAsync();
}
Den Permission-Dialog rufst du idealerweise nicht beim App-Start auf, sondern an einer Stelle, an der der Nutzer den Mehrwert versteht (z. B. nach Login oder bei Aktivierung eines Feature-Toggles). Das verbessert die Akzeptanzquote laut Branchenstudien um 40–60 %. In einem meiner letzten Projekte sind wir von 23 % Opt-in (Dialog beim Start) auf 71 % gesprungen, einfach weil wir den Prompt erst nach dem ersten "Aha-Moment" der App eingebaut haben. Riesiger Hebel, kostet nichts.
Schritt 5: Token-Verwaltung und Backend-Registrierung
Der Device-Token kann sich jederzeit ändern – etwa bei App-Reinstall, Datenwiederherstellung oder Token-Refresh durchs OS. Reagier immer auf das TokenRefresh-Event:
CrossFirebaseCloudMessaging.Current.TokenChanged += async (sender, args) =>
{
var token = args.Token;
await _backendApi.RegisterDeviceAsync(new DeviceRegistration
{
Token = token,
Platform = DeviceInfo.Platform.ToString(),
UserId = _authService.CurrentUserId,
AppVersion = AppInfo.VersionString
});
};
Im Backend speicherst du Tokens mit User- und Plattform-Bezug. Wichtig: Niemals Tokens ohne User-Kontext speichern – sonst sendest du nach einem Logout Nachrichten an den falschen Empfänger. Ich habe genau diesen Fehler einmal in einer Banking-App gesehen, und das ist exakt der Albtraum, den du nicht haben willst.
Schritt 6: ASP.NET Core Backend mit FCM HTTP v1 API
2026 ist die Legacy-FCM-API tot. Du musst das FirebaseAdmin SDK oder direkt OAuth 2.0 mit einem Service Account verwenden. Erstell einen Service Account in der Firebase-Console (Projekteinstellungen → Dienstkonten → Neuen privaten Schlüssel generieren) und speicher die JSON-Datei sicher (z. B. in Azure Key Vault – bitte nicht ins Repo committen, ja?).
dotnet add package FirebaseAdmin --version 3.0.0
using FirebaseAdmin;
using FirebaseAdmin.Messaging;
using Google.Apis.Auth.OAuth2;
public class FcmPushService
{
private static FirebaseApp? _app;
public FcmPushService(IConfiguration config)
{
if (_app != null) return;
_app = FirebaseApp.Create(new AppOptions
{
Credential = GoogleCredential.FromFile(config["Firebase:ServiceAccountPath"])
});
}
public async Task<string> SendAsync(string token, string title, string body, IDictionary<string, string>? data = null)
{
var message = new Message
{
Token = token,
Notification = new Notification { Title = title, Body = body },
Data = data,
Android = new AndroidConfig
{
Priority = Priority.High,
Notification = new AndroidNotification
{
ChannelId = "default_channel",
Sound = "default"
}
},
Apns = new ApnsConfig
{
Aps = new Aps
{
Sound = "default",
ContentAvailable = true,
MutableContent = true
}
}
};
return await FirebaseMessaging.DefaultInstance.SendAsync(message);
}
}
Für den Versand an mehrere Geräte verwendest du SendEachForMulticastAsync – das ersetzt das in 2024 abgekündigte SendMulticastAsync. Klingt nach einer Kleinigkeit, aber ich habe es schon erlebt, dass ältere Tutorials genau hier hängen bleiben.
Schritt 7: Notifications im Vorder- und Hintergrund verarbeiten
.NET MAUI unterscheidet drei Zustände:
- Foreground: App ist aktiv. Du musst die Notification selbst anzeigen.
- Background: App läuft im Hintergrund. Das System zeigt die Notification automatisch.
- Terminated: App ist beendet. Beim Tap auf die Notification startet die App und du bekommst die Daten als Launch-Argument.
CrossFirebaseCloudMessaging.Current.NotificationReceived += (sender, args) =>
{
if (args.Data.TryGetValue("deepLink", out var route))
{
Shell.Current.Dispatcher.Dispatch(async () =>
await Shell.Current.GoToAsync(route));
}
};
Topic-Subscriptions: Broadcast ohne Token-Liste
Statt einzelne Tokens zu pflegen, kannst du Geräte zu Topics abonnieren – ideal für Newsletter, Sport-Scores oder regionale Benachrichtigungen:
await CrossFirebaseCloudMessaging.Current.SubscribeToTopicAsync("news_de");
// Backend sendet einfach an /topics/news_de
Der Vorteil: Skalierbarkeit. Der Nachteil: Du kannst einen Topic-Send nicht zuverlässig user-individualisieren. Für personalisierte Inhalte bleiben Token-basierte Sends die richtige Wahl. Beide Ansätze schließen sich übrigens nicht aus – ich kombiniere in der Praxis gerne beides.
Testen ohne Backend: FCM-Test-Console und APNs-Tools
Für schnelle Tests reicht die Firebase-Console: Cloud Messaging → "Erste Kampagne erstellen" → Device-Token einfügen. Für lokale APNs-Tests unter iOS empfiehlt sich NWPusher oder die mit Xcode 15+ ausgelieferte Push Notifications-App.
Im iOS-Simulator (ab Xcode 11.4) kannst du eine .apns-Datei per Drag-and-Drop ins Simulator-Fenster ziehen:
{
"Simulator Target Bundle": "com.mobiletechlead.demo",
"aps": {
"alert": {
"title": "Test",
"body": "Hallo aus dem Simulator"
},
"sound": "default"
}
}
Häufige Fehler und Troubleshooting
"FirebaseApp with name [DEFAULT] doesn't exist"
Du hast CrossFirebase.Initialize() nicht oder zu spät aufgerufen. Initialisierung muss in FinishedLaunching (iOS) bzw. OnCreate (Android) passieren – vor dem ersten FCM-Zugriff.
iOS: "no valid 'aps-environment' entitlement string found"
Das Provisioning Profile enthält keine Push-Capability. Aktivier unter developer.apple.com → Identifiers die Capability für deine App-ID, regenerier das Provisioning Profile und lade es neu in Visual Studio. Ja, das nervt jedes Mal aufs Neue.
Android: Notifications kommen nur, wenn die App offen ist
Wahrscheinlich blockiert die Hersteller-spezifische Battery-Optimization deine App. Bitt den Nutzer einmalig, die Optimierung zu deaktivieren – besonders wichtig auf Xiaomi, Huawei und OnePlus. (Spoiler: Das ist die Nummer 1 in unserem Support-Postfach.)
iOS: Notification kommt im Sandbox, aber nicht in Production
Sandbox- und Production-Tokens sind unterschiedliche Tokens. Stell sicher, dass dein Backend zwischen Debug- und Release-Builds unterscheidet und an den richtigen APNs-Endpoint sendet.
Trimming entfernt Notification-Payload-Klassen
Bei NativeAOT/Trimming kann der Linker JSON-Klassen entfernen. Nutz Source-Generated JsonSerializerContext oder füg ein TrimmerRoots.xml-Descriptor hinzu.
Best Practices für 2026
- Frag Permissions kontextuell, nicht beim ersten Start.
- Speicher Tokens mit Plattform-Tag – iOS und Android haben unterschiedliche Payload-Limits (4 KB FCM, 5 KB APNs für Critical Alerts).
- Implementier Idempotenz: Bei Token-Refresh kann derselbe Token mehrfach gesendet werden. Auf Backend-Seite mit Upsert arbeiten.
- Nutz Notification Channels feingranular – ein "default_channel" reicht selten. Trenn Marketing, Transaktionen und Sicherheit, damit Nutzer einzelne Kategorien stummschalten können.
- Logge Delivery-Failures: FCM markiert ungültige Tokens mit
UNREGISTEREDoderINVALID_ARGUMENT. Räum diese aus deiner DB. - Respektier Quiet Hours: Sende keine Marketing-Notifications zwischen 22 und 8 Uhr lokaler Zeit. APNs hat dafür den
interruption-level-Parameter (passive, active, time-sensitive, critical).
FAQ: Push-Notifications in .NET MAUI 10
Funktionieren Push-Notifications auch ohne Firebase?
Ja. Auf iOS kannst du direkt gegen APNs senden – Firebase ist optional. Auf Android brauchst du zwingend einen Provider: FCM (Standard auf Google-Geräten), Huawei Push Kit (für Huawei-Geräte ohne Google-Services) oder Amazon Device Messaging. Eine plattformübergreifende Alternative ist OneSignal, das beide Provider abstrahiert, aber pro Gerät pricing-relevant ist.
Was ist der Unterschied zwischen Notification- und Data-Messages?
Notification-Messages werden vom System automatisch angezeigt, wenn die App im Hintergrund ist. Data-Messages kommen immer direkt in deinen Code, auch im Hintergrund – du musst die Anzeige selbst übernehmen. Für Deep-Linking, Inhaltsänderungen und Silent-Updates solltest du reine Data-Messages verwenden (auf iOS mit content-available: 1).
Wie hoch ist das Payload-Limit bei FCM und APNs?
FCM erlaubt bis zu 4 KB Payload. APNs erlaubt 4 KB für Standard-Notifications und 5 KB für Voice-over-IP- und Critical-Alerts. Wenn deine Daten größer sind, sendest du nur eine ID und lädst den Inhalt per HTTPS-Call nach. Klingt umständlich, ist aber Standard-Praxis.
Kann ich Push-Notifications im iOS-Simulator testen?
Ja, ab Xcode 11.4 unterstützt der Simulator Remote-Push-Notifications. Du kannst entweder eine .apns-Datei per Drag-and-Drop ins Simulator-Fenster ziehen oder mit xcrun simctl push aus der Kommandozeile senden. Echte APNs-Tokens funktionieren im Simulator allerdings nicht – nur lokale Tests.
Muss ich Push-Tokens in der DSGVO-Datenschutzerklärung erwähnen?
Ja. Push-Tokens gelten in vielen EU-Auslegungen als personenbezogene Daten, da sie ein bestimmtes Gerät identifizieren. Dokumentiere Zweck, Speicherdauer und Drittlandtransfer (Firebase liegt auf US-Servern!). Praktisch: Verzicht auf Marketing-Pushes ohne Opt-In, Token-Löschung bei Account-Deletion und Standard-Vertragsklauseln (SCCs) für die Firebase-Nutzung sind die drei wichtigsten Bausteine.
Fazit: Stabile Push-Notifications brauchen Disziplin auf drei Ebenen
Push-Notifications in .NET MAUI 10 sind 2026 nicht mehr die Frickel-Aufgabe von Xamarin-Zeiten – sofern du auf drei Dinge achtest: korrekte Provider-Konfiguration (FCM mit aktueller HTTP-v1-API, APNs mit gültigem Auth-Key), sauberes Token-Lifecycle-Management (Refresh, Backend-Sync, Cleanup ungültiger Tokens) und kontextuelle UX (Permissions zur richtigen Zeit, sinnvolle Channels, Quiet Hours respektieren).
Mit dem hier gezeigten Setup hast du ein produktionsreifes Fundament für jede Mobile App – egal ob B2C-Marketing-App, Enterprise-Tool oder Finance-Lösung mit DSGVO-Anforderungen. Und ehrlich? Wenn du diese drei Ebenen ernst nimmst, sparst du dir die Hälfte der Support-Tickets, die sonst landen würden. Viel Erfolg beim Implementieren.