教程:使用 Azure 通知中心向特定 iOS 设备推送通知Tutorial: Push notifications to specific iOS devices using Azure Notification Hubs

概述Overview

本教程演示如何使用 Azure 通知中心将突发新闻通知广播到 iOS 应用。This tutorial shows you how to use Azure Notification Hubs to broadcast breaking news notifications to an iOS app. 完成后,可注册感兴趣的突发新闻类别并仅接收这些类别的推送通知。When complete, you are able to register for breaking news categories you are interested in, and receive only push notifications for those categories. 此方案对于很多应用来说是常见模式,在这些应用中必须将通知发送到以前声明过对它们感兴趣的用户组,这样的应用包括 RSS 阅读器、针对音乐迷的应用等。This scenario is a common pattern for many apps where notifications have to be sent to groups of users that have previously declared interest in them, for example, RSS reader, apps for music fans, etc.

在创建通知中心的注册时,通过加入一个或多个 标记 来启用广播方案。Broadcast scenarios are enabled by including one or more tags when creating a registration in the notification hub. 将通知发送到标记时,已注册该标记的设备会接收通知。When notifications are sent to a tag, devices that have registered for the tag receive the notification. 因为标签是简单的字符串,它们不必提前设置。Because tags are simply strings, they do not have to be provisioned in advance. 有关标记的详细信息,请参阅通知中心路由和标记表达式For more information about tags, see Notification Hubs Routing and Tag Expressions.

在本教程中,会执行以下步骤:In this tutorial, you take the following steps:

  • 向应用中添加类别选择Add a category selection to the app
  • 发送带标记的通知Send tagged notifications
  • 从设备发送通知Send notifications from the device
  • 运行应用并生成通知Run the app and generate notifications

先决条件Prerequisites

本主题基于以下教程中创建的应用:教程:使用 Azure 通知中心向 iOS 应用推送通知This topic builds on the app you created in Tutorial: Push notifications to iOS apps using Azure Notification Hubs. 在开始学习本教程之前,必须已完成教程:使用 Azure 通知中心向 iOS 应用推送通知Before starting this tutorial, you must have already completed Tutorial: Push notifications to iOS apps using Azure Notification Hubs.

向应用程序中添加类别选择Add category selection to the app

第一步是向现有 Storyboard 添加 UI 元素,这些元素允许用户选择要注册的类别。The first step is to add the UI elements to your existing storyboard that enable the user to select categories to register. 用户选择的类别存储在设备上。The categories selected by a user are stored on the device. 应用程序启动时,使用所选类别作为标签在通知中心创建设备注册。When the app starts, a device registration is created in your notification hub with the selected categories as tags.

  1. MainStoryboard_iPhone.storyboard 中,从对象库添加以下组件:In your MainStoryboard_iPhone.storyboard add the following components from the object library:

    • 具有“Breaking News”文本的标签A label with "Breaking News" text,

    • 具有“World”、“Politics”、“Business”、“Technology”、“Science”、“Sports”类别文本的标签Labels with category texts "World", "Politics", "Business", "Technology", "Science", "Sports",

    • 六个开关,每个类别一个。默认情况下,各开关的“状态” 均设置为“关闭” 。Six switches, one per category, set each switch State to be Off by default.

    • 一个标有“Subscribe”的按钮One button labeled "Subscribe"

      Storyboard 应类似于:Your storyboard should look as follows:

      Xcode 接口生成器

  2. 在助手编辑器中,为所有开关创建插座并称它们为“WorldSwitch”、“PoliticsSwitch”、“BusinessSwitch”、“TechnologySwitch”、“ScienceSwitch”、“SportsSwitch”In the assistant editor, create outlets for all the switches and call them "WorldSwitch", "PoliticsSwitch", "BusinessSwitch", "TechnologySwitch", "ScienceSwitch", "SportsSwitch"

  3. 为名为 subscribe 的按钮创建一个操作;ViewController.h 应包含以下代码:Create an Action for your button called subscribe; your ViewController.h should contain the following code:

    @property (weak, nonatomic) IBOutlet UISwitch *WorldSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *PoliticsSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *BusinessSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *TechnologySwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *ScienceSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *SportsSwitch;
    
    - (IBAction)subscribe:(id)sender;
    
  4. 创建名为 Notifications 的新 Cocoa Touch 类Create a new Cocoa Touch Class called Notifications. 在文件 Notifications.h 的接口部分中复制以下代码:Copy the following code in the interface section of the file Notifications.h:

    @property NSData* deviceToken;
    
    - (id)initWithConnectionString:(NSString*)listenConnectionString HubName:(NSString*)hubName;
    
    - (void)storeCategoriesAndSubscribeWithCategories:(NSArray*)categories
                completion:(void (^)(NSError* error))completion;
    
    - (NSSet*)retrieveCategories;
    
    - (void)subscribeWithCategories:(NSSet*)categories completion:(void (^)(NSError *))completion;
    
  5. 将以下导入指令添加到 Notifications.m:Add the following import directive to Notifications.m:

    #import <WindowsAzureMessaging/WindowsAzureMessaging.h>
    
  6. 在文件 Notifications.m 的实现部分中复制以下代码。Copy the following code in the implementation section of the file Notifications.m.

    SBNotificationHub* hub;
    
    - (id)initWithConnectionString:(NSString*)listenConnectionString HubName:(NSString*)hubName{
    
        hub = [[SBNotificationHub alloc] initWithConnectionString:listenConnectionString
                                    notificationHubPath:hubName];
    
        return self;
    }
    
    - (void)storeCategoriesAndSubscribeWithCategories:(NSSet *)categories completion:(void (^)(NSError *))completion {
        NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
        [defaults setValue:[categories allObjects] forKey:@"BreakingNewsCategories"];
    
        [self subscribeWithCategories:categories completion:completion];
    }
    
    - (NSSet*)retrieveCategories {
        NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    
        NSArray* categories = [defaults stringArrayForKey:@"BreakingNewsCategories"];
    
        if (!categories) return [[NSSet alloc] init];
        return [[NSSet alloc] initWithArray:categories];
    }
    
    - (void)subscribeWithCategories:(NSSet *)categories completion:(void (^)(NSError *))completion
    {
        //[hub registerNativeWithDeviceToken:self.deviceToken tags:categories completion: completion];
    
        NSString* templateBodyAPNS = @"{\"aps\":{\"alert\":\"$(messageParam)\"}}";
    
        [hub registerTemplateWithDeviceToken:self.deviceToken name:@"simpleAPNSTemplate" 
            jsonBodyTemplate:templateBodyAPNS expiryTemplate:@"0" tags:categories completion:completion];
    }
    

    此类使用本地存储来存储和检索此设备接收的新闻类别。This class uses local storage to store and retrieve the categories of news that this device receives. 此外,它还包含一个用于通过模板注册来注册这些类别的方法。Also, it contains a method to register for these categories using a Template registration.

  7. AppDelegate.h 文件中,为 Notifications.h 添加 import 语句,并为 Notifications 类的实例添加一个属性:In the AppDelegate.h file, add an import statement for Notifications.h and add a property for an instance of the Notifications class:

    #import "Notifications.h"
    
    @property (nonatomic) Notifications* notifications;
    
  8. AppDelegate.m 中的 didFinishLaunchingWithOptions 方法开头,添加用于初始化 notifications 实例的代码。In the didFinishLaunchingWithOptions method in AppDelegate.m, add the code to initialize the notifications instance at the beginning of the method.
    hubinfo.h 中定义的 HUBNAMEHUBLISTENACCESS 内,<hub name><connection string with listen access> 占位符应已替换为通知中心名称和前面获取的 DefaultListenSharedAccessSignature 的连接字符串HUBNAME and HUBLISTENACCESS (defined in hubinfo.h) should already have the <hub name> and <connection string with listen access> placeholders replaced with your notification hub name and the connection string for DefaultListenSharedAccessSignature that you obtained earlier

    self.notifications = [[Notifications alloc] initWithConnectionString:HUBLISTENACCESS HubName:HUBNAME];
    

    Note

    由于使用客户端应用程序分发的凭据通常是不安全的,只应使用客户端应用程序分发具有侦听访问权限的密钥。Because credentials that are distributed with a client app are not generally secure, you should only distribute the key for listen access with your client app. 侦听访问权限允许应用程序注册通知,但是无法修改现有注册,也无法发送通知。Listen access enables your app to register for notifications, but existing registrations cannot be modified and notifications cannot be sent. 在受保护的后端服务中使用完全访问权限密钥,以便发送通知和更改现有注册。The full access key is used in a secured backend service for sending notifications and changing existing registrations.

  9. AppDelegate.mdidRegisterForRemoteNotificationsWithDeviceToken 方法内的代码替换为以下代码,以将设备令牌传递给 notifications 类。In the didRegisterForRemoteNotificationsWithDeviceToken method in AppDelegate.m, replace the code in the method with the following code to pass the device token to the notifications class. notifications 类将通知注册到类别。The notifications class performs the registering for notifications with the categories. 如果用户更改类别选择,则调用 subscribeWithCategories 方法以响应“订阅”按钮,从而进行更新 。If the user changes category selections, call the subscribeWithCategories method in response to the subscribe button to update them.

    Note

    由于 Apple Push Notification 服务 (APNS) 分配的设备标记随时可能更改,因此应该经常注册通知,以免通知失败。Because the device token assigned by the Apple Push Notification Service (APNS) can chance at any time, you should register for notifications frequently to avoid notification failures. 此示例在每次应用程序启动时注册通知。This example registers for notification every time that the app starts. 对于经常运行(一天一次以上)的应用程序,如果每次注册间隔时间不到一天,可以跳过注册来节省带宽。For apps that are run frequently, more than once a day, you can probably skip registration to preserve bandwidth if less than a day has passed since the previous registration.

    self.notifications.deviceToken = deviceToken;
    
    // Retrieves the categories from local storage and requests a registration for these categories
    // each time the app starts and performs a registration.
    
    NSSet* categories = [self.notifications retrieveCategories];
    [self.notifications subscribeWithCategories:categories completion:^(NSError* error) {
        if (error != nil) {
            NSLog(@"Error registering for notifications: %@", error);
        }
    }];
    

    此时,didRegisterForRemoteNotificationsWithDeviceToken 方法中不应有任何代码。At this point, there should be no other code in the didRegisterForRemoteNotificationsWithDeviceToken method.

  10. 完成通知中心入门教程后,以下方法应已存在于 AppDelegate.m 中。The following methods should already be present in AppDelegate.m from completing the Get started with Notification Hubs tutorial. 否则,请添加这些方法。If not, add them.

    - (void)MessageBox:(NSString *)title message:(NSString *)messageText
    {
    
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:messageText delegate:self
            cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [alert show];
    }
    
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:
       (NSDictionary *)userInfo {
       NSLog(@"%@", userInfo);
       [self MessageBox:@"Notification" message:[[userInfo objectForKey:@"aps"] valueForKey:@"alert"]];
     }
    

    此方法通过显示简单的 UIAlert处理运行应用程序时收到的通知。This method handles notifications received when the app is running by displaying a simple UIAlert.

  11. ViewController.m 中,为 AppDelegate.h 添加 import 语句,并将以下代码复制到 XCode 生成的 subscribe 方法中。In ViewController.m, add an import statement for AppDelegate.h and copy the following code into the XCode-generated subscribe method. 此代码会更新通知注册,使用用户在用户界面中选择的新类别标记。This code updates the notification registration to use the new category tags the user has chosen in the user interface.

    #import "Notifications.h"
    
    NSMutableArray* categories = [[NSMutableArray alloc] init];
    
    if (self.WorldSwitch.isOn) [categories addObject:@"World"];
    if (self.PoliticsSwitch.isOn) [categories addObject:@"Politics"];
    if (self.BusinessSwitch.isOn) [categories addObject:@"Business"];
    if (self.TechnologySwitch.isOn) [categories addObject:@"Technology"];
    if (self.ScienceSwitch.isOn) [categories addObject:@"Science"];
    if (self.SportsSwitch.isOn) [categories addObject:@"Sports"];
    
    Notifications* notifications = [(AppDelegate*)[[UIApplication sharedApplication]delegate] notifications];
    
    [notifications storeCategoriesAndSubscribeWithCategories:categories completion: ^(NSError* error) {
        if (!error) {
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:"Notification" message:"Subscribed" delegate:self
            cancelButtonTitle:@"OK" otherButtonTitles: nil];
            [alert show];
        } else {
            NSLog(@"Error subscribing: %@", error);
        }
    }];
    

    此方法创建类别的 NSMutableArray,使用 Notifications 类将该列表存储在本地存储中,并将相应的标签注册到通知中心。This method creates an NSMutableArray of categories and uses the Notifications class to store the list in the local storage and registers the corresponding tags with your notification hub. 更改类别时,使用新类别重新创建注册。When categories are changed, the registration is recreated with the new categories.

  12. ViewController.m 中,在 viewDidLoad 方法中添加以下代码,以根据前面保存的类别来设置用户界面。In ViewController.m, add the following code in the viewDidLoad method to set the user interface based on the previously saved categories.

    // This updates the UI on startup based on the status of previously saved categories.
    
    Notifications* notifications = [(AppDelegate*)[[UIApplication sharedApplication]delegate] notifications];
    
    NSSet* categories = [notifications retrieveCategories];
    
    if ([categories containsObject:@"World"]) self.WorldSwitch.on = true;
    if ([categories containsObject:@"Politics"]) self.PoliticsSwitch.on = true;
    if ([categories containsObject:@"Business"]) self.BusinessSwitch.on = true;
    if ([categories containsObject:@"Technology"]) self.TechnologySwitch.on = true;
    if ([categories containsObject:@"Science"]) self.ScienceSwitch.on = true;
    if ([categories containsObject:@"Sports"]) self.SportsSwitch.on = true;
    

应用程序现在可以在设备的本地存储区中存储一组类别,每当应用程序启动时,会使用这些类别注册到通知中心。The app can now store a set of categories in the device local storage used to register with the notification hub whenever the app starts. 用户可以在运行时更改选择的类别,并单击 subscribe 方法来更新设备注册。The user can change the selection of categories at runtime and click the subscribe method to update the registration for the device. 接下来,更新应用,直接从应用本身发送突发新闻通知。Next, you update the app to send the breaking news notifications directly in the app itself.

(可选)发送带标记的通知(optional) Send tagged notifications

如果无权访问 Visual Studio,可以跳到下一部分,并从应用内部发送通知。If you don't have access to Visual Studio, you can skip to the next section and send notifications from the app itself. 还可以在 Azure 门户 中使用通知中心的调试选项卡发送适当的模板通知。You can also send the proper template notification from the Azure portal using the debug tab for your notification hub.

本部分说明如何从 .NET 控制台应用以标记模板通知的形式发送突发新闻。In this section, you send breaking news as tagged template notifications from a .NET console app.

  1. 在 Visual Studio 中创建新的 Visual C# 控制台应用程序:a.In Visual Studio, create a new Visual C# console application: a. 在菜单中,选择“文件” > “新建” > “项目” 。On the menu, select File > New > Project. b.b. 展开“Visual C#”,然后选择“Windows 桌面” 。Expand Visual C#, and select Windows Desktop. c.c. 在模板列表中选择“控制台应用(.NET Framework)” 。Select Console App (.NET Framework) in the list of templates. d.d. 输入应用的名称Enter a name for the app. e.e. 为应用选择文件夹Select a folder for the app. f.f. 选择“确定”创建该项目。 Select OK to create the project.

  2. 在 Visual Studio 主菜单中,选择“工具” > “NuGet 包管理器” > “包管理器控制台”,并在控制台窗口中输入以下字符串 :On the Visual Studio main menu, select Tools > NuGet Package Manager > Package Manager Console and then, in the console window, enter the following string:

    Install-Package Microsoft.Azure.NotificationHubs
    
  3. EnterSelect Enter.
    此操作会使用 Microsoft.Azure.Notification Hubs NuGet 包添加对 Azure 通知中心 SDK 的引用。This action adds a reference to the Azure Notification Hubs SDK by using the Microsoft.Azure.Notification Hubs NuGet package.

  4. 打开文件 Program.cs 并添加以下 using 语句:Open the Program.cs file, and add the following using statement:

    using Microsoft.Azure.NotificationHubs;
    
  5. Program 类中,添加以下方法,或替换此方法(如果已存在):In the Program class, add the following method, or replace it if it already exists:

    private static async void SendTemplateNotificationAsync()
    {
        // Define the notification hub.
        NotificationHubClient hub = NotificationHubClient.CreateClientFromConnectionString("<connection string with full access>", "<hub name>");
    
        // Create an array of breaking news categories.
        var categories = new string[] { "World", "Politics", "Business", "Technology", "Science", "Sports"};
    
        // Send the notification as a template notification. All template registrations that contain
        // "messageParam" and the proper tags will receive the notifications.
        // This includes APNS, WNS, and MPNS template registrations.
    
        Dictionary<string, string> templateParams = new Dictionary<string, string>();
    
        foreach (var category in categories)
        {
            templateParams["messageParam"] = "Breaking " + category + " News!";
            await hub.SendTemplateNotificationAsync(templateParams, category);
        }
    }
    

    此代码针对字符串数组中的所有 6 个标记发送模板通知。This code sends a template notification for each of the six tags in the string array. 使用标记是为了确保设备仅接收已注册类别的通知。The use of tags ensures that devices receive notifications only for the registered categories.

  6. 在前面的代码中,将 <hub name><connection string with full access> 占位符替换为通知中心名称和从通知中心仪表板获取的 DefaultFullSharedAccessSignature 的连接字符串。In the preceding code, replace the <hub name> and <connection string with full access> placeholders with your notification hub name and the connection string for DefaultFullSharedAccessSignature from the dashboard of your notification hub.

  7. Main 方法中添加以下行:In the Main method, add the following lines:

    SendTemplateNotificationAsync();
    Console.ReadLine();
    
  8. 生成控制台应用。Build the console app.

(可选)从设备发送通知(optional) Send notifications from the device

通常,通知由后端服务发送,但你也可以直接从应用发送突发新闻通知。Normally notifications would be sent by a backend service but, you can send breaking news notifications directly from the app. 为此,需更新通知中心入门教程中定义的 SendNotificationRESTAPI 方法。To do so, you update the SendNotificationRESTAPI method that you defined in the Get started with Notification Hubs tutorial.

  1. ViewController.m 中,按如下所示更新 SendNotificationRESTAPI 方法,使其接受类别标记的参数并发送适当的模板通知。In ViewController.m, update the SendNotificationRESTAPI method as follows so that it accepts a parameter for the category tag and sends the proper template notification.

    - (void)SendNotificationRESTAPI:(NSString*)categoryTag
    {
        NSURLSession* session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration
                                    defaultSessionConfiguration] delegate:nil delegateQueue:nil];
    
        NSString *json;
    
        // Construct the messages REST endpoint
        NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@/messages/%@", HubEndpoint,
                                            HUBNAME, API_VERSION]];
    
        // Generated the token to be used in the authorization header.
        NSString* authorizationToken = [self generateSasToken:[url absoluteString]];
    
        //Create the request to add the template notification message to the hub
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        [request setHTTPMethod:@"POST"];
    
        // Add the category as a tag
        [request setValue:categoryTag forHTTPHeaderField:@"ServiceBusNotification-Tags"];
    
        // Template notification
        json = [NSString stringWithFormat:@"{\"messageParam\":\"Breaking %@ News : %@\"}",
                categoryTag, self.notificationMessage.text];
    
        // Signify template notification format
        [request setValue:@"template" forHTTPHeaderField:@"ServiceBusNotification-Format"];
    
        // JSON Content-Type
        [request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    
        //Authenticate the notification message POST request with the SaS token
        [request setValue:authorizationToken forHTTPHeaderField:@"Authorization"];
    
        //Add the notification message body
        [request setHTTPBody:[json dataUsingEncoding:NSUTF8StringEncoding]];
    
        // Send the REST request
        NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request
                    completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
            {
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
                if (error || httpResponse.statusCode != 200)
                {
                    NSLog(@"\nError status: %d\nError: %@", httpResponse.statusCode, error);
                }
                if (data != NULL)
                {
                    //xmlParser = [[NSXMLParser alloc] initWithData:data];
                    //[xmlParser setDelegate:self];
                    //[xmlParser parse];
                }
            }];
    
        [dataTask resume];
    }
    
  2. ViewController.m 中更新 Send Notification 操作(如以下代码中所示)。In ViewController.m, update the Send Notification action as shown in the code that follows. 如此,它会使用各个标记向多个平台分别发送通知。So that it sends the notifications using each tag individually and sends to multiple platforms.

    - (IBAction)SendNotificationMessage:(id)sender
    {
        self.sendResults.text = @"";
    
        NSArray* categories = [NSArray arrayWithObjects: @"World", @"Politics", @"Business",
                                @"Technology", @"Science", @"Sports", nil];
    
        // Lets send the message as breaking news for each category to WNS and APNS
        // using a template.
        for(NSString* category in categories)
        {
            [self SendNotificationRESTAPI:category];
        }
    }
    
  3. 重新生成项目,并确定没有生成错误。Rebuild your project and make sure you have no build errors.

运行应用并生成通知Run the app and generate notifications

  1. 按“运行”按钮生成项目并启动应用程序。Press the Run button to build the project and start the app. 选择要订阅的一些突发新闻选项,然后按“订阅” 按钮。Select some breaking news options to subscribe to and then press the Subscribe button. 此时应显示一个对话框,指示已订阅通知。You should see a dialog indicating the notifications have been subscribed to.

    iOS 上的示例通知

    选择“订阅” 时,应用将所选类别转换为标记,并针对所选标记从通知中心请求注册新设备。When you choose Subscribe, the app converts the selected categories into tags and requests a new device registration for the selected tags from the notification hub.

  2. 输入要以突发新闻形式发送的消息,然后按“发送通知” 按钮。Enter a message to be sent as breaking news then press the Send Notification button. 或者,运行 .NET 控制台应用以生成通知。Alternatively, run the .NET console app to generate notifications.

    在 iOS 中更改通知首选项

  3. 每个订阅突发新闻的设备都会收到刚刚发送的突发新闻通知。Each device subscribed to breaking news receives the breaking news notifications you just sent.

后续步骤Next steps

在本教程中,向注册类别的特定 iOS 设备发送了广播通知。In this tutorial, you sent broadcast notifications to specific iOS devices that have registered for the categories. 若要了解如何推送本地化通知,请转到以下教程:To learn how to push localized notifications, advance to the following tutorial: