Tutorial: Mengirim pemberitahuan push ke pengguna tertentu menggunakan Azure Notification Hubs

Tutorial ini memperlihatkan cara menggunakan Azure Notification Hubs untuk mengirim pemberitahuan push ke pengguna aplikasi tertentu pada perangkat tertentu. Backend ASP.NET WebAPI digunakan untuk mengautentikasi klien dan membuat pemberitahuan, seperti yang ditunjukkan dalam artikel panduan Mendaftar dari backend aplikasi Anda.

Dalam tutorial ini, lakukan langkah-langkah berikut:

  • Membuat proyek WebAPI
  • Mengautentikasi klien ke backend WebAPI
  • Mendaftar untuk pemberitahuan dengan menggunakan backend WebAPI
  • Mengirim pemberitahuan dari backend WebAPI
  • Terbitkan backend WebAPI baru
  • Modifikasi aplikasi iOS Anda
  • Uji aplikasi

Prasyarat

Tutorial ini mengasumsikan bahwa Anda telah membuat dan mengonfigurasi hub notifikasi seperti yang dijelaskan di Kirim pemberitahuan push ke aplikasi iOS menggunakan Azure Notification Hubs. Tutorial ini juga merupakan prasyarat untuk tutorial Secure Push (iOS). Jika Anda ingin menggunakan Mobile Apps sebagai layanan backend Anda, lihat Mulai Mobile Apps dengan Push.

Buat proyek WebAPI

Bagian berikut membahas pembuatan backend ASP.NET WebAPI baru. Proses ini memiliki tiga tujuan utama:

  • Mengautentikasi klien: Anda menambahkan penangan pesan untuk mengautentikasi permintaan klien dan mengaitkan pengguna dengan permintaan.
  • Daftar untuk pemberitahuan dengan menggunakan backend WebAPI: Anda menambahkan pengontrol untuk menangani pendaftaran baru untuk perangkat klien untuk menerima pemberitahuan. Nama pengguna yang diautentikasi secara otomatis ditambahkan ke pendaftaran sebagai tag.
  • Kirim pemberitahuan ke klien: Anda menambahkan pengontrol untuk menyediakan cara bagi pengguna untuk memicu dorongan aman ke perangkat dan klien yang terkait dengan tag.

Buat backend API web ASP.NET Core 6.0 dengan melakukan tindakan berikut:

Untuk memeriksa, mulai Visual Studio. Pada menu Alat, pilih Ekstensi dan Pembaruan. Cari NuGet Package Manager di Visual Studio versi Anda, dan pastikan Anda memiliki versi terbaru. Jika versi Anda bukan versi terbaru, hapus instalasinya, lalu instal ulang NuGet Package Manager.

Cuplikan layar kotak dialog Ekstensi dan Pembaruan dengan paket NuGet Package manage for Visual Studios package highlighted.

Catatan

Pastikan Anda telah memasang Visual Studio Azure SDK untuk penyebaran situs web.

  1. Mulai Visual Studio atau Visual Studio Express.

  2. Pilih Penjelajah Server, dan masuk ke akun Azure Anda. Untuk membuat sumber daya situs web di akun Anda, Anda harus masuk.

  3. Di menu File Visual Studio, pilih Baru>Proyek.

  4. Masukkan Web API di kotak pencarian.

  5. Pilih templat proyek ASP.NET Core Web API, lalu pilih Berikutnya.

  6. Dalam dialog Konfigurasikan proyek baru, beri nama proyek AppBackend, lalu pilih Berikutnya.

  7. Dalam dialog Informasi tambahan:

    • Konfirmasi bahwa Kerangka Kerja adalah .NET 6.0 (Dukungan jangka panjang).
    • Konfirmasi bahwa kotak centang untuk Gunakan pengontrol (hapus centang untuk menggunakan API minimal) dicentang.
    • Hapus centang Aktifkan dukungan OpenAPI.
    • Pilih Buat.

Menghapus file templat WeatherForecast

  1. Hapus contoh file WeatherForecast.cs dan Controllers/WeatherForecastController.cs dari proyek AppBackend baru.
  2. Buka Properties\launchSettings.json.
  3. Ubah properti launchUrl dari weatherforcast menjadi appbackend.

Di jendela Konfigurasikan Microsoft Azure Web App, pilih langganan lalu, di daftar paket Azure App Service, lakukan salah satu tindakan berikut ini:

  • Pilih paket Azure App Service yang telah Anda buat.
  • Pilih Buat paket layanan aplikasi baru, lalu buat paket.

Anda tidak memerlukan database untuk tutorial ini. Setelah memilih paket layanan aplikasi, pilih OK untuk membuat proyek.

Jendela Konfigurasikan Microsoft Azure Web App

Jika Anda tidak melihat halaman ini untuk mengonfigurasi paket layanan aplikasi, lanjutkan dengan tutorial. Anda dapat mengonfigurasinya saat menerbitkan aplikasi nanti.

Autentikasi klien ke backened WebAPI

Di bagian ini, Anda membuat kelas message-handler baru bernama AuthenticationTestHandler untuk backend baru. Kelas ini berasal dari DelegatingHandler dan ditambahkan sebagai penangan pesan sehingga dapat memproses semua permintaan yang masuk ke backend.

  1. Di Penjelajah Solusi, klik kanan proyek AppBackend, pilih Tambahkan, lalu pilih Kelas.

  2. Beri nama kelas baru AuthenticationTestHandler.cs, lalu pilih Tambahkan untuk menghasilkan kelas. Kelas ini mengautentikasi pengguna dengan menggunakan Otentikasi Dasar untuk kesederhanaan. Aplikasi Anda dapat menggunakan skema autentikasi apa pun.

  3. Di AuthenticationTestHandler.cs, tambahkan pernyataan using berikut:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. Di AuthenticationTestHandler.cs, ganti AuthenticationTestHandler definisi kelas dengan kode berikut:

    Handler mengotorisasi permintaan ketika tiga kondisi berikut ini benar:

    • Permintaan ini menyertakan header Otorisasi.
    • Permintaan ini menggunakan autentikasi dasar.
    • String nama pengguna dan string kata sandi adalah string yang sama.

    Jika tidak, permintaan ditolak. Autentikasi ini bukan pendekatan autentikasi dan otorisasi yang benar. Ini hanya contoh sederhana untuk tutorial ini.

    Jika pesan permintaan diautentikasi dan diotorisasi oleh AuthenticationTestHandler, pengguna otentikasi dasar dilampirkan ke permintaan saat ini di HttpContext. Informasi pengguna di HttpContext akan digunakan oleh pengontrol lain (RegisterController) nantinya untuk menambahkan tag ke permintaan pendaftaran pemberitahuan.

    public class AuthenticationTestHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var authorizationHeader = request.Headers.GetValues("Authorization").First();
    
            if (authorizationHeader != null && authorizationHeader
                .StartsWith("Basic ", StringComparison.InvariantCultureIgnoreCase))
            {
                string authorizationUserAndPwdBase64 =
                    authorizationHeader.Substring("Basic ".Length);
                string authorizationUserAndPwd = Encoding.Default
                    .GetString(Convert.FromBase64String(authorizationUserAndPwdBase64));
                string user = authorizationUserAndPwd.Split(':')[0];
                string password = authorizationUserAndPwd.Split(':')[1];
    
                if (VerifyUserAndPwd(user, password))
                {
                    // Attach the new principal object to the current HttpContext object
                    HttpContext.Current.User =
                        new GenericPrincipal(new GenericIdentity(user), new string[0]);
                    System.Threading.Thread.CurrentPrincipal =
                        System.Web.HttpContext.Current.User;
                }
                else return Unauthorized();
            }
            else return Unauthorized();
    
            return base.SendAsync(request, cancellationToken);
        }
    
        private bool VerifyUserAndPwd(string user, string password)
        {
            // This is not a real authentication scheme.
            return user == password;
        }
    
        private Task<HttpResponseMessage> Unauthorized()
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }
    }
    

    Catatan

    Catatan keamanan: Kelas AuthenticationTestHandler tidak menyediakan autentikasi yang benar. Ini hanya digunakan untuk meniru autentikasi dasar dan tidak aman. Anda harus menerapkan mekanisme autentikasi aman dalam aplikasi dan layanan produksi Anda.

  5. Untuk mendaftarkan penangan pesan, tambahkan kode berikut di akhir metode Register di file Program.cs:

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. Simpan perubahan Anda.

Daftar untuk pemberitahuan dengan menggunakan bakcend WebAPI

Di bagian ini, Anda menambahkan pengontrol baru ke backend WebAPI untuk menangani permintaan untuk mendaftarkan pengguna dan perangkat untuk pemberitahuan dengan menggunakan pustaka klien untuk hub notifikasi. Pengontrol menambahkan tag pengguna untuk pengguna yang diautentikasi dan dilampirkan ke HttpContext oleh AuthenticationTestHandler. Tag memiliki format string, "username:<actual username>".

  1. Pada Penjelajah Solusi, klik kanan proyek AppBackenddan pilih Manage NuGet Packages.

  2. Di panel kiri, pilih Online lalu, dalam kotak Pencarian, ketik Microsoft.Azure.NotificationHubs.

  3. Dalam daftar hasil, pilih Microsoft Azure Notification Hubs, lalu pilih Pasang. Selesaikan penginstalan, lalu tutup jendela NuGet Package Manager.

    Tindakan ini menambahkan referensi ke SDK Microsoft Azure Notification Hubs dengan menggunakan paket Microsoft.Azure.Notification Hubs NuGet.

  4. Buat file kelas baru yang mewakili koneksi dengan hub notifikasi yang digunakan untuk mengirim notifikasi. Di Penjelajah Solusi, klik kanan folder Model, pilih Tambahkan, lalu pilih Kelas. Beri nama kelas baruNotifications .cs, lalu pilih Tambahkan untuk membuat kelas.

    Jendela Tambahkan Item Baru

  5. Di Nitifications.cs, tambahkan usingpernyataan berikut di bagian atas file:

    using Microsoft.Azure.NotificationHubs;
    
  6. Ganti Notifications definisi kelas dengan kode berikut, dan ganti dua placeholder dengan string koneksi (dengan akses penuh) untuk hub notifikasi dan nama hub Anda (tersedia di portal Microsoft Azure):

    public class Notifications
    {
        public static Notifications Instance = new Notifications();
    
        public NotificationHubClient Hub { get; set; }
    
        private Notifications() {
            Hub = NotificationHubClient.CreateClientFromConnectionString("<your hub's DefaultFullSharedAccessSignature>",
                                                                            "<hub name>");
        }
    }
    

    Penting

    Masukkan nama dan DefaultListenSharedAccessSignature hub Anda sebelum melanjutkan.

  7. Selanjutnya, buat pengontrol baru bernama RegisterController. Di Penjelajah Solusi,klik kanan folder Pengontrol, pilih Tambahkan, lalu pilih Pengontrol.

  8. Pilih Pengontrol API - Kosong, lalu pilih Tambahkan.

  9. Dalam kotak Nama pengontrol, ketik RegisterController untuk memberi nama kelas baru, lalu pilih Tambahkan.

    Jendela Tambahkan Pengontrol.

  10. Di RegisterController.cs, tambahkan pernyataan using berikut:

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. Tambahkan kode berikut di dalam RegisterController definisi kelas. Dalam kode ini, Anda menambahkan tag pengguna untuk pengguna yang dilampirkan ke HttpContext. Pengguna diautentikasi dan dilampirkan ke HttpContext oleh filter pesan yang Anda tambahkan, AuthenticationTestHandler. Anda juga dapat menambahkan pemeriksaan opsional untuk memverifikasi bahwa pengguna memiliki hak untuk mendaftarkan tag yang diminta.

    private NotificationHubClient hub;
    
    public RegisterController()
    {
        hub = Notifications.Instance.Hub;
    }
    
    public class DeviceRegistration
    {
        public string Platform { get; set; }
        public string Handle { get; set; }
        public string[] Tags { get; set; }
    }
    
    // POST api/register
    // This creates a registration id
    public async Task<string> Post(string handle = null)
    {
        string newRegistrationId = null;
    
        // make sure there are no existing registrations for this push handle (used for iOS and Android)
        if (handle != null)
        {
            var registrations = await hub.GetRegistrationsByChannelAsync(handle, 100);
    
            foreach (RegistrationDescription registration in registrations)
            {
                if (newRegistrationId == null)
                {
                    newRegistrationId = registration.RegistrationId;
                }
                else
                {
                    await hub.DeleteRegistrationAsync(registration);
                }
            }
        }
    
        if (newRegistrationId == null) 
            newRegistrationId = await hub.CreateRegistrationIdAsync();
    
        return newRegistrationId;
    }
    
    // PUT api/register/5
    // This creates or updates a registration (with provided channelURI) at the specified id
    public async Task<HttpResponseMessage> Put(string id, DeviceRegistration deviceUpdate)
    {
        RegistrationDescription registration = null;
        switch (deviceUpdate.Platform)
        {
            case "mpns":
                registration = new MpnsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "wns":
                registration = new WindowsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "apns":
                registration = new AppleRegistrationDescription(deviceUpdate.Handle);
                break;
            case "fcm":
                registration = new FcmRegistrationDescription(deviceUpdate.Handle);
                break;
            default:
                throw new HttpResponseException(HttpStatusCode.BadRequest);
        }
    
        registration.RegistrationId = id;
        var username = HttpContext.Current.User.Identity.Name;
    
        // add check if user is allowed to add these tags
        registration.Tags = new HashSet<string>(deviceUpdate.Tags);
        registration.Tags.Add("username:" + username);
    
        try
        {
            await hub.CreateOrUpdateRegistrationAsync(registration);
        }
        catch (MessagingException e)
        {
            ReturnGoneIfHubResponseIsGone(e);
        }
    
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    // DELETE api/register/5
    public async Task<HttpResponseMessage> Delete(string id)
    {
        await hub.DeleteRegistrationAsync(id);
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    private static void ReturnGoneIfHubResponseIsGone(MessagingException e)
    {
        var webex = e.InnerException as WebException;
        if (webex.Status == WebExceptionStatus.ProtocolError)
        {
            var response = (HttpWebResponse)webex.Response;
            if (response.StatusCode == HttpStatusCode.Gone)
                throw new HttpRequestException(HttpStatusCode.Gone.ToString());
        }
    }
    
  12. Simpan perubahan Anda.

Kirim pemberitahuan dari backend WebAPI

Di bagian ini, Anda menambahkan pengontrol baru yang mengekspos cara perangkat klien untuk mengirim pemberitahuan. Pemberitahuan didasarkan pada tag nama pengguna yang menggunakan Azure Notification Hubs .NET Library di ASP.NET backend WebAPI.

  1. Buat pengontrol baru lainnya bernama NotificationsController dengan cara yang sama seperti Anda membuat RegisterController di bagian sebelumnya.

  2. Di NotificationsController.cs, tambahkan using pernyataan berikut:

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. Tambahkan metode berikut ke kelas NotificationsController:

    Kode ini mengirimkan jenis notifikasi yang didasarkan pada parameter Layanan Pemberitahuan Platform pns (PNS). Nilai to_tag digunakan untuk mengatur tag nama pengguna pada pesan. Tag ini harus cocok dengan tag nama pengguna dari pendaftaran hub notifikasi aktif. Pesan pemberitahuan ditarik dari isi permintaan KIRIMAN dan diformat untuk target PNS.

    Berdasarkan PNS yang digunakan oleh perangkat yang didukung untuk menerima notifikasi, notifikasi didukung oleh berbagai format. Misalnya, pada perangkat Windows, Anda mungkin menggunakan pemberitahuan toast dengan WNS yang tidak didukung langsung oleh PNS lain. Dalam hal demikian, backend Anda perlu memformat pemberitahuan ke pemberitahuan yang didukung untuk PNS perangkat yang Anda rencanakan untuk didukung. Kemudian gunakan API kirim yang sesuai pada kelas NotificationHubClient.

    public async Task<HttpResponseMessage> Post(string pns, [FromBody]string message, string to_tag)
    {
        var user = HttpContext.Current.User.Identity.Name;
        string[] userTag = new string[2];
        userTag[0] = "username:" + to_tag;
        userTag[1] = "from:" + user;
    
        Microsoft.Azure.NotificationHubs.NotificationOutcome outcome = null;
        HttpStatusCode ret = HttpStatusCode.InternalServerError;
    
        switch (pns.ToLower())
        {
            case "wns":
                // Windows 8.1 / Windows Phone 8.1
                var toast = @"<toast><visual><binding template=""ToastText01""><text id=""1"">" + 
                            "From " + user + ": " + message + "</text></binding></visual></toast>";
                outcome = await Notifications.Instance.Hub.SendWindowsNativeNotificationAsync(toast, userTag);
                break;
            case "apns":
                // iOS
                var alert = "{\"aps\":{\"alert\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendAppleNativeNotificationAsync(alert, userTag);
                break;
            case "fcm":
                // Android
                var notif = "{ \"data\" : {\"message\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendFcmNativeNotificationAsync(notif, userTag);
                break;
        }
    
        if (outcome != null)
        {
            if (!((outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Abandoned) ||
                (outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Unknown)))
            {
                ret = HttpStatusCode.OK;
            }
        }
    
        return Request.CreateResponse(ret);
    }
    
  4. Untuk menjalankan aplikasi dan memastikan keakuratan pekerjaan Anda sejauh ini, pilih kunci F5. Aplikasi ini membuka browser web, dan ditampilkan di beranda ASP.NET.

Terbitkan backend WebAPI baru

Selanjutnya, Anda menerapkan aplikasi ke situs web Azure untuk membuatnya dapat diakses dari semua perangkat.

  1. Klik kanan proyek AppBackend, lalu pilih Terbitkan.

  2. Pilih Microsoft Azure App Service sebagai target penerbitan Anda, lalu pilih **Terbitkan. Jendela Buat Azure App Service terbuka. Di sini Anda dapat membuat semua sumber daya Azure yang diperlukan untuk menjalankan ASP.NET web di Azure.

    Petak peta Microsoft Azure App Service

  3. Di jendela Buat Azure App Service, pilih akun Azure Anda. Pilih Ubah Jenis>Aplikasi Web. Pertahankan Nama Aplikasi Web default, lalu pilih Langganan, Grup Sumber Daya, dan Paket Azure App Service.

  4. Pilih Buat.

  5. Catat properti URL Situs di bagian Ringkasan. URL ini adalah titik akhir back-end Anda nanti dalam tutorial.

  6. Pilih Terbitkan.

Setelah Anda menyelesaikan wizard, wizard akan menerbitkan aplikasi web ASP.NET ke Azure lalu membuka aplikasi di browser default. Aplikasi Anda dapat dilihat di Azure App Services.

URL menggunakan nama aplikasi web yang Anda tentukan sebelumnya, dengan format http://<>.azurewebsites.net.

Modifikasi aplikasi iOS Anda

  1. Buka aplikasi tampilan Halaman Tunggal yang Anda buat di tutorial Kirim pemberitahuan push ke aplikasi iOS menggunakan tutorial Azure Notification Hubs.

    Catatan

    Bagian ini mengasumsikan bahwa proyek Anda dikonfigurasi dengan nama organisasi kosong. Jika tidak, prepend nama organisasi Anda ke semua nama kelas.

  2. Dalam file Main.storyboard, tambahkan komponen yang ditampilkan di cuplikan layar dari pustaka objek.

    Edit papan cerita di penyusun antarmuka Xcode

    • Nama Pengguna: UITextField dengan teks tempat penampung, Masukkan Nama Pengguna, tepat di bawah label kirim hasil dan dibatasi ke margin kiri dan kanan dan di bawah label kirim hasil.

    • KataSandi : UITextField dengan teks tempat penampung, Masukkan Kata Sandi, tepat di bawah bidang teks nama pengguna dan dibatasi ke margin kiri dan kanan dan di bawah bidang teks nama pengguna. Centang opsi Entri Teks Aman di Pemeriksa Atribut, di bawah Kunci Pengembalian.

    • Masuk: UIButton berlabel tepat di bawah bidang teks kata sandi dan kosongkan opsi Diaktifkan di Pemeriksa Atribut, di bawah Control-Content

    • WNS: Beri label dan beralih untuk mengaktifkan pengiriman pemberitahuan Windows Notification Service jika telah disiapkan di hub. Lihat tutorial Memulai Windows.

    • GCM: Beri label dan beralih untuk mengaktifkan pengiriman notifikasi ke Google Cloud Messaging jika telah disiapkan di hub. Lihat tutorial Memulai Android.

    • APNS: Beri label dan beralih untuk mengaktifkan pengiriman pemberitahuan ke Apple Platform Notification Service.

    • Nama Penerima: UITextField dengan teks tempat penampung, Masukkan nama penerima, tepat di bawah label GCM dan dibatasi ke margin kiri dan kanan dan di bawah label GCM.

      Beberapa komponen ditambahkan dalam tutorial Kirim pemberitahuan push ke aplikasi iOS menggunakan tutorial Azure Notification Hubs.

  3. Ctrl seret dari komponen dalam tampilan ke ViewController.h dan tambahkan outlet baru ini:

    @property (weak, nonatomic) IBOutlet UITextField *UsernameField;
    @property (weak, nonatomic) IBOutlet UITextField *PasswordField;
    @property (weak, nonatomic) IBOutlet UITextField *RecipientField;
    @property (weak, nonatomic) IBOutlet UITextField *NotificationField;
    
    // Used to enable the buttons on the UI
    @property (weak, nonatomic) IBOutlet UIButton *LogInButton;
    @property (weak, nonatomic) IBOutlet UIButton *SendNotificationButton;
    
    // Used to enabled sending notifications across platforms
    @property (weak, nonatomic) IBOutlet UISwitch *WNSSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *GCMSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *APNSSwitch;
    
    - (IBAction)LogInAction:(id)sender;
    
  4. Dalam ViewController.h, tambahkan #define berikut ini setelah pernyataan impor Anda. Ganti tempat penampung <Your backend endpoint> dengan URL Tujuan yang Anda gunakan untuk menyebarkan backend aplikasi Anda di bagian sebelumnya. Misalnya, http://your_backend.azurewebsites.net:

    #define BACKEND_ENDPOINT @"<Your backend endpoint>"
    
  5. Dalam proyek Anda, buat kelas Cocoa Touch baru yang dinamai RegisterClient untuk antarmuka dengan ASP.NET backend yang Anda buat. Buat kelas yang mewarisi dari NSObject. Tambahkan kode berikut di RegisterClient.h:

    @interface RegisterClient : NSObject
    
    @property (strong, nonatomic) NSString* authenticationHeader;
    
    -(void) registerWithDeviceToken:(NSData*)token tags:(NSSet*)tags
        andCompletion:(void(^)(NSError*))completion;
    
    -(instancetype) initWithEndpoint:(NSString*)Endpoint;
    
    @end
    
  6. Di bagian RegisterClient.m, perbarui bagian @interface:

    @interface RegisterClient ()
    
    @property (strong, nonatomic) NSURLSession* session;
    @property (strong, nonatomic) NSURLSession* endpoint;
    
    -(void) tryToRegisterWithDeviceToken:(NSData*)token tags:(NSSet*)tags retry:(BOOL)retry
                andCompletion:(void(^)(NSError*))completion;
    -(void) retrieveOrRequestRegistrationIdWithDeviceToken:(NSString*)token
                completion:(void(^)(NSString*, NSError*))completion;
    -(void) upsertRegistrationWithRegistrationId:(NSString*)registrationId deviceToken:(NSString*)token
                tags:(NSSet*)tags andCompletion:(void(^)(NSURLResponse*, NSError*))completion;
    
    @end
    
  7. Ganti bagian@implementation di RegisterClient.m dengan kode berikut:

    @implementation RegisterClient
    
    // Globals used by RegisterClient
    NSString *const RegistrationIdLocalStorageKey = @"RegistrationId";
    
    -(instancetype) initWithEndpoint:(NSString*)Endpoint
    {
        self = [super init];
        if (self) {
            NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
            _session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
            _endpoint = Endpoint;
        }
        return self;
    }
    
    -(void) registerWithDeviceToken:(NSData*)token tags:(NSSet*)tags
                andCompletion:(void(^)(NSError*))completion
    {
        [self tryToRegisterWithDeviceToken:token tags:tags retry:YES andCompletion:completion];
    }
    
    -(void) tryToRegisterWithDeviceToken:(NSData*)token tags:(NSSet*)tags retry:(BOOL)retry
                andCompletion:(void(^)(NSError*))completion
    {
        NSSet* tagsSet = tags?tags:[[NSSet alloc] init];
    
        NSString *deviceTokenString = [[token description]
            stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
        deviceTokenString = [[deviceTokenString stringByReplacingOccurrencesOfString:@" " withString:@""]
                                uppercaseString];
    
        [self retrieveOrRequestRegistrationIdWithDeviceToken: deviceTokenString
            completion:^(NSString* registrationId, NSError *error) {
            NSLog(@"regId: %@", registrationId);
            if (error) {
                completion(error);
                return;
            }
    
            [self upsertRegistrationWithRegistrationId:registrationId deviceToken:deviceTokenString
                tags:tagsSet andCompletion:^(NSURLResponse * response, NSError *error) {
                if (error) {
                    completion(error);
                    return;
                }
    
                NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
                if (httpResponse.statusCode == 200) {
                    completion(nil);
                } else if (httpResponse.statusCode == 410 && retry) {
                    [self tryToRegisterWithDeviceToken:token tags:tags retry:NO andCompletion:completion];
                } else {
                    NSLog(@"Registration error with response status: %ld", (long)httpResponse.statusCode);
    
                    completion([NSError errorWithDomain:@"Registration" code:httpResponse.statusCode
                                userInfo:nil]);
                }
    
            }];
        }];
    }
    
    -(void) upsertRegistrationWithRegistrationId:(NSString*)registrationId deviceToken:(NSData*)token
                tags:(NSSet*)tags andCompletion:(void(^)(NSURLResponse*, NSError*))completion
    {
        NSDictionary* deviceRegistration = @{@"Platform" : @"apns", @"Handle": token,
                                                @"Tags": [tags allObjects]};
        NSData* jsonData = [NSJSONSerialization dataWithJSONObject:deviceRegistration
                            options:NSJSONWritingPrettyPrinted error:nil];
    
        NSLog(@"JSON registration: %@", [[NSString alloc] initWithData:jsonData
                                            encoding:NSUTF8StringEncoding]);
    
        NSString* endpoint = [NSString stringWithFormat:@"%@/api/register/%@", _endpoint,
                                registrationId];
        NSURL* requestURL = [NSURL URLWithString:endpoint];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"PUT"];
        [request setHTTPBody:jsonData];
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
                                                self.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
        [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    
        NSURLSessionDataTask* dataTask = [self.session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            if (!error)
            {
                completion(response, error);
            }
            else
            {
                NSLog(@"Error request: %@", error);
                completion(nil, error);
            }
        }];
        [dataTask resume];
    }
    
    -(void) retrieveOrRequestRegistrationIdWithDeviceToken:(NSString*)token
                completion:(void(^)(NSString*, NSError*))completion
    {
        NSString* registrationId = [[NSUserDefaults standardUserDefaults]
                                    objectForKey:RegistrationIdLocalStorageKey];
    
        if (registrationId)
        {
            completion(registrationId, nil);
            return;
        }
    
        // request new one & save
        NSURL* requestURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/api/register?handle=%@",
                                _endpoint, token]];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"POST"];
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
                                                self.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    
        NSURLSessionDataTask* dataTask = [self.session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
            if (!error && httpResponse.statusCode == 200)
            {
                NSString* registrationId = [[NSString alloc] initWithData:data
                    encoding:NSUTF8StringEncoding];
    
                // remove quotes
                registrationId = [registrationId substringWithRange:NSMakeRange(1,
                                    [registrationId length]-2)];
    
                [[NSUserDefaults standardUserDefaults] setObject:registrationId
                    forKey:RegistrationIdLocalStorageKey];
                [[NSUserDefaults standardUserDefaults] synchronize];
    
                completion(registrationId, nil);
            }
            else
            {
                NSLog(@"Error status: %ld, request: %@", (long)httpResponse.statusCode, error);
                if (error)
                    completion(nil, error);
                else {
                    completion(nil, [NSError errorWithDomain:@"Registration" code:httpResponse.statusCode
                                userInfo:nil]);
                }
            }
        }];
        [dataTask resume];
    }
    
    @end
    

    Kode ini mengimplementasikan logika yang dijelaskan dalam artikel panduan Mendaftar dari backend aplikasi Anda menggunakan NSURLSession untuk melakukan panggilan REST ke backend aplikasi Anda, dan NSUserDefaults untuk secara lokal menyimpan registrationId yang dikembalikan oleh hub pemberitahuan.

    Kelas ini mengharuskan authorizationHeader propertinya diatur agar dapat bekerja dengan baik. Properti ini diatur oleh kelas ViewController setelah masuk.

  8. Dalam ViewController.h, tambahkan pernyataan #import untuk RegisterClient.h. Kemudian tambahkan deklarasi untuk token perangkat dan referensi ke instans RegisterClient di bagian @interface:

    #import "RegisterClient.h"
    
    @property (strong, nonatomic) NSData* deviceToken;
    @property (strong, nonatomic) RegisterClient* registerClient;
    
  9. Di ViewController.m, tambahkan deklarasi metode privat di bagian@interface ini:

    @interface ViewController () <UITextFieldDelegate, NSURLConnectionDataDelegate, NSXMLParserDelegate>
    
    // create the Authorization header to perform Basic authentication with your app back-end
    -(void) createAndSetAuthenticationHeaderWithUsername:(NSString*)username
                    AndPassword:(NSString*)password;
    
    @end
    

    Catatan

    Cuplikan berikut bukan skema autentikasi yang aman, Anda harus mengganti implementasi createAndSetAuthenticationHeaderWithUsername:AndPassword: dengan mekanisme autentikasi tertentu yang menghasilkan token autentikasi untuk digunakan oleh kelas klien register, misalnya OAuth, Active Directory.

  10. Kemudian di bagian @implementation dari ViewController.m, tambahkan kode berikut, yang menambahkan implementasi untuk mengatur token perangkat dan header autentikasi.

    -(void) setDeviceToken: (NSData*) deviceToken
    {
        _deviceToken = deviceToken;
        self.LogInButton.enabled = YES;
    }
    
    -(void) createAndSetAuthenticationHeaderWithUsername:(NSString*)username
                    AndPassword:(NSString*)password;
    {
        NSString* headerValue = [NSString stringWithFormat:@"%@:%@", username, password];
    
        NSData* encodedData = [[headerValue dataUsingEncoding:NSUTF8StringEncoding] base64EncodedDataWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
    
        self.registerClient.authenticationHeader = [[NSString alloc] initWithData:encodedData
                                                    encoding:NSUTF8StringEncoding];
    }
    
    -(BOOL)textFieldShouldReturn:(UITextField *)textField
    {
        [textField resignFirstResponder];
        return YES;
    }
    

    Perhatikan cara mengatur token perangkat memungkinkan tombol Masuk. Ini karena sebagai bagian dari tindakan masuk, pengontrol tampilan mendaftar untuk pemberitahuan push dengan backend aplikasi. Anda tidak ingin tindakan Masuk dapat diakses hingga token perangkat telah disiapkan dengan benar. Anda dapat memisahkan masuk dari pendaftaran push selama yang pertama terjadi sebelum yang terakhir.

  11. Di ViewController.m, gunakan cuplikan berikut untuk mengimplementasikan metode tindakan untuk tombol Masuk dan metode untuk mengirim pesan pemberitahuan menggunakan backend ASP.NET Anda.

    - (IBAction)LogInAction:(id)sender {
        // create authentication header and set it in register client
        NSString* username = self.UsernameField.text;
        NSString* password = self.PasswordField.text;
    
        [self createAndSetAuthenticationHeaderWithUsername:username AndPassword:password];
    
        __weak ViewController* selfie = self;
        [self.registerClient registerWithDeviceToken:self.deviceToken tags:nil
            andCompletion:^(NSError* error) {
            if (!error) {
                dispatch_async(dispatch_get_main_queue(),
                ^{
                    selfie.SendNotificationButton.enabled = YES;
                    [self MessageBox:@"Success" message:@"Registered successfully!"];
                });
            }
        }];
    }
    
    - (void)SendNotificationASPNETBackend:(NSString*)pns UsernameTag:(NSString*)usernameTag
                Message:(NSString*)message
    {
        NSURLSession* session = [NSURLSession
            sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:nil
            delegateQueue:nil];
    
        // Pass the pns and username tag as parameters with the REST URL to the ASP.NET backend
        NSURL* requestURL = [NSURL URLWithString:[NSString
            stringWithFormat:@"%@/api/notifications?pns=%@&to_tag=%@", BACKEND_ENDPOINT, pns,
            usernameTag]];
    
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"POST"];
    
        // Get the mock authenticationheader from the register client
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
            self.registerClient.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    
        //Add the notification message body
        [request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
        [request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding]];
    
        // Execute the send notification REST API on the ASP.NET Backend
        NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
            if (error || httpResponse.statusCode != 200)
            {
                NSString* status = [NSString stringWithFormat:@"Error Status for %@: %d\nError: %@\n",
                                    pns, httpResponse.statusCode, error];
                dispatch_async(dispatch_get_main_queue(),
                ^{
                    // Append text because all 3 PNS calls may also have information to view
                    [self.sendResults setText:[self.sendResults.text stringByAppendingString:status]];
                });
                NSLog(status);
            }
    
            if (data != NULL)
            {
                xmlParser = [[NSXMLParser alloc] initWithData:data];
                [xmlParser setDelegate:self];
                [xmlParser parse];
            }
        }];
        [dataTask resume];
    }
    
  12. Perbarui tindakan untuk tombol Kirim Pemberitahuan untuk menggunakan backend ASP.NET dan kirim ke PNS yang diaktifkan oleh sakelar.

    - (IBAction)SendNotificationMessage:(id)sender
    {
        //[self SendNotificationRESTAPI];
        [self SendToEnabledPlatforms];
    }
    
    -(void)SendToEnabledPlatforms
    {
        NSString* json = [NSString stringWithFormat:@"\"%@\"",self.notificationMessage.text];
    
        [self.sendResults setText:@""];
    
        if ([self.WNSSwitch isOn])
            [self SendNotificationASPNETBackend:@"wns" UsernameTag:self.RecipientField.text Message:json];
    
        if ([self.GCMSwitch isOn])
            [self SendNotificationASPNETBackend:@"gcm" UsernameTag:self.RecipientField.text Message:json];
    
        if ([self.APNSSwitch isOn])
            [self SendNotificationASPNETBackend:@"apns" UsernameTag:self.RecipientField.text Message:json];
    }
    
  13. Dalam fungsi ViewDidLoad, tambahkan yang berikut ini untuk membuat instansi RegisterClient instans dan mengatur delegasi untuk bidang teks Anda.

    self.UsernameField.delegate = self;
    self.PasswordField.delegate = self;
    self.RecipientField.delegate = self;
    self.registerClient = [[RegisterClient alloc] initWithEndpoint:BACKEND_ENDPOINT];
    
  14. Sekarang di AppDelegate.m, hapus semua konten metode application:didRegisterForPushNotificationWithDeviceToken: dan ganti dengan yang berikut (untuk memastikan bahwa pengontrol tampilan berisi token perangkat terbaru yang diambil dari APN):

    // Add import to the top of the file
    #import "ViewController.h"
    
    - (void)application:(UIApplication *)application
                didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
        ViewController* rvc = (ViewController*) self.window.rootViewController;
        rvc.deviceToken = deviceToken;
    }
    
  15. Akhirnya di AppDelegate.m, pastikan Anda memiliki metode berikut:

    - (void)application:(UIApplication *)application didReceiveRemoteNotification: (NSDictionary *)userInfo {
        NSLog(@"%@", userInfo);
        [self MessageBox:@"Notification" message:[[userInfo objectForKey:@"aps"] valueForKey:@"alert"]];
    }
    

Uji aplikasi

  1. Di XCode, jalankan aplikasi pada perangkat iOS fisik (pemberitahuan push tidak berfungsi di simulator).

  2. Di UI aplikasi iOS, masukkan nilai yang sama untuk nama pengguna dan kata sandi. Lalu klik Masuk.

    Aplikasi uji iOS

  3. Anda akan melihat pop-up yang memberi tahu Anda tentang keberhasilan pendaftaran. Klik OK.

    Pemberitahuan pengujian iOS ditampilkan

  4. Di bidang teks *tag nama pengguna Penerima, masukkan tag nama pengguna yang digunakan dengan pendaftaran dari perangkat lain.

  5. Masukkan pesan pemberitahuan dan klik Kirim Pemberitahuan. Hanya perangkat yang memiliki pendaftaran dengan tag nama pengguna penerima yang menerima pesan pemberitahuan. Ini hanya dikirim ke pengguna tersebut.

    Pemberitahuan dengan tag uji iOS

Langkah berikutnya

Dalam tutorial ini, Anda sudah mempelajari cara mengirim pemberitahuan push kepada pengguna tertentu yang memiliki tag terkait dengan pendaftaran mereka. Untuk mempelajari cara membuat pemberitahuan push berbasis lokasi, lanjutkan ke tutorial berikut: