البرنامج التعليمي: إرسال إعلامات فورية إلى مستخدمين محددين باستخدام مراكز إعلام Azure

يوضح لك هذا البرنامج التعليمي طريقة استخدام Azure Notification Hubs لإرسال إشعارات منبثقة إلى مُستخدم تطبيق محدَّد على جهاز محدَّد. يتم استخدام الواجهة الخلفية ASP.NET WebAPI لمصادقة العملاء وإنشاء الإشعارات، كما هو موضح في موضوع الإرشادات التسجيل من الواجهة الخلفية لتطبيقك.

في هذا البرنامج التعليمي، نُفذت الخطوات التالية:

  • إنشاء مشروع WebAPI
  • مصادقة العملاء في للخلفية WebAPI
  • التسجيل للحصول على الإشعارات باستخدام الخلفية WebAPI
  • إرسال الإشعارات من خلفية WebAPI
  • نشر الخلفية WebAPI الجديدة
  • تعديل تطبيق iOS الخاص بك
  • اختبر التطبيق

المتطلبات الأساسية

يفترض هذا البرنامج التعليمي أنك قمت بإنشاء مركز الإشعارات وتكوينه كما هو موضح في إرسال إشعارات فورية إلى تطبيقات iOS باستخدام مراكز إعلام Azure. هذا البرنامج التعليمي هو أيضا الشرط الأساسي للبرنامج التعليمي Secure Push (iOS ). إذا كنت تريد استخدام تطبيقات الجوال كخدمة خلفية لك، فراجع تطبيقات الأجهزة المحمولة بدء الاستخدام مع Push.

إنشاء مشروع WebAPI

تناقش الفصول التالية إنشاء خلفية WebAPI ASP.NET جديدة. ولهذه العملية ثلاثة أغراض رئيسية:

  • مصادقة العملاء: يمكنك إضافة معالج رسالة لمصادقة طلبات العميل وإقران المستخدم مع الطلب.
  • التسجيل للحصول على إعلامات باستخدام واجهة برمجة تطبيقات الويب الخلفية: إضافة وحدة تحكم لمعالجة تسجيلات جديدة لجهاز عميل لتلقي الإشعارات. تتم إضافة اسم المستخدم المصادق عليه تلقائياً إلى التسجيل كعلامة.
  • إرسال إشعارات للعملاء: يمكنك إضافة وحدة تحكم لتوفير طريقة للمستخدمين لتشغيل دفعة آمنة للأجهزة والعملاء المقترنين بالعلامة.

قم بإنشاء الواجهة الخلفية الجديدة لواجهة برمجة تطبيقات الويب ASP.NET Core 6.0 من خلال تنفيذ الإجراءات التالية:

للتحقق، شغل Visual Studio. في قائمة "Tools" ، حدد "Extensions and Updates" . ابحث عن نسخة NuGet Package Manager من إصدار Visual Studio، وتأكد من حصولك على أحدث إصدار. إذا لم يكن الإصدار الخاص بك هو أحدث إصدار، فقم بإلغاء تثبيته، ثم أعد تثبيت NuGet Package Manager.

Screenshot of the Extensions and Updates dialog box with the NuGet Package manage for Visual Studios package highlighted.

ملاحظة

تأكد من تثبيت Visual Studio Azure SDK لنشر موقع ويب.

  1. شغل Visual Studio أو Visual Studio Express.

  2. حدد "Server Explorer" ، ثم سجل الدخول إلى حساب Azure. لإنشاء موارد موقع ويب على حسابك، يجب تسجيل الدخول.

  3. في القائمة ملف في Visual Studio، حدد جديد>Project.

  4. أدخل واجهة برمجة تطبيقات الويب في مربع البحث.

  5. حدد قالب مشروع ASP.NET Core Web API وحدد التالي.

  6. في مربع الحوار تكوين المشروع الجديد ، قم بتسمية ملحق المشروع وحدد التالي.

  7. في مربع الحوار معلومات إضافية :

    • تأكد من أن إطار العمل هو .NET 6.0 (دعم طويل الأجل).
    • تأكد من تحديد خانة الاختيار الخاصة باستخدام وحدات التحكم (قم بإلغاء تحديد استخدام الحد الأدنى من واجهات برمجة التطبيقات ).
    • ألغ تحديد تمكين دعم OpenAPI.
    • حدد Create.

إزالة ملفات قالب WeatherForecast

  1. إزالة WeatherForecast .cs ووحدات التحكم/WeatherForecastController.cs أمثلة من مشروع AppBackend الجديد.
  2. افتح Properties\launchSettings.json.
  3. تغيير خصائص launchUrl من weatherforcast إلى appbackend.

في نافذة Configure Microsoft Azure Web App، حدد اشتراك ثم في قائمة App Service plan، نفذ أي من الإجراءات التالية:

  • حدد خطة خدمة تطبيقات Azure التي قمت بإنشائها بالفعل.
  • حدد Create a new app service plan ثم قم بإنشاء خطة.

لا تحتاج إلى قاعدة بيانات لهذا البرنامج التعليمي. بعد تحديد خطة خدمة التطبيق، حدد "OK" لإنشاء المشروع.

The Configure Microsoft Azure Web App window

إذا لم تشاهد هذه الصفحة لتكوين خطة خدمة التطبيق، فتابع البرنامج التعليمي. يمكنك تكوينه أثناء نشر التطبيق لاحقاً.

مصادقة العملاء في واجهة برمجة تطبيقات الويب الخلفية

في هذا المقطع، يمكنك إنشاء فئة معالج رسالة جديدة تسمى AuthenticationTestHandler للواجهة الخلفية الجديدة. يتم اشتقاق هذه الفئة من DelegatingHandler وإضافتها كمعالج رسالة بحيث يمكن معالجة جميع الطلبات التي تأتي في الخلفية.

  1. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق مشروع AppBackend، وحدد "Add" ، ثم حدد "Class" .

  2. اسم فئة جديدة AuthenticationTestHandler.cs، ثم حدد "Add" لإنشاء الفئة. هذه الفئة تصادق المستخدمين باستخدام المصادقة الأساسية للبساطة. يمكن للتطبيق استخدام أي نظام مصادقة.

  3. في AuthenticationTestHandler.cs، إضافة using العبارات التالية:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. في AuthenticationTestHandler.cs استبدال تعريف AuthenticationTestHandler الفئة مع التعليمات البرمجية التالية:

    المعالج يسمح بالطلب عند تحقق الشروط الثلاثة التالية:

    • يتضمن الطلب عنوان ⁧التصريح.⁩
    • يستخدم الطلب المصادقة الأساسية.
    • سلسلة اسم المستخدم وسلسلة كلمة المرور هي نفس السلسلة.

    وإلا، يتم رفض الطلب. هذه المصادقة ليست مصادقة حقيقية ونهج التصريح. إنها مجرد مثال بسيط لهذا البرنامج التعليمي.

    إذا تمت مصادقة رسالة الطلب ومنح تصريح من قبل AuthenticationTestHandler، يتم إرفاق مستخدم المصادقة الأساسي بالطلب الحالي على HttpContext. سيتم استخدام معلومات المستخدم في HttpContext بواسطة وحدة تحكم أخرى (RegisterController) لاحقاً لإضافة طلب إلى طلب تسجيل الإخطار.

    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;
        }
    }
    

    ملاحظة

    ملاحظة الأمان: AuthenticationTestHandler لا توفر الفئة المصادقة الحقيقية. يتم استخدامه فقط لمحاكاة المصادقة الأساسية كما أنه غير آمن. يجب تطبيق آلية مصادقة آمنة في تطبيقات الإنتاج والخدمات.

  5. لتسجيل معالج الرسائل إضافة التعليمة البرمجية التالية في نهاية الأسلوب Register في الملف " .cs البرنامج" :

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. حفظ التغييرات.

التسجيل للحصول على الإشعارات باستخدام واجهة برمجة تطبيقات الويب الخلفية

في هذا القسم، يمكنك إضافة وحدة تحكم جديدة إلى الواجهة الخلفية لواجهة برمجة تطبيقات الويب للتعامل مع طلبات تسجيل مستخدم وجهاز للإخطارات باستخدام مكتبة العميل لمراكز الإشعارات. تضيف وحدة التحكم علامة مستخدم للمستخدم الذي تمت مصادقته وتعلق عليه HttpContext بواسطة AuthenticationTestHandler. العلامة لها تنسيق السلسلة، "username:<actual username>".

  1. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق مشروع AppBackend ثم حدد "Manage NuGet Packages" .

  2. في الجزء الأيمن، حدد "Online" ثم، في مربع "Search" ، اكتب Microsoft.Azure.NotificationHubs.

  3. في قائمة النتائج، حدد "Microsoft Azure Notification Hubs" ، ثم حدد "Install" . أكمل التثبيت، ثم أغلق نافذة NuGet Package Manager.

    يضيف هذا الإجراء مرجعاً إلى SDK مراكز إشعار Azure باستخدام حزمة Microsoft.Azure.Notification Hubs NuGet.

  4. إنشاء ملف فئة جديد يمثل الاتصال بمركز الإشعارات المستخدم لإرسال الإشعارات. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق المجلد "Models" ، وحدد "Add" ، ثم حدد "Class" . اختر اسم الفئة الجديدة إشعارات.cs، ثم حدد "Add" لإنشاء الفئة.

    The Add New Item window

  5. في إشعارات.cs، أضف العبارة التالية using في أعلى الملف:

    using Microsoft.Azure.NotificationHubs;
    
  6. استبدال ⁧Notifications⁩ تعريف الفئة مع التعليمات البرمجية التالية، واستبدال العنصرين النائبين مع سلسلة الاتصال (مع الوصول الكامل) لمركز الإشعار واسم لوحة الوصل (متوفر في ⁧بوابة 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>");
        }
    }
    

    هام

    أدخل الاسم و DefaultFullSharedAccessSignature لوحة الوصل قبل المتابعة.

  7. بعد ذلك، إنشاء وحدة تحكم جديدة تسمى RegisterController. في مستكشف الحلول، انقر بزر الماوس الأيمن فوق المجلد وحدات التحكم، وحدد "Add" ، ثم حدد "Controller" .

  8. حدد وحدة تحكم API - فارغة، ثم حدد إضافة.

  9. في المربع "Controller name" اكتب RegisterController لتسمية الفئة الجديدة ثم حدد "Add" .

    The Add Controller window.

  10. في سجلController.cs، إضافة using العبارات التالية:

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. إضافة التعليمات البرمجية التالية داخل RegisterController تعريف الفئة. في هذه التعليمة البرمجية، يمكنك إضافة علامة مستخدم للمستخدم المرفق بـ HttpContext. تمت مصادقة المستخدم وإرفاق HttpContext بواسطة عامل تصفية الرسالة التي قمت AuthenticationTestHandler بإضافتها. يمكنك أيضاً إضافة تدقيقات اختيارية للتحقق من أن المستخدم لديه حقوق التسجيل للعلامات المطلوبة.

    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. حفظ التغييرات.

إرسال إشعارات من الواجهة الخلفية لواجهة برمجة تطبيقات الويب

في هذا الفصل، يمكنك إضافة وحدة تحكم جديدة تعرض طريقة للأجهزة العميلة لإرسال إشعار. يستند الإشعار إلى علامة اسم المستخدم الذي يستخدم لوحة إشعارات Azure.NET مكتبة في الخلفية WebAPI ASP.NET.

  1. إنشاء وحدة تحكم جديدة أخرى تسمى NotificationsController بنفس الطريقة التي قمت بإنشائها RegisterController في المقطع السابق.

  2. في NotificationsController.cs، إضافة using العبارات التالية:

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. إضافة الأسلوب التالي إلى NotificationsController الفئة:

    يرسل هذا الرمز نوع إشعار يستند إلى مؤشر خدمة إشعار المنصة pns. يتم استخدام قيمة to_tag لتعيين علامة اسم المستخدم على الرسالة. يجب أن تتطابق هذه العلامة مع علامة اسم مستخدم لتسجيل مركز إشعار نشط. يتم سحب رسالة الإعلام من نص طلب POST وتنسيقها لخدمة إشعار المنصة المستهدف.

    بناءً على خدمة إشعار المنصة التي تستخدمها الأجهزة المعتمدة لتلقي الإشعارات، يتم دعم الإشعارات بمجموعة متنوعة من التنسيقات. على سبيل المثال، على أجهزة الويندوز، قد تستخدم إشعاراً منبثقاً مع WNSغير معتمد مباشرة من قِبَل خدمة إشعار المنصة أخرى. في مثل هذه الحالة، تحتاج الخلفية لتنسيق الإشعار في إشعار معتمد لخدمة إشعار المنصة من الأجهزة التي تخطط لدعمها. ثم استخدم واجهة تطبيق البرامج للإرسال المناسب على الفئة NotificationHubClient class.

    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. لتشغيل التطبيق والتأكد من دقة عملك حتى الآن، اختر المفتاح ⁧⁩F5.⁧⁩ يفتح التطبيق متصفح ويب، ويتم عرضه على الصفحة الرئيسية ASP.NET.

نشر الخلفية WebAPI الجديدة

بعد ذلك، يمكنك نشر التطبيق على موقع Azure على الويب لجعله متاحاً من جميع الأجهزة.

  1. انقر بزر الماوس الأيمن فوق مشروع AppBackend، ثم حدد "Publish" .

  2. حدد "Microsoft Azure App Service" كهدف للنشر، ثم حدد نشر**. يتم فتح نافذة إنشاء خدمة التطبيقات. هنا يمكنك إنشاء جميع الموارد Azure اللازمة لتشغيل التطبيق ASP.NET ويب في Azure.

    The Microsoft Azure App Service tile

  3. في نافذة "Create App Service" ، حدد حساب Azure. حدد "Change Type">"Web App" . احتفظ باسم تطبيق ويبالافتراضي، ثم حدد "Subscription" ، و "Resource Group" ، و "App Service Plan" .

  4. حدد Create.

  5. دون ملاحظة عن خاصية URL للموقع في قسم الملخص. عنوان URL هذا هو نقطة النهاية الخلفية في وقت لاحق في البرنامج التعليمي.

  6. حدد نشر.

بعد الانتهاء من المعالج، فإنه ينشر تطبيق الويب ASP.NET إلى Azure ثم يفتح التطبيق في المستعرض الافتراضي. تطبيقك قابل للعرض في خدمات تطبيقات Azure.

يستخدم عنوان URL اسم تطبيق الويب الذي حددته سابقا، مع التنسيق http://< app_name.azurewebsites.net>.

تعديل تطبيق iOS الخاص بك

  1. افتح تطبيق عرض الصفحة الواحدة الذي أنشأته في البرنامج التعليمي إرسال إشعارات فورية إلى تطبيقات iOS باستخدام البرنامج التعليمي لمراكز إعلام Azure .

    ملاحظة

    يفترض هذا القسم أن مشروعك قد تم تكوينه باسم مؤسسة فارغ. إذا لم يكن الأمر كذلك، فقم بإعداد اسم مؤسستك لجميع أسماء الفئات.

  2. في Main.storyboard الملف، أضف المكونات المعروضة في لقطة الشاشة من مكتبة الكائنات.

    Edit storyboard in Xcode interface builder

    • اسم المستخدم: UITextField مع نص عنصر نائب، أدخل اسم المستخدم، أسفل تسمية إرسال النتائج مباشرة ومقيدة بالهوامش اليسرى واليمنى وأسفل تسمية إرسال النتائج.

    • كلمة المرور: UITextField مع نص عنصر نائب، أدخل كلمة المرور، أسفل حقل نص اسم المستخدم مباشرة ومقيد بالهوامش اليسرى واليمنى وأسفل حقل نص اسم المستخدم. حدد الخيار إدخال نص آمن في "مفتش السمات"، ضمن مفتاح الرجوع.

    • تسجيل الدخول: UIButton المسمى مباشرة أسفل حقل نص كلمة المرور وإلغاء تحديد الخيار ممكن في "مفتش السمات"، ضمن "محتوى التحكم"

    • WNS: التسمية والتبديل لتمكين إرسال الإشعار Windows خدمة الإشعارات إذا تم إعداده على الموزع. راجع البرنامج التعليمي Windows البدء.

    • GCM: التصنيف والتبديل لتمكين إرسال الإشعار إلى Google Cloud Messaging إذا تم إعداده على الموزع. انظر البرنامج التعليمي لبدء تشغيل Android .

    • APNS: التسمية والتبديل لتمكين إرسال الإشعار إلى خدمة إشعارات Apple Platform.

    • اسم مستخدم المستلم: UITextField مع نص عنصر نائب، علامة اسم مستخدم المستلم، أسفل تسمية GCM مباشرة ومقيدة بالهوامش اليسرى واليمنى وأسفل تسمية GCM.

      تمت إضافة بعض المكونات في البرنامج التعليمي إرسال إشعارات فورية إلى تطبيقات iOS باستخدام البرنامج التعليمي لمراكز إشعارات Azure .

  3. Ctrl سحب من المكونات في طريقة العرض إلى ViewController.h وإضافة هذه المنافذ الجديدة:

    @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. في ViewController.h، أضف ما يلي #define بعد بيانات الاستيراد. استبدل <Your backend endpoint> العنصر النائب بعنوان URL المقصود الذي استخدمته لنشر الواجهة الخلفية لتطبيقك في القسم السابق. على سبيل المثال، http://your_backend.azurewebsites.net:

    #define BACKEND_ENDPOINT @"<Your backend endpoint>"
    
  5. في مشروعك، قم بإنشاء فئة جديدة من Cocoa Touch تسمى RegisterClient للتفاعل مع الواجهة الخلفية ASP.NET التي أنشأتها. إنشاء الفئة الموروثة من NSObject. ثم قم بإضافة التعليمة البرمجية التالية في 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. في RegisterClient.mالقسم ، قم بتحديث @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. استبدل المقطع @implementation الموجود في RegisterClient.m بالتعليمة البرمجية التالية:

    @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
    

    تقوم هذه التعليمة البرمجية بتنفيذ المنطق الموضح في مقالة الإرشادات التسجيل من الواجهة الخلفية لتطبيقك باستخدام NSURLSession لإجراء مكالمات REST إلى الواجهة الخلفية لتطبيقك ، وNSUserDefaults لتخزين معرف التسجيل الذي تم إرجاعه بواسطة مركز الإعلام محليا.

    تتطلب هذه الفئة تعيين خاصيتها authorizationHeader من أجل العمل بشكل صحيح. يتم تعيين هذه الخاصية بواسطة ViewController الفئة بعد تسجيل الدخول.

  8. في ViewController.h، أضف بيانا #import ل RegisterClient.h. ثم أضف إعلانا للرمز المميز للجهاز ومرجعا RegisterClient إلى مثيل في @interface القسم:

    #import "RegisterClient.h"
    
    @property (strong, nonatomic) NSData* deviceToken;
    @property (strong, nonatomic) RegisterClient* registerClient;
    
  9. في ViewController.m، أضف إعلان أسلوب خاص في @interface المقطع:

    @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
    

    ملاحظة

    المقتطف التالي ليس نظام مصادقة آمنا، يجب عليك استبدال تنفيذ createAndSetAuthenticationHeaderWithUsername:AndPassword: آلية المصادقة المحددة التي تنشئ رمزا مميزا للمصادقة ليتم استهلاكه من قبل فئة عميل السجل، على سبيل المثال OAuth و Active Directory.

  10. ثم في @implementation قسم ، أضف التعليمة ViewController.mالبرمجية التالية ، والتي تضيف التنفيذ لإعداد الرمز المميز للجهاز ورأس المصادقة.

    -(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;
    }
    

    لاحظ كيف يمكن إعداد الرمز المميز للجهاز زر تسجيل الدخول . ذلك لأنه كجزء من إجراء تسجيل الدخول ، تسجل وحدة التحكم في العرض للإشعارات المباشرة باستخدام الواجهة الخلفية للتطبيق. لا تريد أن يكون إجراء تسجيل الدخول قابلا للوصول حتى يتم إعداد الرمز المميز للجهاز بشكل صحيح. يمكنك فصل تسجيل الدخول عن تسجيل الدفع طالما أن الأول يحدث قبل الأخير.

  11. في ViewController.m، استخدم المقتطفات التالية لتنفيذ طريقة الإجراء الخاصة بزر تسجيل الدخول وطريقة لإرسال رسالة الإعلام باستخدام الواجهة الخلفية ASP.NET.

    - (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. قم بتحديث الإجراء الخاص بالزر "إرسال إعلام" لاستخدام الواجهة الخلفية ASP.NET والإرسال إلى أي PNS ممكن بواسطة مفتاح تبديل.

    - (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. في الدالة ViewDidLoad ، أضف ما يلي لإنشاء مثيل RegisterClient وتعيين المفوض لحقول النص الخاصة بك.

    self.UsernameField.delegate = self;
    self.PasswordField.delegate = self;
    self.RecipientField.delegate = self;
    self.registerClient = [[RegisterClient alloc] initWithEndpoint:BACKEND_ENDPOINT];
    
  14. الآن في AppDelegate.m، قم بإزالة كل محتوى الطريقة application:didRegisterForPushNotificationWithDeviceToken: واستبدله بما يلي (للتأكد من أن وحدة تحكم العرض تحتوي على أحدث رمز مميز للجهاز تم استرداده من APNs):

    // 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. أخيرا AppDelegate.m، تأكد من أن لديك الطريقة التالية:

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

اختبر التطبيق

  1. في XCode ، قم بتشغيل التطبيق على جهاز iOS فعلي (لا تعمل إشعارات الدفع في جهاز المحاكاة).

  2. في واجهة مستخدم تطبيق iOS، أدخل نفس القيمة لكل من اسم المستخدم وكلمة المرور. ثم انقر على تسجيل الدخول.

    iOS test application

  3. يجب أن ترى نافذة منبثقة تعلمك بنجاح التسجيل. انقر فوق موافق.

    iOS test notification displayed

  4. في الحقل النصي *علامة اسم مستخدم المستلم، أدخل علامة اسم المستخدم المستخدمة مع التسجيل من جهاز آخر.

  5. أدخل رسالة إعلام وانقر على إرسال إشعار. تتلقى فقط الأجهزة التي لديها تسجيل باستخدام علامة اسم المستخدم المستلم رسالة الإعلام. يتم إرسالها فقط إلى هؤلاء المستخدمين.

    iOS test tagged notification

الخطوات التالية

في هذا البرنامج التعليمي، تعلمت كيفية دفع الإشعارات إلى مستخدمين محددين لديهم علامات مرتبطة بتسجيلاتهم. لمعرفة طريقة عرض الإعلامات إلى أجهزة Android معينة، الدخول إلى البرنامج التعليمي التالي: