Tutorial: Send localized push notifications to iOS using Azure Notification Hubs

This tutorial shows you how to use the templates feature of Azure Notification Hubs to broadcast breaking news notifications that have been localized by language and device. In this tutorial, you start with the iOS app created in Use Notification Hubs to send breaking news. When complete, you can register for categories you're interested in, specify a language in which to receive the notifications, and receive only push notifications for the selected categories in that language.

There are two parts to this scenario:

  • iOS app allows client devices to specify a language, and to subscribe to different breaking news categories;
  • The back-end broadcasts the notifications, using the tag and template features of Azure Notification Hubs.

In this tutorial, you take the following steps:

  • Update the app user interface
  • Build the iOS app
  • Send localized template notifications from .NET console app
  • Send localized template notifications from the device

Overview

In Use Notification Hubs to send breaking news, you built an app that used tags to subscribe to notifications for different news categories. Many apps, however, target multiple markets and require localization. It means that the content of the notifications themselves have to be localized and delivered to the correct set of devices. This tutorial shows you how to use the template feature of Notification Hubs to easily deliver localized breaking news notifications.

Note

One way to send localized notifications is to create multiple versions of each tag. For instance, to support English, French, and Mandarin, you would need three different tags for world news: "world_en", "world_fr", and "world_ch". You would then have to send a localized version of the world news to each of these tags. In this topic, you use templates to avoid the proliferation of tags and the requirement of sending multiple messages.

Templates are a way to specify how a specific device should receive a notification. The template specifies the exact payload format by referring to properties that are part of the message sent by your app back-end. In your case, you send a locale-agnostic message containing all supported languages:

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

Then you ensure that devices register with a template that refers to the correct property. For instance, an iOS app that wants to register for French news registers using the following syntax:

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

For more information on templates, see Templates article.

Prerequisites

Update the app user interface

In this section, you modify the Breaking News app that you created in the topic Use Notification Hubs to send breaking news to send localized breaking news using templates.

In your MainStoryboard_iPhone.storyboard, add a Segmented Control with the three languages: English, French, and Mandarin.

Creating the iOS UI storyboard

Then make sure to add an IBOutlet in your ViewController.h as shown in the following image:

Create outlets for the switches

Build the iOS app

  1. In your Notification.h, add the retrieveLocale method, and modify the store and subscribe methods as shown in the following code:

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

    In your Notification.m, modify the storeCategoriesAndSubscribe method, by adding the locale parameter and storing it in the user defaults:

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

    Then modify the subscribe method to include the locale:

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

    You use the method registerTemplateWithDeviceToken, instead of registerNativeWithDeviceToken. When you register for a template, you have to provide the json template and also a name for the template (as the app might want to register different templates). Make sure to register your categories as tags, as you want to make sure to receive the notifications for those news.

    Add a method to retrieve the locale from the user default settings:

    - (int) retrieveLocale {
        NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    
        int locale = [defaults integerForKey:@"BreakingNewsLocale"];
    
        return locale < 0?0:locale;
    }
    
  2. Now that you modified the Notifications class, you have to make sure that the ViewController makes use of the new UISegmentControl. Add the following line in the viewDidLoad method to make sure to show the locale that is currently selected:

    self.Locale.selectedSegmentIndex = [notifications retrieveLocale];
    

    Then, in your subscribe method, change your call to the storeCategoriesAndSubscribe to the following code:

    [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. Finally, you have to update the didRegisterForRemoteNotificationsWithDeviceToken method in your AppDelegate.m, so that you can correctly refresh your registration when your app starts. Change your call to the subscribe method of notifications with the following code:

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

(optional) Send localized template notifications from .NET console app

When you send template notifications, you only need to provide a set of properties. In this scenario, the set of properties contain the localized version of the current news.

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

Send notifications using a C# console app

This section shows how to send notifications using a console app. The code broadcasts notifications to both Windows Store and iOS devices. Modify the SendTemplateNotificationAsync method in the console app you previously created with the following code:

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

    // Apple requires the apns-push-type header for all requests
    var headers = new Dictionary<string, string> {{"apns-push-type", "alert"}};

    // 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, GCM, 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);
    }
}

The SendTemplateNotificationAsync method delivers the localized piece of news to all your devices, irrespective of the platform. Your notification hub builds and delivers the correct native payload to all the devices subscribed to a specific tag.

Sending notification with Mobile Services

In your Mobile Services scheduler, use the following script:

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

(optional) Send localized template notifications from the device

If you don't have access to Visual Studio, or want to just test sending the localized template notifications directly from the app on the device. You can add the localized template parameters to the SendNotificationRESTAPI method you defined in the previous tutorial.

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

Next steps

In this tutorial, you sent localized notifications to iOS devices. To learn how to push notifications to specific users of iOS apps, advance to the following tutorial: