Självstudie: Skicka push-meddelanden till Flutter-appar med Hjälp av Azure Notification Hubs via en serverdelstjänst
I den här självstudien använder du Azure Notification Hubs för att skicka meddelanden till ett Flutter-program som riktar sig till Android och iOS.
En ASP.NET Core webb-API-serverdel används för att hantera enhetsregistrering för klienten med den senaste och bästa installationsmetoden. Tjänsten skickar även push-meddelanden på ett plattformsoberoende sätt.
De här åtgärderna hanteras med Notification Hubs SDK för serverdelsåtgärder. Mer information om den övergripande metoden finns i dokumentationen om registrering från din appserverdel .
I den här självstudien går vi igenom följande steg:
- Konfigurera Push Notification Services och Azure Notification Hubs.
- Skapa ett ASP.NET Core webb-API-serverdelsprogram.
- Skapa ett plattformsoberoende flutterprogram.
- Konfigurera det interna Android-projektet för push-meddelanden.
- Konfigurera det interna iOS-projektet för push-meddelanden.
- Testa lösningen.
Förutsättningar
Om du vill följa med behöver du:
- En Azure-prenumeration där du kan skapa och hantera resurser.
- Flutter toolkit (tillsammans med dess förutsättningar).
- Visual Studio Code med plugin-programmet Flutter och Dart installerat.
- CocoaPods installerat för att hantera biblioteksberoenden.
- Möjligheten att köra appen på antingen Android-enheter (fysiska enheter eller emulatorenheter) eller iOS (endast fysiska enheter).
För Android måste du ha:
- En utvecklare har låst upp en fysisk enhet eller en emulator (som kör API 26 och senare med Google Play Services installerat).
För iOS måste du ha:
- Ett aktivt Apple Developer-konto.
- En fysisk iOS-enhet som är registrerad på ditt utvecklarkonto(kör iOS 13.0 och senare).
- Ett .p12-utvecklingscertifikat installerat i nyckelringen så att du kan köra en app på en fysisk enhet.
Observera
iOS-simulatorn stöder inte fjärrmeddelanden och därför krävs en fysisk enhet när du utforskar det här exemplet på iOS. Du behöver dock inte köra appen på både Android och iOS för att kunna slutföra den här självstudien.
Du kan följa stegen i det här exemplet med första principer utan tidigare erfarenhet. Du kommer dock att ha nytta av att känna till följande aspekter.
- Apple Developer Portal.
- ASP.NET Core.
- Google Firebase Console.
- Microsoft Azure och Skicka push-meddelanden till iOS-appar med Azure Notification Hubs.
- Flutter och Dart för plattformsoberoende utveckling.
- Kotlin och Swift för android- och iOS-intern utveckling.
De angivna stegen är specifika för macOS. Du kan följa med i Windows genom att hoppa över iOS-aspekterna .
Konfigurera Push Notification Services och Azure Notification Hub
I det här avsnittet konfigurerar du Firebase Cloud Messaging (FCM) och Apple Push Notification Services (APNS). Sedan skapar och konfigurerar du en meddelandehubb så att den fungerar med dessa tjänster.
Skapa ett Firebase-projekt och aktivera Firebase Cloud Messaging för Android
Logga in på Firebase-konsolen. Skapa ett nytt Firebase-projekt som anger PushDemo som projektnamn.
Observera
Ett unikt namn genereras åt dig. Som standard består detta av en gemen variant av namnet du angav plus ett genererat tal avgränsat med ett bindestreck. Du kan ändra detta om du vill förutsatt att det fortfarande är globalt unikt.
När du har skapat projektet väljer du Lägg till Firebase i din Android-app.
På sidan Lägg till Firebase i din Android-app utför du följande steg.
Ange ett namn för ditt paket för Android-paketnamnet. Till exempel:
com.<organization_identifier>.<package_name>
.Välj Registrera app.
Välj Ladda ned google-services.json. Spara sedan filen i en lokal mapp för senare användning och välj Nästa.
Välj Nästa.
Välj Fortsätt till konsolen
Observera
Om knappen Fortsätt till konsolen inte är aktiverad på grund av verifieringsinstallationskontrollen väljer du Hoppa över det här steget.
I Firebase-konsolen väljer du kugghjulet för projektet. Välj sedan Projektinställningar.
Observera
Om du inte har laddat ned google-services.json-filen kan du ladda ned den på den här sidan.
Växla till fliken Cloud Messaging längst upp. Kopiera och spara servernyckeln för senare användning. Du använder det här värdet för att konfigurera meddelandehubben.
Registrera din iOS-app för push-meddelanden
Om du vill skicka push-meddelanden till en iOS-app registrerar du ditt program med Apple och registrerar dig för push-meddelanden.
Om du inte redan har registrerat din app bläddrar du till iOS-etableringsportalen i Apple Developer Center. Logga in på portalen med ditt Apple-ID, gå till Certifikat, Identifierare & profiler och välj sedan Identifierare. Klicka här om du + vill registrera en ny app.
På skärmen Registrera en ny identifierare väljer du alternativknappen App-ID :n. Välj sedan Fortsätt.
Uppdatera följande tre värden för din nya app och välj sedan Fortsätt:
Beskrivning: Ange ett beskrivande namn för din app.
Paket-ID: Ange ett paket-ID för formuläret com.organization_identifier<>.<>product_name enligt beskrivningen i appdistributionsguiden. På följande skärmbild
mobcat
används värdet som organisationsidentifierare och PushDemo-värdet används som produktnamn.Push-meddelanden: Kontrollera alternativet Push-meddelanden i avsnittet Funktioner .
Den här åtgärden genererar ditt app-ID och begär att du bekräftar informationen. Välj Fortsätt och välj sedan Registrera för att bekräfta det nya app-ID:t.
När du har valt Registrera visas det nya app-ID:t som ett radobjekt på sidan Certifikat, Identifierare & Profiler .
På sidan Certifikat letar identifierare & profiler , under Identifierare, upp det radobjekt för app-ID som du skapade. Välj sedan dess rad för att visa skärmen Redigera app-ID-konfiguration .
Skapa ett certifikat för Notification Hubs
Ett certifikat krävs för att meddelandehubben ska fungera med Apple Push Notification Services (APNS) och kan tillhandahållas på något av två sätt:
Skapa ett p12-pushcertifikat som kan laddas upp direkt till Notification Hub (den ursprungliga metoden)
Skapa ett p8-certifikat som kan användas för tokenbaserad autentisering (den nyare och rekommenderade metoden)
Den nyare metoden har ett antal fördelar som beskrivs i Tokenbaserad autentisering (HTTP/2) för APNS. Färre steg krävs men krävs även för specifika scenarier. Det finns dock steg för båda metoderna eftersom båda kommer att fungera i den här självstudien.
ALTERNATIV 1: Skapa ett p12-pushcertifikat som kan laddas upp direkt till Notification Hub
På din Mac kör du nyckelringsåtkomstverktyget. Den kan öppnas från mappen Verktyg eller mappen Övrigt på Launchpad.
Välj Nyckelringsåtkomst, expandera Certifikatassistenten och välj sedan Begär ett certifikat från en certifikatutfärdare.
Observera
Som standard väljer Nyckelringsåtkomst det första objektet i listan. Detta kan vara ett problem om du är i kategorin Certifikat och Apple Worldwide Developer Relations Certification Authority inte är det första objektet i listan. Kontrollera att du har ett icke-nyckelobjekt, eller att nyckeln för Apple Worldwide Developer Relations Certification Authority är vald innan du genererar CSR (certifikatsigneringsbegäran).
Välj din användare Email adress, ange ditt eget namn, se till att du anger Sparad till disk och välj sedan Fortsätt. Lämna CA-Email adress tom eftersom det inte krävs.
Ange ett namn för csr-filen (Certificate Signing Request) i Spara som, välj platsen i Var och välj sedan Spara.
Den här åtgärden sparar CSR-filen på den valda platsen. Standardplatsen är Desktop. Kom ihåg den plats som valts för filen.
Tillbaka på sidan Certifikat, identifierare & profiler i iOS-etableringsportalen rullar du ned till det markerade alternativet Push-meddelanden och väljer sedan Konfigurera för att skapa certifikatet.
Fönstret TLS/SSL-certifikat för Apple Push Notification Service visas. Välj knappen Skapa certifikat under avsnittet TLS/SSL-certifikat för utveckling .
Skärmen Skapa ett nytt certifikat visas.
Observera
I den här självstudien används ett utvecklingscertifikat. Samma process används när du registrerar ett produktionscertifikat. Se bara till att du använder samma certifikattyp när du skickar meddelanden.
Välj Välj fil, bläddra till den plats där du sparade CSR-filen och dubbelklicka sedan på certifikatnamnet för att läsa in den. Välj sedan Fortsätt.
När portalen har skapat certifikatet väljer du knappen Ladda ned . Spara certifikatet och kom ihåg den plats där det sparas.
Certifikatet laddas ned och sparas på datorn i mappen Hämtade filer .
Observera
Som standard heter det nedladdade utvecklingscertifikatet aps_development.cer.
Dubbelklicka på det nedladdade push-certifikatet aps_development.cer. Den här åtgärden installerar det nya certifikatet i nyckelringen enligt följande bild:
Observera
Även om namnet i certifikatet kan vara annorlunda kommer namnet att föregås av Apple Development iOS Push Services och ha rätt paketidentifierare associerad med det.
I Nyckelringsåtkomstkontrollerar + du Klicka på det nya push-certifikatet som du skapade i kategorin Certifikat. Välj Exportera, namnge filen, välj formatet p12 och välj sedan Spara.
Du kan välja att skydda certifikatet med ett lösenord, men ett lösenord är valfritt. Klicka på OK om du vill kringgå skapandet av lösenord. Anteckna filnamnet och platsen för det exporterade p12-certifikatet. De används för att aktivera autentisering med APNs.
Observera
Namnet på p12-filen och platsen kan skilja sig från vad som visas i den här självstudien.
ALTERNATIV 2: Skapa ett p8-certifikat som kan användas för tokenbaserad autentisering
Anteckna följande information:
- App-ID-prefix (team-ID)
- Paket-ID
När du är tillbaka i Certifikat, Identifierare & profiler klickar du på Nycklar.
Observera
Om du redan har en nyckel konfigurerad för APNS kan du återanvända p8-certifikatet som du laddade ned direkt efter att det skapades. I så fall kan du ignorera steg 3 till 5.
+ Klicka på knappen (eller knappen Skapa en nyckel) för att skapa en ny nyckel.
Ange ett lämpligt nyckelnamnsvärde , kontrollera sedan alternativet Apple Push Notifications Service (APNS) och klicka sedan på Fortsätt följt av Registrera på nästa skärm.
Klicka på Ladda ned och flytta sedan p8-filen (prefixet med AuthKey_) till en säker lokal katalog och klicka sedan på Klar.
Observera
Se till att hålla p8-filen på en säker plats (och spara en säkerhetskopia). När du har laddat ned nyckeln kan den inte laddas ned igen eftersom serverkopian tas bort.
På Nycklar klickar du på den nyckel som du skapade (eller en befintlig nyckel om du har valt att använda den i stället).
Anteckna nyckel-ID-värdet .
Öppna ditt p8-certifikat i ett lämpligt program, till exempel Visual Studio Code. Anteckna nyckelvärdet (mellan -----BEGIN PRIVATE KEY----- och -----END PRIVATE KEY-----).
-----BEGIN PRIVAT NYCKEL-----
<key_value>
-----END PRIVAT NYCKEL-----Observera
Det här är det tokenvärde som ska användas senare för att konfigurera Notification Hub.
I slutet av de här stegen bör du ha följande information för senare användning i Konfigurera din meddelandehubb med APNS-information:
- Team-ID (se steg 1)
- Paket-ID (se steg 1)
- Nyckel-ID (se steg 7)
- Tokenvärde (p8-nyckelvärde som hämtades i steg 8)
Skapa en etableringsprofil för appen
Gå tillbaka till iOS-etableringsportalen, välj Certifikat, Identifierare & profiler, välj Profiler på den vänstra menyn och välj + sedan för att skapa en ny profil. Skärmen Registrera en ny etableringsprofil visas.
Välj iOS-apputveckling under Utveckling som etableringsprofiltyp och välj sedan Fortsätt.
Välj sedan det app-ID som du skapade i listrutan App-ID och välj Fortsätt.
I fönstret Välj certifikat väljer du det utvecklingscertifikat som du använder för kodsignering och väljer Fortsätt.
Observera
Det här certifikatet är inte det push-certifikat som du skapade i föregående steg. Det här är ditt utvecklingscertifikat. Om det inte finns någon måste du skapa den eftersom det är en förutsättning för den här självstudien. Utvecklarcertifikat kan skapas i Apple Developer Portal, via Xcode eller i Visual Studio.
Gå tillbaka till sidan Certifikat, Identifierare & profiler , välj Profiler på den vänstra menyn och välj + sedan för att skapa en ny profil. Skärmen Registrera en ny etableringsprofil visas.
I fönstret Välj certifikat väljer du det utvecklingscertifikat som du skapade. Välj sedan Fortsätt.
Välj sedan de enheter som ska användas för testning och välj Fortsätt.
Välj slutligen ett namn för profilen i Etableringsprofilnamn och välj Generera.
När den nya etableringsprofilen skapas väljer du Ladda ned. Kom ihåg den plats där den sparas.
Bläddra till platsen för etableringsprofilen och dubbelklicka sedan på den för att installera den på utvecklingsdatorn.
Skapa en meddelandehubb
I det här avsnittet skapar du en meddelandehubb och konfigurerar autentisering med APNS. Du kan använda ett p12-pushcertifikat eller tokenbaserad autentisering. Om du vill använda en meddelandehubb som du redan har skapat kan du gå vidare till steg 5.
Logga in på Azure.
Klicka på Skapa en resurs, sök efter och välj Notification Hub och klicka sedan på Skapa.
Uppdatera följande fält och klicka sedan på Skapa:
GRUNDLÄGGANDE INFORMATION
Prenumeration: Välj målprenumeration i listrutan
Resursgrupp: Skapa en ny resursgrupp (eller välj en befintlig)NAMNOMRÅDESINFORMATION
Notification Hub-namnområde: Ange ett globalt unikt namn för Notification Hub-namnområdet
Observera
Kontrollera att alternativet Skapa ny har valts för det här fältet.
INFORMATION OM MEDDELANDEHUBBEN
Notification Hub: Ange ett namn för Notification Hub
Plats: Välj en lämplig plats i listrutan
Prisnivå: Behåll standardalternativet KostnadsfriObservera
Såvida du inte har nått det maximala antalet hubbar på den kostnadsfria nivån.
När Notification Hub har etablerats navigerar du till den resursen.
Gå till din nya Notification Hub.
Välj Åtkomstprinciper i listan (under HANTERA).
Anteckna värdena för principnamn tillsammans med motsvarande anslutningssträngvärden .
Konfigurera meddelandehubben med APNS-information
Under Notification Services väljer du Apple och följer sedan lämpliga steg baserat på den metod du valde tidigare i avsnittet Skapa ett certifikat för Notification Hubs .
Observera
Använd endast produktionsläget för programläge om du vill skicka push-meddelanden till användare som har köpt din app från butiken.
ALTERNATIV 1: Använda ett .p12-pushcertifikat
Välj Certifikat.
Välj filikonen.
Välj den .p12-fil som du exporterade tidigare och välj sedan Öppna.
Ange rätt lösenord om det behövs.
Välj Sandbox-läge .
Välj Spara.
ALTERNATIV 2: Använda tokenbaserad autentisering
Välj Token.
Ange följande värden som du hämtade tidigare:
- Nyckel-ID
- Paket-ID
- Team-ID
- Token
Välj Sandbox.
Välj Spara.
Konfigurera meddelandehubben med FCM-information
- Välj Google (GCM/FCM) i avsnittet Inställningar på den vänstra menyn.
- Ange servernyckeln som du antecknade från Google Firebase-konsolen.
- Välj Spara i verktygsfältet.
Skapa ett ASP.NET Core webb-API-serverdelsprogram
I det här avsnittet skapar du ASP.NET Core webb-API-serverdelen för att hantera enhetsregistrering och skicka meddelanden till Flutter-mobilappen.
Skapa ett webbprojekt
I Visual Studio väljer du Ny>fillösning.
Välj .NET Core>App>ASP.NET Core>API>Nästa.
I dialogrutan Konfigurera din nya ASP.NET Core webb-API väljer du Målramverk för .NET Core 3.1.
Ange PushDemoApi som projektnamn och välj sedan Skapa.
Börja felsöka (Kommando retur + ) för att testa den mallade appen.
Observera
Den mallade appen är konfigurerad för att använda WeatherForecastController som launchUrl. Detta anges i Egenskaper>launchSettings.json.
Om du uppmanas att ange ett ogiltigt utvecklingscertifikat hittades meddelandet:
Klicka på Ja för att godkänna att du kör verktyget "dotnet dev-certs https" för att åtgärda detta. Verktyget "dotnet dev-certs https" uppmanar dig sedan att ange ett lösenord för certifikatet och lösenordet för nyckelringen.
Klicka på Ja när du uppmanas att installera och lita på det nya certifikatet och ange sedan lösenordet för nyckelringen.
Expandera mappen Controllers och ta sedan bort WeatherForecastController.cs.
Ta bort WeatherForecast.cs.
Konfigurera lokala konfigurationsvärden med hjälp av secret manager-verktyget. Genom att frigöra hemligheterna från lösningen ser du till att de inte hamnar i källkontrollen. Öppna Terminal och gå sedan till katalogen för projektfilen och kör följande kommandon:
dotnet user-secrets init dotnet user-secrets set "NotificationHub:Name" <value> dotnet user-secrets set "NotificationHub:ConnectionString" <value>
Ersätt platshållarvärdena med ditt eget meddelandehubbnamn och anslutningssträng värden. Du antecknar dem i avsnittet skapa en meddelandehubb . Annars kan du leta upp dem i Azure.
NotificationHub:Name:
Se Namn i Essentials-sammanfattningen överst i Översikt.NotificationHub:ConnectionString:
Se DefaultFullSharedAccessSignature i ÅtkomstprinciperObservera
För produktionsscenarier kan du titta på alternativ som Azure KeyVault för att lagra anslutningssträng på ett säkert sätt. För enkelhetens skull läggs hemligheterna till i Azure App Service programinställningar.
Autentisera klienter med en API-nyckel (valfritt)
API-nycklar är inte lika säkra som token, men räcker för den här självstudien. En API-nyckel kan enkelt konfigureras via ASP.NET Middleware.
Lägg till API-nyckeln i de lokala konfigurationsvärdena.
dotnet user-secrets set "Authentication:ApiKey" <value>
Observera
Du bör ersätta platshållarvärdet med ditt eget och anteckna det.
Kontroll + Klicka på PushDemoApi-projektet , välj Ny mapp på menyn Lägg till och klicka sedan på Lägg till med autentisering som mappnamn.
Kontroll + Klicka på mappen Autentisering och välj sedan Ny fil... på menyn Lägg till .
Välj Allmän>tom klass, ange ApiKeyAuthOptions.cs som Namn och klicka sedan på Nytt och lägg till följande implementering.
using Microsoft.AspNetCore.Authentication; namespace PushDemoApi.Authentication { public class ApiKeyAuthOptions : AuthenticationSchemeOptions { public const string DefaultScheme = "ApiKey"; public string Scheme => DefaultScheme; public string ApiKey { get; set; } } }
Lägg till ytterligare en tom klass i mappen Autentisering med namnet ApiKeyAuthHandler.cs och lägg sedan till följande implementering.
using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace PushDemoApi.Authentication { public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOptions> { const string ApiKeyIdentifier = "apikey"; public ApiKeyAuthHandler( IOptionsMonitor<ApiKeyAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) {} protected override Task<AuthenticateResult> HandleAuthenticateAsync() { string key = string.Empty; if (Request.Headers[ApiKeyIdentifier].Any()) { key = Request.Headers[ApiKeyIdentifier].FirstOrDefault(); } else if (Request.Query.ContainsKey(ApiKeyIdentifier)) { if (Request.Query.TryGetValue(ApiKeyIdentifier, out var queryKey)) key = queryKey; } if (string.IsNullOrWhiteSpace(key)) return Task.FromResult(AuthenticateResult.Fail("No api key provided")); if (!string.Equals(key, Options.ApiKey, StringComparison.Ordinal)) return Task.FromResult(AuthenticateResult.Fail("Invalid api key.")); var identities = new List<ClaimsIdentity> { new ClaimsIdentity("ApiKeyIdentity") }; var ticket = new AuthenticationTicket( new ClaimsPrincipal(identities), Options.Scheme); return Task.FromResult(AuthenticateResult.Success(ticket)); } } }
Observera
En autentiseringshanterare är en typ som implementerar beteendet för ett schema, i det här fallet ett anpassat API-nyckelschema.
Lägg till ytterligare en tom klass i mappen Autentisering med namnet ApiKeyAuthenticationBuilderExtensions.cs och lägg sedan till följande implementering.
using System; using Microsoft.AspNetCore.Authentication; namespace PushDemoApi.Authentication { public static class AuthenticationBuilderExtensions { public static AuthenticationBuilder AddApiKeyAuth( this AuthenticationBuilder builder, Action<ApiKeyAuthOptions> configureOptions) { return builder .AddScheme<ApiKeyAuthOptions, ApiKeyAuthHandler>( ApiKeyAuthOptions.DefaultScheme, configureOptions); } } }
Observera
Den här tilläggsmetoden förenklar konfigurationskoden för mellanprogram i Startup.cs vilket gör den mer läsbar och allmänt enklare att följa.
I Startup.cs uppdaterar du metoden ConfigureServices för att konfigurera API-nyckelautentiseringen under anropet till tjänsterna. AddControllers-metod .
using PushDemoApi.Authentication; using PushDemoApi.Models; using PushDemoApi.Services; public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme; options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme; }).AddApiKeyAuth(Configuration.GetSection("Authentication").Bind); }
I Startup.cs uppdaterar du metoden Configure (Konfigurera ) för att anropa metoderna UseAuthentication och UseAuthorization-tillägget på appens IApplicationBuilder. Se till att dessa metoder anropas efter UseRouting och före appen. UseEndpoints.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
Observera
Anropa UseAuthentication registrerar mellanprogrammet som använder tidigare registrerade autentiseringsscheman (från ConfigureServices). Detta måste anropas innan mellanprogram som är beroende av att användare autentiseras.
Lägga till beroenden och konfigurera tjänster
ASP.NET Core stöder mönstret för beroendeinmatning (DI), vilket är en teknik för att uppnå inversion av kontroll (IoC) mellan klasser och deras beroenden.
Användning av meddelandehubben och Notification Hubs SDK för serverdelsåtgärder kapslas in i en tjänst. Tjänsten registreras och görs tillgänglig genom en lämplig abstraktion.
Kontroll + Klicka på mappen Beroenden och välj sedan Hantera NuGet-paket....
Sök efter Microsoft.Azure.NotificationHubs och kontrollera att det är markerat.
Klicka på Lägg till paket och klicka sedan på Acceptera när du uppmanas att godkänna licensvillkoren.
Kontroll + Klicka på PushDemoApi-projektet , välj Ny mapp på Menyn Lägg till och klicka sedan på Lägg till med modeller som mappnamn.
Kontroll + Klicka på mappen Modeller och välj sedan Ny fil... på menyn Lägg till .
Välj Allmän>tom klass, ange PushTemplates.cs som Namn och klicka sedan på Nytt och lägg till följande implementering.
namespace PushDemoApi.Models { public class PushTemplates { public class Generic { public const string Android = "{ \"notification\": { \"title\" : \"PushDemo\", \"body\" : \"$(alertMessage)\"}, \"data\" : { \"action\" : \"$(alertAction)\" } }"; public const string iOS = "{ \"aps\" : {\"alert\" : \"$(alertMessage)\"}, \"action\" : \"$(alertAction)\" }"; } public class Silent { public const string Android = "{ \"data\" : {\"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\"} }"; public const string iOS = "{ \"aps\" : {\"content-available\" : 1, \"apns-priority\": 5, \"sound\" : \"\", \"badge\" : 0}, \"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\" }"; } } }
Observera
Den här klassen innehåller de tokeniserade meddelandenyttolasterna för de allmänna och tysta meddelanden som krävs i det här scenariot. Nyttolaster definieras utanför installationen för att tillåta experimentering utan att behöva uppdatera befintliga installationer via tjänsten. Hantering av ändringar av installationer på det här sättet ligger utanför omfånget för den här självstudien. Överväg anpassade mallar för produktion.
Lägg till ytterligare en tom klass i mappen Modeller med namnet DeviceInstallation.cs och lägg sedan till följande implementering.
using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace PushDemoApi.Models { public class DeviceInstallation { [Required] public string InstallationId { get; set; } [Required] public string Platform { get; set; } [Required] public string PushChannel { get; set; } public IList<string> Tags { get; set; } = Array.Empty<string>(); } }
Lägg till ytterligare en tom klass i mappen Modeller med namnet NotificationRequest.cs och lägg sedan till följande implementering.
using System; namespace PushDemoApi.Models { public class NotificationRequest { public string Text { get; set; } public string Action { get; set; } public string[] Tags { get; set; } = Array.Empty<string>(); public bool Silent { get; set; } } }
Lägg till ytterligare en tom klass i mappen Modeller med namnet NotificationHubOptions.cs och lägg sedan till följande implementering.
using System.ComponentModel.DataAnnotations; namespace PushDemoApi.Models { public class NotificationHubOptions { [Required] public string Name { get; set; } [Required] public string ConnectionString { get; set; } } }
Lägg till en ny mapp i PushDemoApi-projektetmed namnet Tjänster.
Lägg till ett tomt gränssnitt i mappen Tjänster med namnet INotificationService.cs och lägg sedan till följande implementering.
using System.Threading; using System.Threading.Tasks; using PushDemoApi.Models; namespace PushDemoApi.Services { public interface INotificationService { Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token); Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token); Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token); } }
Lägg till en tom klass i mappen Tjänster med namnet NotificationHubsService.cs och lägg sedan till följande kod för att implementera gränssnittet INotificationService :
using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.NotificationHubs; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using PushDemoApi.Models; namespace PushDemoApi.Services { public class NotificationHubService : INotificationService { readonly NotificationHubClient _hub; readonly Dictionary<string, NotificationPlatform> _installationPlatform; readonly ILogger<NotificationHubService> _logger; public NotificationHubService(IOptions<NotificationHubOptions> options, ILogger<NotificationHubService> logger) { _logger = logger; _hub = NotificationHubClient.CreateClientFromConnectionString( options.Value.ConnectionString, options.Value.Name); _installationPlatform = new Dictionary<string, NotificationPlatform> { { nameof(NotificationPlatform.Apns).ToLower(), NotificationPlatform.Apns }, { nameof(NotificationPlatform.Fcm).ToLower(), NotificationPlatform.Fcm } }; } public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token) { if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) || string.IsNullOrWhiteSpace(deviceInstallation?.Platform) || string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel)) return false; var installation = new Installation() { InstallationId = deviceInstallation.InstallationId, PushChannel = deviceInstallation.PushChannel, Tags = deviceInstallation.Tags }; if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform)) installation.Platform = platform; else return false; try { await _hub.CreateOrUpdateInstallationAsync(installation, token); } catch { return false; } return true; } public async Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token) { if (string.IsNullOrWhiteSpace(installationId)) return false; try { await _hub.DeleteInstallationAsync(installationId, token); } catch { return false; } return true; } public async Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token) { if ((notificationRequest.Silent && string.IsNullOrWhiteSpace(notificationRequest?.Action)) || (!notificationRequest.Silent && (string.IsNullOrWhiteSpace(notificationRequest?.Text)) || string.IsNullOrWhiteSpace(notificationRequest?.Action))) return false; var androidPushTemplate = notificationRequest.Silent ? PushTemplates.Silent.Android : PushTemplates.Generic.Android; var iOSPushTemplate = notificationRequest.Silent ? PushTemplates.Silent.iOS : PushTemplates.Generic.iOS; var androidPayload = PrepareNotificationPayload( androidPushTemplate, notificationRequest.Text, notificationRequest.Action); var iOSPayload = PrepareNotificationPayload( iOSPushTemplate, notificationRequest.Text, notificationRequest.Action); try { if (notificationRequest.Tags.Length == 0) { // This will broadcast to all users registered in the notification hub await SendPlatformNotificationsAsync(androidPayload, iOSPayload, token); } else if (notificationRequest.Tags.Length <= 20) { await SendPlatformNotificationsAsync(androidPayload, iOSPayload, notificationRequest.Tags, token); } else { var notificationTasks = notificationRequest.Tags .Select((value, index) => (value, index)) .GroupBy(g => g.index / 20, i => i.value) .Select(tags => SendPlatformNotificationsAsync(androidPayload, iOSPayload, tags, token)); await Task.WhenAll(notificationTasks); } return true; } catch (Exception e) { _logger.LogError(e, "Unexpected error sending notification"); return false; } } string PrepareNotificationPayload(string template, string text, string action) => template .Replace("$(alertMessage)", text, StringComparison.InvariantCulture) .Replace("$(alertAction)", action, StringComparison.InvariantCulture); Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, CancellationToken token) { var sendTasks = new Task[] { _hub.SendFcmNativeNotificationAsync(androidPayload, token), _hub.SendAppleNativeNotificationAsync(iOSPayload, token) }; return Task.WhenAll(sendTasks); } Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, IEnumerable<string> tags, CancellationToken token) { var sendTasks = new Task[] { _hub.SendFcmNativeNotificationAsync(androidPayload, tags, token), _hub.SendAppleNativeNotificationAsync(iOSPayload, tags, token) }; return Task.WhenAll(sendTasks); } } }
Observera
Tagguttrycket som anges i SendTemplateNotificationAsync är begränsat till 20 taggar. Det är begränsat till 6 för de flesta operatorer, men uttrycket innehåller endast OR (||) i det här fallet. Om det finns fler än 20 taggar i begäran måste de delas upp i flera begäranden. Mer information finns i dokumentationen om routnings- och tagguttryck .
I Startup.cs uppdaterar du metoden ConfigureServices för att lägga till NotificationHubsService som en singleton-implementering av INotificationService.
using PushDemoApi.Models; using PushDemoApi.Services; public void ConfigureServices(IServiceCollection services) { ... services.AddSingleton<INotificationService, NotificationHubService>(); services.AddOptions<NotificationHubOptions>() .Configure(Configuration.GetSection("NotificationHub").Bind) .ValidateDataAnnotations(); }
Skapa meddelande-API:et
Kontroll + Klicka på mappen Controllers och välj sedan Ny fil... på menyn Lägg till .
Välj ASP.NET Core>Webb-API-kontrollantklass, ange NotificationsController som Namn och klicka sedan på Ny.
Observera
Om du följer med Visual Studio 2019 väljer du mallen API-kontrollant med läs-/skrivåtgärder .
Lägg till följande namnområden överst i filen.
using System.ComponentModel.DataAnnotations; using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PushDemoApi.Models; using PushDemoApi.Services;
Uppdatera den mallade kontrollanten så att den härleds från ControllerBase och är dekorerad med attributet ApiController .
[ApiController] [Route("api/[controller]")] public class NotificationsController : ControllerBase { // Templated methods here }
Observera
Basklassen Controller har stöd för vyer, men detta behövs inte i det här fallet, så ControllerBase kan användas i stället. Om du följer med Visual Studio 2019 kan du hoppa över det här steget.
Om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel bör du även dekorera NotificationsController med attributet Auktorisera .
[Authorize]
Uppdatera konstruktorn för att acceptera den registrerade instansen av INotificationService som ett argument och tilldela den till en skrivskyddad medlem.
readonly INotificationService _notificationService; public NotificationsController(INotificationService notificationService) { _notificationService = notificationService; }
I launchSettings.json (i mappen Egenskaper ) ändrar du launchUrl från
weatherforecast
till api/notifications så att den matchar url:en som anges i attributet RegistrationsControllerRoute .Börja felsöka (Kommando + retur) för att verifiera att appen fungerar med den nya NotificationsController och returnerar statusen 401 Obehörig .
Observera
Visual Studio kanske inte startar appen automatiskt i webbläsaren. Du kommer att använda Postman för att testa API:et från och med nu.
På en ny Postman-flik ställer du in begäran på GET. Ange adressen nedan och ersätt platshållarprogrammetUrl <> med https applicationUrl som finns i Egenskaper>launchSettings.json.
<applicationUrl>/api/notifications
Observera
ApplicationUrl ska vara förhttps://localhost:5001 standardprofilen. Om du använder IIS (standard i Visual Studio 2019 i Windows) bör du använda applicationUrl som anges i iisSettings-objektet i stället. Du får ett 404-svar om adressen är felaktig.
Om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel måste du konfigurera begärandehuvudena så att de inkluderar apikey-värdet .
Nyckel Värde apikey <your_api_key> Klicka på knappen Skicka .
Observera
Du bör få statusen 200 OK med lite JSON-innehåll .
Om du får en SSL-certifikatverifieringsvarning kan du ändra inställningen för SSL-certifikatverifiering för begäran Postman i inställningarna.
Ersätt de mallade klassmetoderna i NotificationsController.cs med följande kod.
[HttpPut] [Route("installations")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<IActionResult> UpdateInstallation( [Required]DeviceInstallation deviceInstallation) { var success = await _notificationService .CreateOrUpdateInstallationAsync(deviceInstallation, HttpContext.RequestAborted); if (!success) return new UnprocessableEntityResult(); return new OkResult(); } [HttpDelete()] [Route("installations/{installationId}")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<ActionResult> DeleteInstallation( [Required][FromRoute]string installationId) { var success = await _notificationService .DeleteInstallationByIdAsync(installationId, CancellationToken.None); if (!success) return new UnprocessableEntityResult(); return new OkResult(); } [HttpPost] [Route("requests")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<IActionResult> RequestPush( [Required]NotificationRequest notificationRequest) { if ((notificationRequest.Silent && string.IsNullOrWhiteSpace(notificationRequest?.Action)) || (!notificationRequest.Silent && string.IsNullOrWhiteSpace(notificationRequest?.Text))) return new BadRequestResult(); var success = await _notificationService .RequestNotificationAsync(notificationRequest, HttpContext.RequestAborted); if (!success) return new UnprocessableEntityResult(); return new OkResult(); }
Skapa API-appen
Nu skapar du en API-app i Azure App Service för att vara värd för serverdelstjänsten.
Logga in på Azure Portal.
Klicka på Skapa en resurs, sök efter och välj API-app och klicka sedan på Skapa.
Uppdatera följande fält och klicka sedan på Skapa.
Appnamn:
Ange ett globalt unikt namn för API-appenPrenumeration:
Välj samma målprenumeration som du skapade meddelandehubben i.Resursgrupp:
Välj samma resursgrupp som du skapade meddelandehubben i.App Service plan/plats:
Skapa en ny App Service planObservera
Ändra från standardalternativet till en plan som innehåller SSL-stöd . Annars måste du vidta lämpliga åtgärder när du arbetar med mobilappen för att förhindra att HTTP-begäranden blockeras.
Application Insights:
Behåll det föreslagna alternativet (en ny resurs skapas med det namnet) eller välj en befintlig resurs.När API-appen har etablerats går du till den resursen.
Anteckna URL-egenskapen i Essentials-sammanfattningen överst i översikten. Den här URL:en är din serverdelsslutpunkt som kommer att användas senare i den här självstudien.
Observera
URL:en använder API-appnamnet som du angav tidigare, med formatet
https://<app_name>.azurewebsites.net
.Välj Konfiguration i listan (under Inställningar).
För var och en av inställningarna nedan klickar du på Ny programinställning för att ange Namn och ett värde och klickar sedan på OK.
Namn Värde Authentication:ApiKey
<api_key_value> NotificationHub:Name
<hub_name_value> NotificationHub:ConnectionString
<hub_connection_string_value> Observera
Det här är samma inställningar som du definierade tidigare i användarinställningarna. Du bör kunna kopiera över dessa. Inställningen Authentication:ApiKey krävs bara om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel . I produktionsscenarier kan du titta på alternativ som Azure KeyVault. Dessa har lagts till som programinställningar för enkelhetens skull i det här fallet.
När alla programinställningar har lagts till klickar du på Spara och sedan på Fortsätt.
Publicera serverdelstjänsten
Därefter distribuerar du appen till API-appen för att göra den tillgänglig från alla enheter.
Observera
Följande steg är specifika för Visual Studio för Mac. Om du följer visual studio 2019 i Windows är publiceringsflödet annorlunda. Se Publicera till Azure App Service i Windows.
Ändra konfigurationen från Debug till Release om du inte redan har gjort det.
Kontroll + Klicka påPushDemoApi-projektet och välj sedan Publicera till Azure... på menyn Publicera .
Följ autentiseringsflödet om du uppmanas att göra det. Använd det konto som du använde i föregående skapa AVSNITTET API-app .
Välj den Azure App Service API-app som du skapade tidigare i listan som publiceringsmål och klicka sedan på Publicera.
När du har slutfört guiden publiceras appen till Azure och appen öppnas. Anteckna URL: en om du inte redan har gjort det. Den här URL:en är din serverdelsslutpunkt som används senare i den här självstudien.
Verifiera det publicerade API:et
I Postman öppnar du en ny flik, ställer in begäran på PUT och anger adressen nedan. Ersätt platshållaren med den basadress som du antecknar i föregående publicera avsnittet serverdelstjänst .
https://<app_name>.azurewebsites.net/api/notifications/installations
Observera
Basadressen ska vara i formatet
https://<app_name>.azurewebsites.net/
Om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel måste du konfigurera begärandehuvudena så att de inkluderar apikey-värdet .
Nyckel Värde apikey <your_api_key> Välj raw-alternativet för brödtexten och välj sedan JSON i listan med formatalternativ och ta sedan med lite platshållar-JSON-innehåll :
{}
Klicka på Skicka.
Observera
Du bör få statusen 422 UnprocessableEntity från tjänsten.
Utför steg 1–4 igen, men den här gången anger du slutpunkten för begäranden för att verifiera att du får svaret 400 Felaktig begäran .
https://<app_name>.azurewebsites.net/api/notifications/requests
Observera
Det går ännu inte att testa API:et med giltiga begärandedata eftersom det kräver plattformsspecifik information från klientmobilappen.
Skapa ett plattformsoberoende flutterprogram
I det här avsnittet skapar du ett Flutter-mobilprogram som implementerar push-meddelanden på ett plattformsoberoende sätt.
Det gör att du kan registrera och avregistrera från en meddelandehubb via serverdelstjänsten som du skapade.
En avisering visas när en åtgärd anges och appen är i förgrunden. Annars visas meddelanden i meddelandecentret.
Observera
Du utför vanligtvis registreringsåtgärderna (och avregistreringen) under lämplig punkt i programmets livscykel (eller som en del av din första körningsupplevelse kanske) utan explicita indata från användarregistret/avregistrerar. Det här exemplet kräver dock explicita användarindata så att den här funktionen kan utforskas och testas enklare.
Skapa Flutter-lösningen
Öppna en ny instans av Visual Studio Code.
Öppna kommandopaletten (Skift-kommando + + P).
Välj kommandot Flutter: New Project (Flutter: Nytt projekt) och tryck sedan på Retur.
Ange push_demo som projektnamn och välj sedan en Projektplats.
När du uppmanas att göra det väljer du Hämta paket.
Kontroll + Klicka på kotlin-mappen (under app>src>main) och välj sedan Visa i Finder. Byt sedan namn på de underordnade mapparna (under mappen kotlin ) till
com
,<your_organization>
respektivepushdemo
.Observera
När du använder Visual Studio Code-mallen använder dessa mappar som standard com, <till exempel project_name>. Om mobcat används för organisationen bör mappstrukturen visas som:
- kotlin
- Com
- mobcat
- pushdemo
- mobcat
- Com
- kotlin
I Visual Studio Code uppdaterar du applicationId-värdet iandroid-appen>>build.gradle till
com.<your_organization>.pushdemo
.Observera
Du bör använda ditt eget organisationsnamn för <platshållaren your_organization> . Om du till exempel använder mobcat som organisation resulterar det i paketnamnsvärdetcom.mobcat.pushdemo.
Uppdatera paketattributet i AndroidManifest.xml-filerna under src>debug, src>main respektive src>profile . Se till att värdena matchar det applicationId som du använde i föregående steg.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.<your_organization>.pushdemo>"> ... </manifest>
android:label
Uppdatera attributet i AndroidManifest.xml-filen under src>main till PushDemo. Lägg sedan tillandroid:allowBackup
attributet, direkt underandroid:label
, och ange dess värde till false.<application android:name="io.flutter.app.FlutterApplication" android:label="PushDemo" android:allowBackup="false" android:icon="@mipmap/ic_launcher"> ... </application>
Öppna filen build.gradle på appnivå (android>app>build.gradle) och uppdatera sedan compileSdkVersion (från android-avsnittet ) för att använda API 29. Uppdatera sedan värdena minSdkVersion och targetSdkVersion (från avsnittet defaultConfig ) till 26 respektive 29 .
Observera
Endast de enheter som kör API-nivå 26 och senare stöds i den här självstudien, men du kan utöka det för att stödja enheter som kör äldre versioner.
Kontroll + Klicka på mappen ios och välj sedan Öppna i Xcode.
I Xcode klickar du på Runner ( xcodeproj högst upp, inte mappen). Välj sedan runner-målet och välj fliken Allmänt . När Du har valt All build configuration ( Alla byggkonfigurationer) uppdaterar du paketidentifieraren till
com.<your_organization>.PushDemo
.Observera
Du bör använda ditt eget organisationsnamn för <platshållaren your_organization> . Om du till exempel använder mobcat som organisation resulterar det i ett paketidentifierarvärde för com.mobcat.PushDemo.
Klicka på Info.plist och uppdatera sedan värdet för Paketnamn till PushDemo
Stäng Xcode och återgå till Visual Studio Code.
I Visual Studio Code öppnar du pubspec.yaml, lägger till http- och flutter_secure_storageDart-paketen som beroenden. Spara sedan filen och klicka på Hämta paket när du uppmanas att göra det.
dependencies: flutter: sdk: flutter http: ^0.12.1 flutter_secure_storage: ^3.3.3
I Terminal ändrar du katalogen till ios-mappen (för ditt Flutter-projekt). Kör sedan kommandot poddinstallation för att installera nya poddar (krävs av flutter_secure_storage-paketet ).
Kontroll + Klicka på mappen lib och välj sedan Ny fil på menyn med main_page.dart som filnamn. Lägg sedan till följande kod.
import 'package:flutter/material.dart'; class MainPage extends StatefulWidget { @override _MainPageState createState() => _MainPageState(); } class _MainPageState extends State<MainPage> { @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[], ) ) ); } }
Ersätt den mallade koden med följande i main.dart.
import 'package:flutter/material.dart'; import 'package:push_demo/main_page.dart'; final navigatorKey = GlobalKey<NavigatorState>(); void main() => runApp(MaterialApp(home: MainPage(), navigatorKey: navigatorKey));
I Terminal skapar och kör du appen på varje målplattform för att testa att den mallade appen körs på dina enheter. Kontrollera att enheter som stöds är anslutna.
flutter run
Implementera plattformsoberoende komponenter
Kontroll + Klicka på mappen lib och välj sedan Ny mapp på menyn med modeller som Mappnamn.
Kontroll + Klicka på mappen models och välj sedan Ny fil på menyn med device_installation.dart som filnamn. Lägg sedan till följande kod.
class DeviceInstallation { final String deviceId; final String platform; final String token; final List<String> tags; DeviceInstallation(this.deviceId, this.platform, this.token, this.tags); DeviceInstallation.fromJson(Map<String, dynamic> json) : deviceId = json['installationId'], platform = json['platform'], token = json['pushChannel'], tags = json['tags']; Map<String, dynamic> toJson() => { 'installationId': deviceId, 'platform': platform, 'pushChannel': token, 'tags': tags, }; }
Lägg till en ny fil i mappen modelsmed namnet push_demo_action.dart som definierar uppräkningen av åtgärder som stöds i det här exemplet.
enum PushDemoAction { actionA, actionB, }
Lägg till en ny mapp i projektet med namnet services och lägg sedan till en ny fil i mappen med namnet device_installation_service.dart med följande implementering.
import 'package:flutter/services.dart'; class DeviceInstallationService { static const deviceInstallation = const MethodChannel('com.<your_organization>.pushdemo/deviceinstallation'); static const String getDeviceIdChannelMethod = "getDeviceId"; static const String getDeviceTokenChannelMethod = "getDeviceToken"; static const String getDevicePlatformChannelMethod = "getDevicePlatform"; Future<String> getDeviceId() { return deviceInstallation.invokeMethod(getDeviceIdChannelMethod); } Future<String> getDeviceToken() { return deviceInstallation.invokeMethod(getDeviceTokenChannelMethod); } Future<String> getDevicePlatform() { return deviceInstallation.invokeMethod(getDevicePlatformChannelMethod); } }
Observera
Du bör använda ditt eget organisationsnamn för <platshållaren your_organization> . Om du till exempel använder mobcat som organisation resulterar det i ett MethodChannel-namn på com.mobcat.pushdemo/deviceinstallation.
Den här klassen kapslar in hur du arbetar med den underliggande interna plattformen för att hämta information om den nödvändiga enhetsinstallationen. En MethodChannel underlättar dubbelriktad asynkron kommunikation med de underliggande interna plattformarna. Den plattformsspecifika motsvarigheten för den här kanalen skapas i senare steg.
Lägg till en annan fil i mappen notification_action_service.dart med följande implementering.
import 'package:flutter/services.dart'; import 'dart:async'; import 'package:push_demo/models/push_demo_action.dart'; class NotificationActionService { static const notificationAction = const MethodChannel('com.<your_organization>.pushdemo/notificationaction'); static const String triggerActionChannelMethod = "triggerAction"; static const String getLaunchActionChannelMethod = "getLaunchAction"; final actionMappings = { 'action_a' : PushDemoAction.actionA, 'action_b' : PushDemoAction.actionB }; final actionTriggeredController = StreamController.broadcast(); NotificationActionService() { notificationAction .setMethodCallHandler(handleNotificationActionCall); } Stream get actionTriggered => actionTriggeredController.stream; Future<void> triggerAction({action: String}) async { if (!actionMappings.containsKey(action)) { return; } actionTriggeredController.add(actionMappings[action]); } Future<void> checkLaunchAction() async { final launchAction = await notificationAction.invokeMethod(getLaunchActionChannelMethod) as String; if (launchAction != null) { triggerAction(action: launchAction); } } Future<void> handleNotificationActionCall(MethodCall call) async { switch (call.method) { case triggerActionChannelMethod: return triggerAction(action: call.arguments as String); default: throw MissingPluginException(); break; } } }
Observera
Detta används som en enkel mekanism för att centralisera hanteringen av meddelandeåtgärder så att de kan hanteras på ett plattformsoberoende sätt med hjälp av en starkt typifierad uppräkning. Tjänsten gör det möjligt för den underliggande interna plattformen att utlösa en åtgärd när en har angetts i meddelandenyttolasten. Den gör det också möjligt för den gemensamma koden att retroaktivt kontrollera om en åtgärd angavs under programstarten när Flutter är redo att bearbeta den. När appen till exempel startas genom att trycka på ett meddelande från meddelandecentret.
Lägg till en ny fil i tjänstmappenmed namnet notification_registration_service.dart med följande implementering.
import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:push_demo/services/device_installation_service.dart'; import 'package:push_demo/models/device_installation.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class NotificationRegistrationService { static const notificationRegistration = const MethodChannel('com.<your_organization>.pushdemo/notificationregistration'); static const String refreshRegistrationChannelMethod = "refreshRegistration"; static const String installationsEndpoint = "api/notifications/installations"; static const String cachedDeviceTokenKey = "cached_device_token"; static const String cachedTagsKey = "cached_tags"; final deviceInstallationService = DeviceInstallationService(); final secureStorage = FlutterSecureStorage(); String baseApiUrl; String apikey; NotificationRegistrationService(this.baseApiUrl, this.apikey) { notificationRegistration .setMethodCallHandler(handleNotificationRegistrationCall); } String get installationsUrl => "$baseApiUrl$installationsEndpoint"; Future<void> deregisterDevice() async { final cachedToken = await secureStorage.read(key: cachedDeviceTokenKey); final serializedTags = await secureStorage.read(key: cachedTagsKey); if (cachedToken == null || serializedTags == null) { return; } var deviceId = await deviceInstallationService.getDeviceId(); if (deviceId.isEmpty) { throw "Unable to resolve an ID for the device."; } var response = await http .delete("$installationsUrl/$deviceId", headers: {"apikey": apikey}); if (response.statusCode != 200) { throw "Deregister request failed: ${response.reasonPhrase}"; } await secureStorage.delete(key: cachedDeviceTokenKey); await secureStorage.delete(key: cachedTagsKey); } Future<void> registerDevice(List<String> tags) async { try { final deviceId = await deviceInstallationService.getDeviceId(); final platform = await deviceInstallationService.getDevicePlatform(); final token = await deviceInstallationService.getDeviceToken(); final deviceInstallation = DeviceInstallation(deviceId, platform, token, tags); final response = await http.put(installationsUrl, body: jsonEncode(deviceInstallation), headers: {"apikey": apikey, "Content-Type": "application/json"}); if (response.statusCode != 200) { throw "Register request failed: ${response.reasonPhrase}"; } final serializedTags = jsonEncode(tags); await secureStorage.write(key: cachedDeviceTokenKey, value: token); await secureStorage.write(key: cachedTagsKey, value: serializedTags); } on PlatformException catch (e) { throw e.message; } catch (e) { throw "Unable to register device: $e"; } } Future<void> refreshRegistration() async { final currentToken = await deviceInstallationService.getDeviceToken(); final cachedToken = await secureStorage.read(key: cachedDeviceTokenKey); final serializedTags = await secureStorage.read(key: cachedTagsKey); if (currentToken == null || cachedToken == null || serializedTags == null || currentToken == cachedToken) { return; } final tags = jsonDecode(serializedTags); return registerDevice(tags); } Future<void> handleNotificationRegistrationCall(MethodCall call) async { switch (call.method) { case refreshRegistrationChannelMethod: return refreshRegistration(); default: throw MissingPluginException(); break; } } }
Observera
Den här klassen kapslar in användningen av DeviceInstallationService och begäranden till serverdelstjänsten för att utföra nödvändiga registreringsåtgärder för registrering, avregistrering och uppdatering. Argumentet apiKey krävs bara om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel .
Lägg till en ny fil i mappen lib med namnet config.dart med följande implementering.
class Config { static String apiKey = "API_KEY"; static String backendServiceEndpoint = "BACKEND_SERVICE_ENDPOINT"; }
Observera
Detta används som ett enkelt sätt att definiera apphemligheter. Ersätt platshållarvärdena med dina egna. Du bör ha antecknat dessa när du skapade serverdelstjänsten. URL:en för API-appen ska vara
https://<api_app_name>.azurewebsites.net/
. ApiKey-medlemmen krävs bara om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel.Se till att lägga till detta i gitignore-filen för att undvika att checka in dessa hemligheter i källkontrollen.
Implementera det plattformsoberoende användargränssnittet
Ersätt build-funktionen med följande i main_page.dart.
@override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 40.0), child: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ FlatButton( child: Text("Register"), onPressed: registerButtonClicked, ), FlatButton( child: Text("Deregister"), onPressed: deregisterButtonClicked, ), ], ), ), ); }
Lägg till nödvändiga importer överst i filen main_page.dart .
import 'package:push_demo/services/notification_registration_service.dart'; import 'config.dart';
Lägg till ett fält i klassen _MainPageState för att lagra en referens till NotificationRegistrationService.
final notificationRegistrationService = NotificationRegistrationService(Config.backendServiceEndpoint, Config.apiKey);
I klassen _MainPageState implementerar du händelsehanterarna för knapparna Registrera och AvregistrerapåTryckta händelser. Anropa motsvaranderegisteravregistreringsmetoder/ och visa sedan en avisering för att ange resultatet.
void registerButtonClicked() async { try { await notificationRegistrationService.registerDevice(List<String>()); await showAlert(message: "Device registered"); } catch (e) { await showAlert(message: e); } } void deregisterButtonClicked() async { try { await notificationRegistrationService.deregisterDevice(); await showAlert(message: "Device deregistered"); } catch (e) { await showAlert(message: e); } } Future<void> showAlert({ message: String }) async { return showDialog<void>( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text('PushDemo'), content: SingleChildScrollView( child: ListBody( children: <Widget>[ Text(message), ], ), ), actions: <Widget>[ FlatButton( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); }
Nu i main.dart, se till att följande importer finns överst i filen.
import 'package:flutter/material.dart'; import 'package:push_demo/models/push_demo_action.dart'; import 'package:push_demo/services/notification_action_service.dart'; import 'package:push_demo/main_page.dart';
Deklarera en variabel för att lagra referensen till en instans av NotificationActionService och initiera den.
final notificationActionService = NotificationActionService();
Lägg till funktioner för att hantera visningen av en avisering när en åtgärd utlöses.
void notificationActionTriggered(PushDemoAction action) { showActionAlert(message: "${action.toString().split(".")[1]} action received"); } Future<void> showActionAlert({ message: String }) async { return showDialog<void>( context: navigatorKey.currentState.overlay.context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text('PushDemo'), content: SingleChildScrollView( child: ListBody( children: <Widget>[ Text(message), ], ), ), actions: <Widget>[ FlatButton( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); }
Uppdatera huvudfunktionen för att observera NotificationActionService-åtgärdenTriggered-dataströmmen och kontrollera om det finns åtgärder som registrerats under appstarten.
void main() async { runApp(MaterialApp(home: MainPage(), navigatorKey: navigatorKey,)); notificationActionService.actionTriggered.listen((event) { notificationActionTriggered(event as PushDemoAction); }); await notificationActionService.checkLaunchAction(); }
Observera
Det här är bara för att demonstrera mottagandet och spridningen av push-meddelandeåtgärder. Vanligtvis hanteras dessa tyst, till exempel genom att navigera till en viss vy eller uppdatera vissa data i stället för att visa en avisering i det här fallet.
Konfigurera det interna Android-projektet för push-meddelanden
Lägg till Google Services JSON-filen
Kontroll + Klicka på android-mappen och välj sedan Öppna i Android Studio. Växla sedan till projektvyn (om den inte redan är det).
Leta upp den google-services.json fil som du laddade ned tidigare när du konfigurerade PushDemo-projektet i Firebase-konsolen. Dra den sedan till appmodulens rotkatalog (android-app>>).
Konfigurera bygginställningar och behörigheter
Växla projektvyn till Android.
Öppna AndroidManifest.xmloch lägg sedan till INTERNET - och READ_PHONE_STATE-behörigheter efter programelementet före den avslutande taggen.
<manifest> <application>...</application> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> </manifest>
Lägga till Firebase-SDK:er
I Android Studio öppnar du filen build.gradle på projektnivå (Gradle Scripts>build.gradle (Project: android)). och se till att du har klassökvägen "com.google.gms:google-services" i beroendenoden
buildscript
>.buildscript { repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository } dependencies { // ... // Add the following line: classpath 'com.google.gms:google-services:4.3.3' // Google Services plugin } } allprojects { // ... repositories { // Check that you have the following line (if not, add it): google() // Google's Maven repository // ... } }
Observera
Se till att du refererar till den senaste versionen enligt anvisningarna i Firebase-konsolen när du skapade Android-projektet.
I filen build.gradle på appnivå (Gradle Scripts>build.gradle (Modul: app)) tillämpar du plugin-programmet Google Services Gradle. Använd plugin-programmet precis ovanför Android-noden .
// ... // Add the following line: apply plugin: 'com.google.gms.google-services' // Google Services plugin android { // ... }
I samma fil, i noden beroenden , lägger du till beroendet för Cloud Messaging Android-biblioteket .
dependencies { // ... implementation 'com.google.firebase:firebase-messaging:20.2.0' }
Observera
Se till att du refererar till den senaste versionen enligt Cloud Messaging Android-klientdokumentationen.
Spara ändringarna och klicka sedan på knappen Synkronisera nu (från verktygsfältsprompten) eller Synkronisera projekt med Gradle-filer.
Hantera push-meddelanden för Android
I Android Studioklickardu + på paketmappen com.your_organization.pushdemo<> (app>src>main>kotlin) och väljer Paket på menyn Nytt. Ange tjänster som namn och tryck sedan på Retur.
Kontroll + Klicka på mappen services och välj Kotlin-fil/klass på menyn Nytt . Ange DeviceInstallationService som namn och tryck sedan på Retur.
Implementera DeviceInstallationService med hjälp av följande kod.
package com.<your_organization>.pushdemo.services import android.annotation.SuppressLint import android.content.Context import android.provider.Settings.Secure import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel @SuppressLint("HardwareIds") class DeviceInstallationService { companion object { const val DEVICE_INSTALLATION_CHANNEL = "com.<your_organization>.pushdemo/deviceinstallation" const val GET_DEVICE_ID = "getDeviceId" const val GET_DEVICE_TOKEN = "getDeviceToken" const val GET_DEVICE_PLATFORM = "getDevicePlatform" } private var context: Context private var deviceInstallationChannel : MethodChannel val playServicesAvailable get() = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS constructor(context: Context, flutterEngine: FlutterEngine) { this.context = context deviceInstallationChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, DEVICE_INSTALLATION_CHANNEL) deviceInstallationChannel.setMethodCallHandler { call, result -> handleDeviceInstallationCall(call, result) } } fun getDeviceId() : String = Secure.getString(context.applicationContext.contentResolver, Secure.ANDROID_ID) fun getDeviceToken() : String { if(!playServicesAvailable) { throw Exception(getPlayServicesError()) } // TODO: Revisit once we have created the PushNotificationsFirebaseMessagingService val token = "Placeholder_Get_Value_From_FirebaseMessagingService_Implementation" if (token.isNullOrBlank()) { throw Exception("Unable to resolve token for FCM.") } return token } fun getDevicePlatform() : String = "fcm" private fun handleDeviceInstallationCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { GET_DEVICE_ID -> { result.success(getDeviceId()) } GET_DEVICE_TOKEN -> { getDeviceToken(result) } GET_DEVICE_PLATFORM -> { result.success(getDevicePlatform()) } else -> { result.notImplemented() } } } private fun getDeviceToken(result: MethodChannel.Result) { try { val token = getDeviceToken() result.success(token) } catch (e: Exception) { result.error("ERROR", e.message, e) } } private fun getPlayServicesError(): String { val resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) if (resultCode != ConnectionResult.SUCCESS) { return if (GoogleApiAvailability.getInstance().isUserResolvableError(resultCode)){ GoogleApiAvailability.getInstance().getErrorString(resultCode) } else { "This device is not supported" } } return "An error occurred preventing the use of push notifications" } }
Observera
Den här klassen implementerar den plattformsspecifika motsvarigheten
com.<your_organization>.pushdemo/deviceinstallation
för kanalen. Detta definierades i Flutter-delen av appen i DeviceInstallationService.dart. I det här fallet görs anropen från den gemensamma koden till den interna värden. Se till att ersätta <your_organization> med din egen organisation oavsett var detta används.Den här klassen tillhandahåller ett unikt ID (med Secure.AndroidId) som en del av registreringsnyttolasten för meddelandehubben.
Lägg till ytterligare en Kotlin-fil/-klass i mappen services med namnet NotificationRegistrationService och lägg sedan till följande kod.
package com.<your_organization>.pushdemo.services import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class NotificationRegistrationService { companion object { const val NOTIFICATION_REGISTRATION_CHANNEL = "com.<your_organization>.pushdemo/notificationregistration" const val REFRESH_REGISTRATION = "refreshRegistration" } private var notificationRegistrationChannel : MethodChannel constructor(flutterEngine: FlutterEngine) { notificationRegistrationChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, NotificationRegistrationService.NOTIFICATION_REGISTRATION_CHANNEL) } fun refreshRegistration() { notificationRegistrationChannel.invokeMethod(REFRESH_REGISTRATION, null) } }
Observera
Den här klassen implementerar den plattformsspecifika motsvarigheten
com.<your_organization>.pushdemo/notificationregistration
för kanalen. Detta definierades i Flutter-delen av appen i NotificationRegistrationService.dart. I det här fallet görs anropen från den interna värden till den gemensamma koden. Se återigen till att ersätta <your_organization> med din egen organisation oavsett var detta används.Lägg till ytterligare en Kotlin-fil/-klass i mappen services med namnet NotificationActionService och lägg sedan till följande kod.
package com.<your_organization>.pushdemo.services import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel class NotificationActionService { companion object { const val NOTIFICATION_ACTION_CHANNEL = "com.<your_organization>.pushdemo/notificationaction" const val TRIGGER_ACTION = "triggerAction" const val GET_LAUNCH_ACTION = "getLaunchAction" } private var notificationActionChannel : MethodChannel var launchAction : String? = null constructor(flutterEngine: FlutterEngine) { notificationActionChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, NotificationActionService.NOTIFICATION_ACTION_CHANNEL) notificationActionChannel.setMethodCallHandler { call, result -> handleNotificationActionCall(call, result) } } fun triggerAction(action: String) { notificationActionChannel.invokeMethod(NotificationActionService.TRIGGER_ACTION, action) } private fun handleNotificationActionCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { NotificationActionService.GET_LAUNCH_ACTION -> { result.success(launchAction) } else -> { result.notImplemented() } } } }
Observera
Den här klassen implementerar den plattformsspecifika motsvarigheten
com.<your_organization>.pushdemo/notificationaction
för kanalen. Det definierades i Flutter-delen av appen i NotificationActionService.dart. Anrop kan göras i båda riktningarna i det här fallet. Se till att ersätta <your_organization> med din egen organisation oavsett var detta används.Lägg till en ny Kotlin-fil/-klass i paketet com.your_organization.pushdemo<> med namnet PushNotificationsFirebaseMessagingService och implementera sedan med hjälp av följande kod.
package com.<your_organization>.pushdemo import android.os.Handler import android.os.Looper import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import com.<your_organization>.pushdemo.services.NotificationActionService import com.<your_organization>.pushdemo.services.NotificationRegistrationService class PushNotificationsFirebaseMessagingService : FirebaseMessagingService() { companion object { var token : String? = null var notificationRegistrationService : NotificationRegistrationService? = null var notificationActionService : NotificationActionService? = null } override fun onNewToken(token: String) { PushNotificationsFirebaseMessagingService.token = token notificationRegistrationService?.refreshRegistration() } override fun onMessageReceived(message: RemoteMessage) { message.data.let { Handler(Looper.getMainLooper()).post { notificationActionService?.triggerAction(it.getOrDefault("action", null)) } } } }
Observera
Den här klassen ansvarar för att hantera meddelanden när appen körs i förgrunden. Den anropar villkorligt triggerAction på NotificationActionService om en åtgärd ingår i meddelandenyttolasten som tas emot i onMessageReceived. Detta anropar även refreshRegistration på NotificationRegistrationService när Firebase-token återskapas genom att åsidosätta funktionen onNewToken .
Var återigen noga med att ersätta <your_organization> med din egen organisation oavsett var den används.
I AndroidManifest.xml (app>src>main) lägger du till PushNotificationsFirebaseMessagingService längst ned i programelementet med
com.google.firebase.MESSAGING_EVENT
avsiktsfiltret.<manifest> <application> <!-- EXISTING MANIFEST CONTENT --> <service android:name="com.<your_organization>.pushdemo.PushNotificationsFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> </application> </manifest>
När du är tillbaka i DeviceInstallationService kontrollerar du att följande importer finns överst i filen.
package com.<your_organization>.pushdemo import com.<your_organization>.pushdemo.services.PushNotificationsFirebaseMessagingService
Observera
Ersätt <your_organization> med ditt eget organisationsvärde.
Uppdatera platshållartexten Placeholder_Get_Value_From_FirebaseMessagingService_Implementation för att hämta tokenvärdet från PushNotificationFirebaseMessagingService.
fun getDeviceToken() : String { if(!playServicesAvailable) { throw Exception(getPlayServicesError()) } // Get token from the PushNotificationsFirebaseMessagingService.token field. val token = PushNotificationsFirebaseMessagingService.token if (token.isNullOrBlank()) { throw Exception("Unable to resolve token for FCM.") } return token }
I MainActivity kontrollerar du att följande importer finns överst i filen.
package com.<your_organization>.pushdemo import android.content.Intent import android.os.Bundle import com.google.android.gms.tasks.OnCompleteListener import com.google.firebase.iid.FirebaseInstanceId import com.<your_organization>.pushdemo.services.DeviceInstallationService import com.<your_organization>.pushdemo.services.NotificationActionService import com.<your_organization>.pushdemo.services.NotificationRegistrationService import io.flutter.embedding.android.FlutterActivity
Observera
Ersätt <your_organization> med ditt eget organisationsvärde.
Lägg till en variabel för att lagra en referens till DeviceInstallationService.
private lateinit var deviceInstallationService: DeviceInstallationService
Lägg till en funktion med namnet processNotificationActions för att kontrollera om en avsikt har ett extra värde med namnet action. Villkorlig utlösa den åtgärden eller lagra den för användning senare om åtgärden bearbetas under appstart.
private fun processNotificationActions(intent: Intent, launchAction: Boolean = false) { if (intent.hasExtra("action")) { var action = intent.getStringExtra("action"); if (action.isNotEmpty()) { if (launchAction) { PushNotificationsFirebaseMessagingService.notificationActionService?.launchAction = action } else { PushNotificationsFirebaseMessagingService.notificationActionService?.triggerAction(action) } } } }
Åsidosätt funktionen onNewIntent för att anropa processNotificationActions.
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) processNotificationActions(intent) }
Observera
Eftersom LaunchMode för MainActivity har angetts till SingleTop skickas en avsikt till den befintliga aktivitetsinstansen via funktionen onNewIntent i stället för onCreate-funktionen . Därför måste du hantera en inkommande avsikt i både onCreate - och onNewIntent-funktionerna .
Åsidosätt funktionen onCreate och ställ in deviceInstallationService på en ny instans av DeviceInstallationService.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) flutterEngine?.let { deviceInstallationService = DeviceInstallationService(context, it) } }
Ange egenskaperna notificationActionService och notificationRegistrationService på PushNotificationFirebaseMessagingServices.
flutterEngine?.let { deviceInstallationService = DeviceInstallationService(context, it) PushNotificationsFirebaseMessagingService.notificationActionService = NotificationActionService(it) PushNotificationsFirebaseMessagingService.notificationRegistrationService = NotificationRegistrationService(it) }
I samma funktion anropar villkorligt FirebaseInstanceId.getInstance().instanceId. Implementera OnCompleteListener för att ange det resulterande tokenvärdet på PushNotificationFirebaseMessagingService innan du anroparrefreshRegistration.
if(deviceInstallationService?.playServicesAvailable) { FirebaseInstanceId.getInstance().instanceId .addOnCompleteListener(OnCompleteListener { task -> if (!task.isSuccessful) return@OnCompleteListener PushNotificationsFirebaseMessagingService.token = task.result?.token PushNotificationsFirebaseMessagingService.notificationRegistrationService?.refreshRegistration() }) }
Fortfarande i onCreate anropar du processNotificationActions i slutet av funktionen. Använd true för argumentet launchAction för att ange att den här åtgärden bearbetas under appstarten.
processNotificationActions(this.intent, true)
Observera
Du måste registrera om appen varje gång du kör den och stoppa den från en felsökningssession för att fortsätta ta emot push-meddelanden.
Konfigurera det interna iOS-projektet för push-meddelanden
Konfigurera körningsmålet och Info.plist
I Visual Studio Codekontrollerar + du Klicka på mappen ios och välj sedan Öppna i Xcode.
I Xcode klickar du på Runner ( xcodeproj högst upp, inte mappen) och väljer sedan Runner-målet och sedan Signering & Funktioner. När Du har valt Alla byggkonfigurationer väljer du ditt utvecklarkonto för teamet. Kontrollera att alternativet "Hantera signering automatiskt" är markerat och att signeringscertifikatet och etableringsprofilen väljs automatiskt.
Observera
Om du inte ser det nya etableringsprofilvärdet kan du prova att uppdatera profilerna för signeringsidentiteten genom att välja Konto för Xcode-inställningar>> och sedan välja knappen Ladda ned manuella profiler för att ladda ned profilerna.
Klicka på + Kapacitet och sök sedan efter push-meddelanden. Dubbelklicka på Push-meddelanden för att lägga till den här funktionen.
Öppna Info.plist och ange Lägsta systemversion till 13.0.
Observera
Endast de enheter som kör iOS 13.0 och senare stöds i den här självstudien, men du kan utöka den för att stödja enheter som kör äldre versioner.
Öppna Runner.entitlements och se till att INSTÄLLNINGEN APS-miljö är inställd på utveckling.
Hantera push-meddelanden för iOS
Kontroll + Klicka på mappen Runner (i Runner-projektet) och välj sedan Ny grupp med tjänster som namn.
Kontroll + Klicka på mappen Tjänster och välj sedan Ny fil.... Välj sedan Swift-fil och klicka på Nästa. Ange DeviceInstallationService som namn och klicka sedan på Skapa.
Implementera DeviceInstallationService.swift med hjälp av följande kod.
import Foundation class DeviceInstallationService { enum DeviceRegistrationError: Error { case notificationSupport(message: String) } var token : Data? = nil let DEVICE_INSTALLATION_CHANNEL = "com.<your_organization>.pushdemo/deviceinstallation" let GET_DEVICE_ID = "getDeviceId" let GET_DEVICE_TOKEN = "getDeviceToken" let GET_DEVICE_PLATFORM = "getDevicePlatform" private let deviceInstallationChannel : FlutterMethodChannel var notificationsSupported : Bool { get { if #available(iOS 13.0, *) { return true } else { return false } } } init(withBinaryMessenger binaryMessenger : FlutterBinaryMessenger) { deviceInstallationChannel = FlutterMethodChannel(name: DEVICE_INSTALLATION_CHANNEL, binaryMessenger: binaryMessenger) deviceInstallationChannel.setMethodCallHandler(handleDeviceInstallationCall) } func getDeviceId() -> String { return UIDevice.current.identifierForVendor!.description } func getDeviceToken() throws -> String { if(!notificationsSupported) { let notificationSupportError = getNotificationsSupportError() throw DeviceRegistrationError.notificationSupport(message: notificationSupportError) } if (token == nil) { throw DeviceRegistrationError.notificationSupport(message: "Unable to resolve token for APNS.") } return token!.reduce("", {$0 + String(format: "%02X", $1)}) } func getDevicePlatform() -> String { return "apns" } private func handleDeviceInstallationCall(call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case GET_DEVICE_ID: result(getDeviceId()) case GET_DEVICE_TOKEN: getDeviceToken(result: result) case GET_DEVICE_PLATFORM: result(getDevicePlatform()) default: result(FlutterMethodNotImplemented) } } private func getDeviceToken(result: @escaping FlutterResult) { do { let token = try getDeviceToken() result(token) } catch let error { result(FlutterError(code: "UNAVAILABLE", message: error.localizedDescription, details: nil)) } } private func getNotificationsSupportError() -> String { if (!notificationsSupported) { return "This app only supports notifications on iOS 13.0 and above. You are running \(UIDevice.current.systemVersion)" } return "An error occurred preventing the use of push notifications." } }
Observera
Den här klassen implementerar den plattformsspecifika motsvarigheten
com.<your_organization>.pushdemo/deviceinstallation
för kanalen. Detta definierades i Flutter-delen av appen i DeviceInstallationService.dart. I det här fallet görs anropen från den gemensamma koden till den interna värden. Se till att ersätta <your_organization> med din egen organisation oavsett var detta används.Den här klassen ger ett unikt ID (med värdet UIDevice.identifierForVendor ) som en del av registreringsnyttolasten för notification hub.
Lägg till ytterligare en Swift-fil i mappen Tjänster med namnet NotificationRegistrationService och lägg sedan till följande kod.
import Foundation class NotificationRegistrationService { let NOTIFICATION_REGISTRATION_CHANNEL = "com.<your_organization>.pushdemo/notificationregistration" let REFRESH_REGISTRATION = "refreshRegistration" private let notificationRegistrationChannel : FlutterMethodChannel init(withBinaryMessenger binaryMessenger : FlutterBinaryMessenger) { notificationRegistrationChannel = FlutterMethodChannel(name: NOTIFICATION_REGISTRATION_CHANNEL, binaryMessenger: binaryMessenger) } func refreshRegistration() { notificationRegistrationChannel.invokeMethod(REFRESH_REGISTRATION, arguments: nil) } }
Observera
Den här klassen implementerar den plattformsspecifika motsvarigheten
com.<your_organization>.pushdemo/notificationregistration
för kanalen. Detta definierades i Flutter-delen av appen i NotificationRegistrationService.dart. I det här fallet görs anropen från den interna värden till den gemensamma koden. Se återigen till att ersätta <your_organization> med din egen organisation oavsett var detta används.Lägg till ytterligare en Swift-fil i mappen Services med namnet NotificationActionService och lägg sedan till följande kod.
import Foundation class NotificationActionService { let NOTIFICATION_ACTION_CHANNEL = "com.<your_organization>.pushdemo/notificationaction" let TRIGGER_ACTION = "triggerAction" let GET_LAUNCH_ACTION = "getLaunchAction" private let notificationActionChannel: FlutterMethodChannel var launchAction: String? = nil init(withBinaryMessenger binaryMessenger: FlutterBinaryMessenger) { notificationActionChannel = FlutterMethodChannel(name: NOTIFICATION_ACTION_CHANNEL, binaryMessenger: binaryMessenger) notificationActionChannel.setMethodCallHandler(handleNotificationActionCall) } func triggerAction(action: String) { notificationActionChannel.invokeMethod(TRIGGER_ACTION, arguments: action) } private func handleNotificationActionCall(call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case GET_LAUNCH_ACTION: result(launchAction) default: result(FlutterMethodNotImplemented) } } }
Observera
Den här klassen implementerar den plattformsspecifika motsvarigheten
com.<your_organization>.pushdemo/notificationaction
för kanalen. Det definierades i Flutter-delen av appen i NotificationActionService.dart. Anrop kan göras i båda riktningarna i det här fallet. Se till att ersätta <your_organization> med din egen organisation oavsett var detta används.I AppDelegate.swift lägger du till variabler för att lagra en referens till de tjänster som du skapade tidigare.
var deviceInstallationService : DeviceInstallationService? var notificationRegistrationService : NotificationRegistrationService? var notificationActionService : NotificationActionService?
Lägg till en funktion med namnet processNotificationActions för bearbetning av meddelandedata. Villkorlig utlösa den åtgärden eller lagra den för användning senare om åtgärden bearbetas under appstart.
func processNotificationActions(userInfo: [AnyHashable : Any], launchAction: Bool = false) { if let action = userInfo["action"] as? String { if (launchAction) { notificationActionService?.launchAction = action } else { notificationActionService?.triggerAction(action: action) } } }
Åsidosätt funktionen didRegisterForRemoteNotificationsWithDeviceToken och ange tokenvärdet för DeviceInstallationService. Anropa sedan refreshRegistration på NotificationRegistrationService.
override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { deviceInstallationService?.token = deviceToken notificationRegistrationService?.refreshRegistration() }
Åsidosätt funktionen didReceiveRemoteNotification och skicka userInfo-argumentet till funktionen processNotificationActions .
override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) { processNotificationActions(userInfo: userInfo) }
Åsidosätt funktionen didFailToRegisterForRemoteNotificationsWithError för att logga felet.
override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print(error); }
Observera
Det här är en platshållare. Du bör implementera korrekt loggning och felhantering för produktionsscenarier.
I didFinishLaunchingWithOptions instansierar du variablerna deviceInstallationService, notificationRegistrationService och notificationActionService .
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController deviceInstallationService = DeviceInstallationService(withBinaryMessenger: controller.binaryMessenger) notificationRegistrationService = NotificationRegistrationService(withBinaryMessenger: controller.binaryMessenger) notificationActionService = NotificationActionService(withBinaryMessenger: controller.binaryMessenger)
I samma funktion begär du auktorisering villkorligt och registrerar dig för fjärrmeddelanden.
if #available(iOS 13.0, *) { UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in if (granted) { DispatchQueue.main.async { let pushSettings = UIUserNotificationSettings(types: [.alert, .sound, .badge], categories: nil) application.registerUserNotificationSettings(pushSettings) application.registerForRemoteNotifications() } } } }
Om launchOptions innehåller remoteNotification-nyckelnanropar du processNotificationActions i slutet av funktionen didFinishLaunchingWithOptions . Skicka det resulterande userInfo-objektet och använd true för argumentet launchAction . Ett sant värde anger att åtgärden bearbetas under appstarten.
if let userInfo = launchOptions?[.remoteNotification] as? [AnyHashable : Any] { processNotificationActions(userInfo: userInfo, launchAction: true) }
Testa lösningen
Nu kan du testa att skicka meddelanden via serverdelstjänsten.
Skicka ett testmeddelande
Öppna en ny flik i Postman.
Ställ in begäran på POST och ange följande adress:
https://<app_name>.azurewebsites.net/api/notifications/requests
Om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel måste du konfigurera begärandehuvudena så att de inkluderar apikey-värdet .
Nyckel Värde apikey <your_api_key> Välj raw-alternativet för brödtexten och välj sedan JSON i listan med formatalternativ och ta sedan med lite platshållar-JSON-innehåll :
{ "text": "Message from Postman!", "action": "action_a" }
Välj knappen Kod , som finns under knappen Spara längst upp till höger i fönstret. Begäran bör se ut ungefär som i följande exempel när den visas för HTML (beroende på om du har inkluderat ett apikey-huvud ):
POST /api/notifications/requests HTTP/1.1 Host: https://<app_name>.azurewebsites.net apikey: <your_api_key> Content-Type: application/json { "text": "Message from backend service", "action": "action_a" }
Kör PushDemo-programmet på en eller båda målplattformarna (Android och iOS).
Observera
Om du testar på Android kontrollerar du att du inte kör i Felsökning, eller om appen har distribuerats genom att köra programmet, tvingar du appen att stänga och starta den igen från startprogrammet.
Tryck på knappen Registrera i PushDemo-appen.
I Postman stänger du fönstret Generera kodfragment (om du inte redan gjort det) och klickar sedan på knappen Skicka .
Kontrollera att du får ett 200 OK-svar i Postman och att aviseringen visas i appen som visar åtgärden ÅtgärdA mottagen.
Stäng PushDemo-appen och klicka sedan på knappen Skicka igen i Postman.
Kontrollera att du får ett 200 OK-svar i Postman igen. Kontrollera att ett meddelande visas i meddelandefältet för PushDemo-appen med rätt meddelande.
Tryck på meddelandet för att bekräfta att appen öppnas och att åtgärden ActionA har tagit emot aviseringen.
När du är tillbaka i Postman ändrar du den tidigare begärandetexten för att skicka ett tyst meddelande som anger action_b i stället för action_a för åtgärdsvärdet .
{ "action": "action_b", "silent": true }
När appen fortfarande är öppen klickar du på knappen Skicka i Postman.
Kontrollera att du får ett 200 OK-svar i Postman och att aviseringen visas i appen som visar actionB-åtgärden som tagits emot i stället för actionA-åtgärden som tagits emot.
Stäng PushDemo-appen och klicka sedan på knappen Skicka igen i Postman.
Kontrollera att du får ett 200 OK-svar i Postman och att det tysta meddelandet inte visas i meddelandefältet.
Felsökning
Inget svar från serverdelstjänsten
När du testar lokalt kontrollerar du att serverdelstjänsten körs och använder rätt port.
Om du testar mot Azure API-appen kontrollerar du att tjänsten körs och har distribuerats och har startats utan fel.
Kontrollera att du har angett basadressen korrekt i Postman eller i mobilappkonfigurationen när du testar via klienten. Basadressen bör vara https://<api_name>.azurewebsites.net/
eller https://localhost:5001/
när den testas lokalt.
Får inte meddelanden på Android när en felsökningssession har startats eller stoppats
Se till att du registrerar dig igen när du har startat eller stoppat en felsökningssession. Felsökningsprogrammet gör att en ny Firebase-token genereras. Installationen av meddelandehubben måste också uppdateras.
Ta emot en 401-statuskod från serverdelstjänsten
Kontrollera att du anger apikey-begärandehuvudet och att det här värdet matchar det som du hade konfigurerat för serverdelstjänsten.
Om du får det här felet när du testar lokalt kontrollerar du att nyckelvärdet som du definierade i klientkonfigurationen matchar användarinställningsvärdet Authentication:ApiKey som används av API:et.
Om du testar med en API-app kontrollerar du att nyckelvärdet i klientkonfigurationsfilen matchar programinställningen Authentication:ApiKey som du använder i API-appen.
Observera
Om du har skapat eller ändrat den här inställningen efter att du har distribuerat serverdelstjänsten måste du starta om tjänsten för att den ska börja gälla.
Om du väljer att inte slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel kontrollerar du att du inte har tillämpat attributet Auktorisera för klassen NotificationsController .
Ta emot en 404-statuskod från serverdelstjänsten
Kontrollera att metoden för slutpunkt och HTTP-begäran är korrekt. Slutpunkterna bör till exempel vara:
- [PUT]
https://<api_name>.azurewebsites.net/api/notifications/installations
- [DELETE]
https://<api_name>.azurewebsites.net/api/notifications/installations/<installation_id>
- [POST]
https://<api_name>.azurewebsites.net/api/notifications/requests
Eller när du testar lokalt:
- [PUT]
https://localhost:5001/api/notifications/installations
- [DELETE]
https://localhost:5001/api/notifications/installations/<installation_id>
- [POST]
https://localhost:5001/api/notifications/requests
När du anger basadressen i klientappen ska du se till att den slutar med en /
. Basadressen bör vara https://<api_name>.azurewebsites.net/
eller https://localhost:5001/
när den testas lokalt.
Det går inte att registrera och ett felmeddelande för meddelandehubben visas
Kontrollera att testenheten har nätverksanslutning. Bestäm sedan http-svarsstatuskoden genom att ange en brytpunkt för att granska egenskapsvärdet StatusCode i HttpResponse.
Granska de tidigare felsökningsförslagen där det är tillämpligt baserat på statuskoden.
Ange en brytpunkt på raderna som returnerar dessa specifika statuskoder för respektive API. Försök sedan att anropa serverdelstjänsten när du felsöker lokalt.
Kontrollera att serverdelstjänsten fungerar som förväntat via Postman med lämplig nyttolast. Använd den faktiska nyttolasten som skapats av klientkoden för den aktuella plattformen.
Granska de plattformsspecifika konfigurationsavsnitten för att se till att inga steg har missats. Kontrollera att lämpliga värden matchas för installation id
variablerna och token
för lämplig plattform.
Det går inte att lösa ett ID för enhetens felmeddelande visas
Granska de plattformsspecifika konfigurationsavsnitten för att se till att inga steg har missats.
Relaterade länkar
- Översikt över Azure Notification Hubs
- Installera Flutter på macOS
- Installera Flutter i Windows
- Notification Hubs SDK för serverdelsåtgärder
- Notification Hubs SDK på GitHub
- Registrera med programmets serverdel
- Registreringshantering
- Arbeta med taggar
- Arbeta med anpassade mallar
Nästa steg
Nu bör du ha en grundläggande Flutter-app ansluten till en meddelandehubb via en serverdelstjänst och kan skicka och ta emot meddelanden.
Du behöver förmodligen anpassa exemplet som används i den här självstudien så att det passar ditt eget scenario. Vi rekommenderar också att du implementerar mer robust felhantering, omprövningslogik och loggning.
Visual Studio App Center kan snabbt införlivas i mobilappar som tillhandahåller analys och diagnostik för felsökning.