教程:使用 Azure 通知中心向 iOS 设备推送本地化通知

本教程介绍如何使用 Azure 通知中心的模板功能广播已按语言和设备本地化的突发新闻通知。 在本教程中,将从在使用通知中心发送突发新闻中创建的 iOS 应用开始操作。 完成后,可注册感兴趣的突发新闻类别,指定要接收通知的语言并仅接收采用该语言的所选类别的推送通知。

此方案包含两个部分:

  • iOS 应用程序允许客户端设备指定一种语言并订阅不同的突发新闻类别;
  • 后端使用 Azure 通知中心的“标记”和“模板”功能广播通知。

在本教程中,会执行以下步骤:

  • 更新应用用户界面
  • 生成 iOS 应用
  • 通过 .NET 控制台应用发送本地化模板通知
  • 通过设备发送本地化模板通知

概述

使用通知中心发送突发新闻中,生成了一个使用“标记”订阅不同新闻类别通知的应用。 但是,很多应用程序针对多个市场,需要本地化。 这意味着通知内容本身必须本地化且传递到正确的设备组。 本教程介绍如何使用通知中心的“模板”功能轻松传递本地化突发新闻通知。

Note

发送本地化通知的一种方式是创建每个标签的多个版本。 例如,要支持英语、法语和汉语,需要对世界新闻使用三种不同的标签:“world_en”、“world_fr”和“world_ch”。 然后必须将本地化版本的世界新闻分别发送到这些标签。 在本主题中,会使用模板来避免增生标签和发送多个消息的要求。

在较高级别上,模板是指定特定设备应如何接收通知的一种方法。 模板通过引用作为应用程序后端所发消息的一部分的属性,指定确切的负载格式。 在此示例中,将发送包含所有支持语言的区域设置未知的消息:

{
    "News_English": "...",
    "News_French": "...",
    "News_Mandarin": "..."
}

然后确保设备注册到引用正确属性的模板。 例如,要注册法语新闻的 iOS 应用会使用以下语法注册:

{
    aps:{
        alert: "$(News_French)"
    }
}

有关模板的详细信息,请参阅模板一文。

先决条件

  • 完成向特定 iOS 设备推送通知教程,并具有可用代码,因为本教程直接基于该代码。
  • 可以选用 Visual Studio 2012 或更高版本。

更新应用用户界面

本部分会修改在使用通知中心发送突发新闻主题中创建的“突发新闻”应用,以便使用模板发送本地化突发新闻。

在 MainStoryboard_iPhone.storyboard 中,添加使用以下三种语言的分段控件:英语、法语和汉语。

然后确保在 ViewController.h 中添加 IBOutlet,如下图所示:

生成 iOS 应用

  1. 在 Notification.h 中,添加 retrieveLocale 方法,并修改 store 和 subscribe 方法,如以下代码中所示:

        - (void) storeCategoriesAndSubscribeWithLocale:(int) locale categories:(NSSet*) categories completion: (void (^)(NSError* error))completion;
    
        - (void) subscribeWithLocale:(int) locale categories:(NSSet*) categories completion:(void (^)(NSError *))completion;
    
        - (NSSet*) retrieveCategories;
    
        - (int) retrieveLocale;
    

    在 Notification.m 中,通过添加区域设置参数并将它存储在用户默认值中,修改 storeCategoriesAndSubscribe 方法:

        - (void) storeCategoriesAndSubscribeWithLocale:(int) locale categories:(NSSet *)categories completion:(void (^)(NSError *))completion {
            NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
            [defaults setValue:[categories allObjects] forKey:@"BreakingNewsCategories"];
            [defaults setInteger:locale forKey:@"BreakingNewsLocale"];
            [defaults synchronize];
    
            [self subscribeWithLocale: locale categories:categories completion:completion];
        }
    

    然后修改 subscribe 方法以包括该区域设置:

        - (void) subscribeWithLocale: (int) locale categories:(NSSet *)categories completion:(void (^)(NSError *))completion{
            SBNotificationHub* hub = [[SBNotificationHub alloc] initWithConnectionString:@"<connection string>" notificationHubPath:@"<hub name>"];
    
            NSString* localeString;
            switch (locale) {
                case 0:
                    localeString = @"English";
                    break;
                case 1:
                    localeString = @"French";
                    break;
                case 2:
                    localeString = @"Mandarin";
                    break;
            }
    
            NSString* template = [NSString stringWithFormat:@"{\"aps\":{\"alert\":\"$(News_%@)\"},\"inAppMessage\":\"$(News_%@)\"}", localeString, localeString];
    
            [hub registerTemplateWithDeviceToken:self.deviceToken name:@"localizednewsTemplate" jsonBodyTemplate:template expiryTemplate:@"0" tags:categories completion:completion];
        }
    

    请使用 registerTemplateWithDeviceToken 而非 registerNativeWithDeviceToken 方法。 注册一个模板时,必须提供 json 模板并指定其名称(因为应用可能需要注册不同的模板)。 确保将类别作为标记注册,因为需确保接收有关这些新闻的通知。

    添加一个方法以从用户默认设置中检索区域设置:

       - (int) retrieveLocale {
           NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    
           int locale = [defaults integerForKey:@"BreakingNewsLocale"];
    
           return locale < 0?0:locale;
       }
    
  2. 现在已修改 Notifications 类,必须确保 ViewController 使用新的 UISegmentControl。 在 viewDidLoad 方法中添加以下行,以确保显示当前选择的区域设置:

        self.Locale.selectedSegmentIndex = [notifications retrieveLocale];
    

    然后,在 subscribe 方法中,将对 storeCategoriesAndSubscribe 的调用更改为以下代码:

        [notifications storeCategoriesAndSubscribeWithLocale: self.Locale.selectedSegmentIndex categories:[NSSet setWithArray:categories] completion: ^(NSError* error) {
            if (!error) {
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Notification" message:
                                      @"Subscribed!" delegate:nil cancelButtonTitle:
                                      @"OK" otherButtonTitles:nil, nil];
                [alert show];
            } else {
                NSLog(@"Error subscribing: %@", error);
            }
        }];
    
  3. 最后,必须在 AppDelegate.m 中更新 didRegisterForRemoteNotificationsWithDeviceToken 方法,以便在应用启动时正确刷新注册信息。 将对通知的 subscribe 方法的调用更改为以下代码:

        NSSet* categories = [self.notifications retrieveCategories];
        int locale = [self.notifications retrieveLocale];
        [self.notifications subscribeWithLocale: locale categories:categories completion:^(NSError* error) {
            if (error != nil) {
                NSLog(@"Error registering for notifications: %@", error);
            }
        }];
    

(可选)通过 .NET 控制台应用发送本地化模板通知

发送模板通知时,只需提供一组属性。 在此方案中,这组属性包含当前新闻的本地化版本。

{
    "News_English": "World News in English!",
    "News_French": "World News in French!",
    "News_Mandarin": "World News in Mandarin!"
}

使用 C# 控制台应用发送通知

本部分演示如何使用控制台应用发送通知。 代码向 Windows 应用商店和 iOS 设备广播通知。 使用以下代码修改前面创建的控制台应用中的 SendTemplateNotificationAsync 方法:

private static async void SendTemplateNotificationAsync()
{
    // Define the notification hub.
    NotificationHubClient hub = 
        NotificationHubClient.CreateClientFromConnectionString(
            "<connection string with full access>", "<hub name>");

    // Sending the notification as a template notification. All template registrations that contain 
    // "messageParam" or "News_<local selected>" and the proper tags will receive the notifications. 
    // This includes APNS, WNS, and MPNS template registrations.
    Dictionary<string, string> templateParams = new Dictionary<string, string>();

    // Create an array of breaking news categories.
    var categories = new string[] { "World", "Politics", "Business", "Technology", "Science", "Sports"};
    var locales = new string[] { "English", "French", "Mandarin" };

    foreach (var category in categories)
    {
        templateParams["messageParam"] = "Breaking " + category + " News!";

        // Sending localized News for each tag too...
        foreach( var locale in locales)
        {
            string key = "News_" + locale;

            // Your real localized news content would go here.
            templateParams[key] = "Breaking " + category + " News in " + locale + "!";
        }

        await hub.SendTemplateNotificationAsync(templateParams, category);
    }
}

无论使用何种平台,SendTemplateNotificationAsync 方法都会将本地化新闻传送到所有设备。 通知中心生成正确的本机有效负载并将其传送到已订阅特定标记的所有设备。

使用移动服务发送通知

在移动服务计划程序中,使用以下脚本:

var azure = require('azure');
var notificationHubService = azure.createNotificationHubService('<hub name>', '<connection string with full access>');
var notification = {
        "News_English": "World News in English!",
        "News_French": "World News in French!",
        "News_Mandarin", "World News in Mandarin!"
}
notificationHubService.send('World', notification, function(error) {
    if (!error) {
        console.warn("Notification successful");
    }
});

(可选)通过设备发送本地化的模板通知

如果无权访问 Visual Studio,或者只是想要试一试直接从设备上的应用发送本地化的模板通知。 那么,可将本地化模板参数添加到在前一教程中定义的 SendNotificationRESTAPI 方法。

    - (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 : %@\","
          \"News_English\":\"Breaking %@ News in English : %@\","
          \"News_French\":\"Breaking %@ News in French : %@\","
          \"News_Mandarin\":\"Breaking %@ News in Mandarin : %@\","
          categoryTag, self.notificationMessage.text,
          categoryTag, self.notificationMessage.text,  // insert English localized news here
          categoryTag, self.notificationMessage.text,  // insert French localized news here
          categoryTag, self.notificationMessage.text]; // insert Mandarin localized news here

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

后续步骤

在本教程中,会向 iOS 设备发送本地化通知。 若要了解如何向特定的 iOS 应用用户推送通知,请转到以下教程: