Hvorfor push notifikationer stadig er afgørende for mobile apps i 2026
Push notifikationer er en af de mest kraftfulde mekanismer til at fastholde brugere i din mobilapp. Det er ikke bare en påstand — undersøgelser viser konsekvent, at apps med velimplementerede push notifikationer har markant højere engagement-rater og lavere churn. Men her er sagen: implementeringen i .NET MAUI er ikke triviel.
Den kræver koordination mellem Firebase Cloud Messaging (FCM), Apple Push Notification Service (APNs), platformspecifik konfiguration og en server-backend. Det lyder som meget — og det er det faktisk også.
Denne guide tager dig fra nul til en fuldt fungerende push notifikation-løsning i .NET MAUI med .NET 10. Vi dækker både Android og iOS, inkluderer arbejdende kodeeksempler og adresserer de fejl og faldgruber, som de fleste tutorials bekvemt overser. Så lad os komme i gang.
Arkitekturen bag push notifikationer
Før vi skriver en eneste linje kode, er det vigtigt at forstå flowet. Push notifikationer involverer tre aktører:
- Din app (klienten) — registrerer sig hos platformens notifikationstjeneste og modtager et unikt device token.
- Din server (backend) — gemmer device tokens og sender notifikationer via FCM eller APNs.
- Platformens notifikationstjeneste (FCM/APNs) — modtager beskeder fra din server og leverer dem til den korrekte enhed.
Simpelt nok i teorien. I praksis er der en del bevægelige dele.
I .NET MAUI har du tre primære tilgange:
- Direkte Firebase-integration med Plugin.FirebasePushNotifications — simpelt, open-source, og velegnet til de fleste projekter.
- Azure Notification Hubs — Microsofts officielle løsning med skalering, segmentering og multi-platform support.
- Tredjepartstjenester som OneSignal — SaaS-løsning med dashboard og analytics (kan være fristende, men du mister kontrol).
I denne guide fokuserer vi primært på Firebase-tilgangen med Plugin.FirebasePushNotifications. Den giver den bedste balance mellem enkelhed og kontrol for de fleste .NET MAUI-projekter, og det er den løsning jeg har haft bedst erfaring med i praksis.
Trin 1: Opret Firebase-projekt og konfigurer platforme
Firebase Console-opsætning
Start med at oprette et nyt projekt i Firebase Console. Giv det et passende navn og vælg om du vil aktivere Google Analytics (anbefales til produktionsapps). Når projektet er oprettet, skal du tilføje både en Android-app og en iOS-app.
For Android:
- Klik "Add app" og vælg Android.
- Indtast dit package name — dette skal matche
ApplicationIdi din.csproj-fil (f.eks.com.ditfirma.dinapp). Får du det forkert her, virker intet senere. Dobbeltcheck det. - Download
google-services.jsonog placer den iPlatforms/Android/i dit MAUI-projekt.
For iOS:
- Klik "Add app" og vælg iOS.
- Indtast dit Bundle ID — det skal også matche
ApplicationId. - Download
GoogleService-Info.plistog placer den iPlatforms/iOS/.
Konfigurer Firebase-filerne i .csproj
Tilføj følgende til din .csproj-fil for at sikre korrekt build action:
<ItemGroup Condition="$(TargetFramework.Contains('-android'))">
<GoogleServicesJson Include="Platforms\Android\google-services.json" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.Contains('-ios'))">
<BundleResource Include="Platforms\iOS\GoogleService-Info.plist" />
</ItemGroup>
Glemmer du dette trin, får du kryptiske build-fejl. Det har jeg selv brugt tid på at debugge mere end én gang.
Trin 2: iOS-specifik opsætning — Entitlements og APNs
iOS kræver mere opsætning end Android. Ærligt talt, det er den del af processen der tager mest tid — ikke fordi det er svært, men fordi der er mange små skridt der skal passe sammen.
Apple Developer Portal
- Log ind på Apple Developer Portal og naviger til "Certificates, Identifiers & Profiles".
- Under "Identifiers" — find eller opret dit App ID med samme Bundle ID som dit MAUI-projekt.
- Aktiver Push Notifications-capability for dette App ID.
- Under "Keys" — opret en ny APNs-nøgle. Markér "Apple Push Notifications service (APNs)" og download nøglen. Gem Key ID — du skal bruge den i Firebase Console.
Upload APNs-nøgle til Firebase
I Firebase Console, naviger til dit iOS-app under "Project settings" → "Cloud Messaging". Upload din APNs Authentication Key og indtast Key ID samt dit Team ID (findes i Apple Developer Portal under Membership).
Tilføj Entitlements.plist
Opret en fil kaldet Entitlements.plist i Platforms/iOS/ mappen:
<?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>
Vigtigt: Skift værdien til production når du bygger til App Store. For udvikling og TestFlight skal den stå på development. Det her er en klassisk fejlkilde.
Opdater Info.plist
Tilføj remote notification-understøttelse til din Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
Link Entitlements i Visual Studio
I Visual Studio: højreklik på projektet → Properties → iOS → Bundle Signing. Under "Custom Entitlements" peger du på din Platforms/iOS/Entitlements.plist-fil. Alternativt kan du tilføje det direkte i .csproj:
<PropertyGroup Condition="$(TargetFramework.Contains('-ios'))">
<CodesignEntitlements>Platforms\iOS\Entitlements.plist</CodesignEntitlements>
</PropertyGroup>
Trin 3: Installer og konfigurer Plugin.FirebasePushNotifications
Plugin.FirebasePushNotifications er det mest populære community-bibliotek til Firebase push notifikationer i .NET MAUI. Den seneste version (3.2.x) understøtter .NET 10 og forenkler opsætningen markant sammenlignet med tidligere versioner.
Installation via NuGet
dotnet add package Plugin.FirebasePushNotifications
Bemærk: Pluginet understøtter Android og iOS, men ikke Mac Catalyst eller Windows. Fjern disse target frameworks fra din .csproj hvis de er inkluderet, eller brug conditional compilation. Det her fanger folk tit, fordi MAUI-templates som standard inkluderer alle platforme.
Konfigurer MauiProgram.cs
Registrer pluginet i din MauiProgram.cs med extension-metoden UseFirebasePushNotifications:
using Plugin.FirebasePushNotifications;
namespace MinApp;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseFirebasePushNotifications(options =>
{
options.AutoRegistration = true;
options.PresentationOptions =
NotificationPresentationOptions.Alert |
NotificationPresentationOptions.Badge |
NotificationPresentationOptions.Sound;
})
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
#endif
// Registrer din egen notifikation-service
builder.Services.AddSingleton<IPushNotificationHandler,
AppPushNotificationHandler>();
return builder.Build();
}
}
Android-specifik konfiguration
For Android 13+ (API 33) kræves eksplicit tilladelse til notifikationer. Tilføj dette til din AndroidManifest.xml:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Hvis dit projekt har target API 30 eller højere, skal du også tilføje package visibility-queries:
<queries>
<intent>
<action android:name="androidx.browser.customtabs.CustomTabsService" />
</intent>
</queries>
Trin 4: Implementer notifikationshåndtering
Nu kommer den vigtigste del — at håndtere notifikationer korrekt i alle tre app-tilstande: forgrund, baggrund og lukket. Det er her de fleste implementeringer fejler, så vær ekstra opmærksom.
Opret en PushNotificationHandler
using Plugin.FirebasePushNotifications;
namespace MinApp.Services;
public class AppPushNotificationHandler : IPushNotificationHandler
{
public void OnNotificationReceived(
IDictionary<string, object> data)
{
// Appen er i forgrunden — vis evt. en in-app besked
if (data.TryGetValue("title", out var title) &&
data.TryGetValue("body", out var body))
{
MainThread.BeginInvokeOnMainThread(async () =>
{
await Shell.Current.DisplayAlert(
title?.ToString() ?? "Notifikation",
body?.ToString() ?? "",
"OK");
});
}
}
public void OnNotificationOpened(
IDictionary<string, object> data)
{
// Brugeren trykkede på notifikationen
if (data.TryGetValue("route", out var route))
{
MainThread.BeginInvokeOnMainThread(async () =>
{
await Shell.Current.GoToAsync(
route?.ToString() ?? "//main");
});
}
}
public void OnError(string error)
{
System.Diagnostics.Debug.WriteLine(
$"Push notification fejl: {error}");
}
}
Registrer for notifikationer og hent token
I din App.xaml.cs eller en startside kan du registrere for notifikationer og lytte efter token-opdateringer:
using Plugin.FirebasePushNotifications;
namespace MinApp;
public partial class App : Application
{
public App()
{
InitializeComponent();
}
protected override void OnStart()
{
base.OnStart();
// Lyt efter token-ændringer
CrossFirebasePushNotification.Current.TokenRefreshed += (s, e) =>
{
var token = e.Token;
System.Diagnostics.Debug.WriteLine(
$"FCM Token: {token}");
// Send token til din backend
Task.Run(() => SendTokenToBackend(token));
};
// Registrer for push notifikationer
CrossFirebasePushNotification.Current
.RegisterForPushNotificationsAsync();
}
private async Task SendTokenToBackend(string token)
{
// Implementer API-kald til din backend
using var client = new HttpClient();
var content = new StringContent(
System.Text.Json.JsonSerializer.Serialize(
new { deviceToken = token }),
System.Text.Encoding.UTF8,
"application/json");
await client.PostAsync(
"https://din-api.dk/api/devices/register",
content);
}
}
Trin 5: Android Notification Channels
Siden Android 8.0 (API 26) er notification channels obligatoriske. De giver brugerne mulighed for at tilpasse notifikationsindstillinger pr. kanal — lyd, vibration, vigtighed og så videre. Det er en af de ting der virker som overkill, men faktisk giver god mening for brugeroplevelsen.
Opret notification channels
#if ANDROID
using Android.App;
using Android.OS;
namespace MinApp.Platforms.Android;
public static class NotificationChannelSetup
{
public static void CreateChannels()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
return;
var notificationManager = (NotificationManager)
Platform.CurrentActivity!
.GetSystemService(
global::Android.Content.Context
.NotificationService)!;
// Hovedkanal for generelle notifikationer
var generalChannel = new NotificationChannel(
"general_notifications",
"Generelle notifikationer",
NotificationImportance.Default)
{
Description = "Generelle app-notifikationer"
};
notificationManager.CreateNotificationChannel(
generalChannel);
// Kanal for vigtige opdateringer
var urgentChannel = new NotificationChannel(
"urgent_notifications",
"Vigtige opdateringer",
NotificationImportance.High)
{
Description = "Vigtige opdateringer der kræver opmærksomhed"
};
urgentChannel.EnableVibration(true);
urgentChannel.EnableLights(true);
notificationManager.CreateNotificationChannel(
urgentChannel);
}
}
#endif
Kald NotificationChannelSetup.CreateChannels() tidligt i appens livscyklus — f.eks. i MainActivity.OnCreate eller i MauiProgram.cs med platform-conditional kode.
Trin 6: Håndter tilladelser korrekt
På iOS beder systemet automatisk om tilladelse når du kalder RegisterForPushNotificationsAsync. Det er fint. Men på Android 13+ skal du eksplicit anmode om POST_NOTIFICATIONS-tilladelsen. Her er en cross-platform tilgang der håndterer begge platforme:
public async Task RequestNotificationPermission()
{
var status = await Permissions.CheckStatusAsync
<Permissions.PostNotifications>();
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync
<Permissions.PostNotifications>();
}
if (status == PermissionStatus.Granted)
{
await CrossFirebasePushNotification.Current
.RegisterForPushNotificationsAsync();
}
else
{
// Vis besked til brugeren om vigtigheden
// af notifikationer for din app
await Shell.Current.DisplayAlert(
"Notifikationer deaktiveret",
"Du kan aktivere notifikationer i systemindstillingerne.",
"OK");
}
}
Et godt tip: Spørg ikke om tilladelse ved første appstart. Vent til brugeren har haft mulighed for at se appens værdi, og forklar hvorfor de bør aktivere notifikationer. Det øger accept-raten markant.
Trin 7: Backend — send notifikationer via FCM
For at sende push notifikationer har du brug for en backend-service. Her er et eksempel med en minimal ASP.NET Core Web API og Firebase Admin SDK:
// Program.cs (ASP.NET Core Minimal API)
using FirebaseAdmin;
using FirebaseAdmin.Messaging;
using Google.Apis.Auth.OAuth2;
var builder = WebApplication.CreateBuilder(args);
// Initialiser Firebase Admin SDK
FirebaseApp.Create(new AppOptions
{
Credential = GoogleCredential
.FromFile("firebase-service-account.json")
});
var app = builder.Build();
app.MapPost("/api/notifications/send",
async (NotificationRequest request) =>
{
var message = new Message
{
Token = request.DeviceToken,
Notification = new Notification
{
Title = request.Title,
Body = request.Body
},
Data = request.Data,
Android = new AndroidConfig
{
Priority = Priority.High,
Notification = new AndroidNotification
{
ChannelId = request.IsUrgent
? "urgent_notifications"
: "general_notifications",
ClickAction = "OPEN_ACTIVITY"
}
},
Apns = new ApnsConfig
{
Aps = new Aps
{
Badge = 1,
Sound = "default",
ContentAvailable = true
}
}
};
var response = await FirebaseMessaging
.DefaultInstance.SendAsync(message);
return Results.Ok(new { MessageId = response });
});
app.Run();
public record NotificationRequest(
string DeviceToken,
string Title,
string Body,
Dictionary<string, string>? Data = null,
bool IsUrgent = false);
Fejlfinding og kendte faldgruber
Her er de problemer du næsten garanteret vil støde på — og løsningerne. Jeg har selv været igennem dem alle.
iOS: Notifikationer virker ikke i Debug-tilstand
Firebase iOS CloudMessaging fungerer kun i Release-tilstand. I Debug-tilstand fejler Configure-metoden med en native class-fejl. Det er frustrerende, men sådan er det. Test push notifikationer via TestFlight eller AdHoc-distribution.
Android: Token ændrer sig ved hver debug-session
Debuggeren forårsager, at der genereres et nyt Firebase-token hver gang. Sørg for at din backend opdaterer tokenet ved hver registrering — implementer altid en TokenRefreshed-event handler. Ellers sender du notifikationer ud i det blå.
iOS: App Store-afvisning med Error 409
Der er et kendt problem i .NET MAUI, hvor build-processen merger legacy-entitlements fra provisioning profilen med moderne entitlements fra Entitlements.plist. Resultatet er duplikerede nøgler (aps-environment og com.apple.developer.aps-environment) der forårsager afvisning.
Løsningen er at sikre, at kun Entitlements.plist-værdier bruges. Opdater til den seneste .NET MAUI-version — dette fix er inkluderet i nyere builds.
Android: Notifikation vises ikke når appen er i forgrund
Når appen er i forgrunden, viser Firebase SDK ikke automatisk notifikationen. Det overrasker mange. Du skal selv oprette og vise den via din OnNotificationReceived-handler — brug enten en in-app dialog, en toast-besked eller opret notifikationen manuelt med NotificationCompat.Builder.
Generelt: Token er null efter registrering
Sørg for at vente på TokenRefreshed-eventet i stedet for at forsøge at læse tokenet synkront. Token-generering er asynkron og kan tage et par sekunder, især ved første appstart. Tålmodighed er en dyd her.
Avanceret: Topic-baserede notifikationer
I stedet for at sende notifikationer til individuelle enheder kan du bruge FCM Topics til at sende til grupper af brugere der har tilmeldt sig et bestemt emne. Det er elegant og skalerer rigtig godt:
// Tilmeld brugeren til et topic
await CrossFirebasePushNotification.Current
.SubscribeAsync("nyheder");
// Afmeld et topic
await CrossFirebasePushNotification.Current
.UnsubscribeAsync("nyheder");
// Backend: Send til alle der følger "nyheder"
var topicMessage = new Message
{
Topic = "nyheder",
Notification = new Notification
{
Title = "Ny artikel tilgængelig",
Body = "Læs den seneste artikel om .NET MAUI"
}
};
await FirebaseMessaging.DefaultInstance
.SendAsync(topicMessage);
Topics er ideelle til nyhedsapps, e-commerce (tænk produktkategorier) og sociale platforme med gruppenotifikationer. Og der er ingen grænse for antallet af topics en bruger kan abonnere på.
Test din implementering
En systematisk teststrategi er afgørende for push notifikationer. Lad være med at springe denne del over — push notifikationer har mange edge cases. Test følgende scenarier:
- App i forgrund — Modtager appen data korrekt? Vises din in-app besked?
- App i baggrund — Vises systemnotifikationen? Navigerer appen korrekt ved tryk?
- App lukket — Vises notifikationen? Startes appen korrekt ved tryk?
- Token-rotation — Opdaterer din backend tokenet korrekt ved
TokenRefreshed? - Tilladelser afvist — Håndterer appen det pænt, at brugeren afviser notifikationstilladelse?
Den nemmeste måde at sende testnotifikationer er direkte fra Firebase Console: naviger til "Cloud Messaging" under "Engage"-menuen, klik "Send your first message" og indtast en testtitel og -tekst. Du kan sende til specifikke device tokens eller topics.
Og husk: Test altid på fysiske enheder. iOS-simulatoren understøtter remote notifikationer kun fra iOS 16+ på macOS 13+ med Apple Silicon eller T2-processorer.
Ofte stillede spørgsmål
Hvad er forskellen mellem push notifikationer og lokale notifikationer i .NET MAUI?
Push notifikationer (remote notifications) sendes fra en ekstern server via FCM eller APNs og kræver internetforbindelse. De bruges til at informere brugeren om nye data, beskeder eller opdateringer fra din backend. Lokale notifikationer genereres derimod direkte af appen på enheden uden serverkommunikation — tænk påmindelser, timers eller opgaver. I .NET MAUI håndteres lokale notifikationer typisk via Plugin.LocalNotification, mens push notifikationer bruger Plugin.FirebasePushNotifications eller Azure Notification Hubs.
Kan jeg bruge push notifikationer i .NET MAUI uden Firebase?
Ja, men det kræver mere arbejde. Du kan bruge Azure Notification Hubs som et alternativ — det abstraherer FCM og APNs bag et samlet API. En anden mulighed er OneSignal, der tilbyder en komplet SaaS-løsning med dashboard og analytics. For iOS kan du også kommunikere direkte med APNs via HTTP/2, men det anbefales kun hvis du har helt specifikke krav der udelukker Firebase.
Hvorfor modtager min iOS-app ikke push notifikationer under udvikling?
De tre mest almindelige årsager er: (1) Firebase iOS CloudMessaging virker kun i Release-tilstand — Debug-builds fejler med en native class-fejl, så test via TestFlight eller AdHoc. (2) Din Entitlements.plist er ikke korrekt konfigureret eller linket i projektet. (3) Din provisioning profile har ikke Push Notifications-capability aktiveret, eller APNs-nøglen er ikke uploadet til Firebase Console. Start med punkt 1 — det er den hyppigste årsag.
Understøtter Plugin.FirebasePushNotifications Windows og Mac Catalyst?
Nej. Plugin.FirebasePushNotifications understøtter kun Android og iOS. Hvis du har brug for notifikationer på Windows, kan du bruge Windows Notification Service (WNS) via Azure Notification Hubs. Mac Catalyst-understøttelse er heller ikke tilgængelig. Fjern disse target frameworks fra dit projekt eller brug conditional compilation for at undgå build-fejl.
Hvordan håndterer jeg push notifikationer når brugeren har appen åben?
Når appen er i forgrunden, viser hverken Android eller iOS automatisk en systemnotifikation fra Firebase. Du skal selv håndtere dette i din OnNotificationReceived-callback. Gode mønstre inkluderer: vis en in-app banner eller toast øverst i skærmen, opdater UI direkte med nye data, eller vis en dialog med notifikationens indhold. Men undgå at vise en fuld alert-dialog for hver eneste notifikation — det er en dårlig brugeroplevelse, og dine brugere vil hurtigt slå notifikationer fra.