Partilhar via


Hubs de Notificação do Azure Rich Push

Descrição Geral

Para envolver os utilizadores com conteúdos avançados instantâneos, uma aplicação poderá querer emitir para além do texto simples. Estas notificações promovem interações do utilizador e apresentam conteúdos como URLs, sons, imagens/cupões e muito mais. Este tutorial baseia-se no tutorial Notificar Utilizadores e mostra como enviar notificações push que incorporam payloads (por exemplo, imagens).

Este tutorial é compatível com o iOS 7 e 8.

Três capturas de ecrã: um ecrã de aplicação com um botão Enviar Push, um ecrã inicial num dispositivo e um logótipo do Windows com um botão Anterior.

A um nível elevado:

  1. O back-end da aplicação:
    • Armazena o payload avançado (neste caso, imagem) na base de dados de back-end/armazenamento local.
    • Envia o ID desta notificação avançada para o dispositivo.
  2. Aplicação no dispositivo:
    • Contacta o back-end que pede o payload avançado com o ID que recebe.
    • Envia notificações de utilizadores no dispositivo quando a obtenção de dados está concluída e mostra o payload imediatamente quando os utilizadores tocam para saber mais.

Projeto WebAPI

  1. No Visual Studio, abra o projeto AppBackend que criou no tutorial Notificar Utilizadores .

  2. Obtenha uma imagem com a qual gostaria de notificar os utilizadores e colocá-la numa pasta img no diretório do projeto.

  3. Clique em Mostrar Todos os Ficheiros na Explorador de Soluções e clique com o botão direito do rato na pasta para Incluir no Projeto.

  4. Com a imagem selecionada, altere a respetiva Ação de Compilação na janela Propriedades para Recurso Incorporado.

    Captura de ecrã a mostrar Explorador de Soluções. O ficheiro de imagem está selecionado e, no respetivo painel Propriedades, o recurso incorporado é listado como a ação de compilação.

  5. Em Notifications.cs, adicione a seguinte using instrução:

    using System.Reflection;
    
  6. Substitua a Notifications classe pelo seguinte código. Certifique-se de que substitui os marcadores de posição pelas credenciais do hub de notificação e pelo nome do ficheiro de imagem:

    public class Notification {
        public int Id { get; set; }
        // Initial notification message to display to users
        public string Message { get; set; }
        // Type of rich payload (developer-defined)
        public string RichType { get; set; }
        public string Payload { get; set; }
        public bool Read { get; set; }
    }
    
    public class Notifications {
        public static Notifications Instance = new Notifications();
    
        private List<Notification> notifications = new List<Notification>();
    
        public NotificationHubClient Hub { get; set; }
    
        private Notifications() {
            // Placeholders: replace with the connection string (with full access) for your notification hub and the hub name from the Azure Classics Portal
            Hub = NotificationHubClient.CreateClientFromConnectionString("{conn string with full access}",  "{hub name}");
        }
    
        public Notification CreateNotification(string message, string richType, string payload) {
            var notification = new Notification() {
                Id = notifications.Count,
                Message = message,
                RichType = richType,
                Payload = payload,
                Read = false
            };
    
            notifications.Add(notification);
    
            return notification;
        }
    
        public Stream ReadImage(int id) {
            var assembly = Assembly.GetExecutingAssembly();
            // Placeholder: image file name (for example, logo.png).
            return assembly.GetManifestResourceStream("AppBackend.img.{logo.png}");
        }
    }
    
  7. No NotificationsController.cs, redefina NotificationsController com o seguinte código. Esta ação envia um ID inicial de notificação avançada silenciosa para o dispositivo e permite a obtenção da imagem do lado do cliente:

    // Return http response with image binary
    public HttpResponseMessage Get(int id) {
        var stream = Notifications.Instance.ReadImage(id);
    
        var result = new HttpResponseMessage(HttpStatusCode.OK);
        result.Content = new StreamContent(stream);
        // Switch in your image extension for "png"
        result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/{png}");
    
        return result;
    }
    
    // Create rich notification and send initial silent notification (containing id) to client
    public async Task<HttpResponseMessage> Post() {
        // Replace the placeholder with image file name
        var richNotificationInTheBackend = Notifications.Instance.CreateNotification("Check this image out!", "img",  "{logo.png}");
    
        var usernameTag = "username:" + HttpContext.Current.User.Identity.Name;
    
        // Silent notification with content available
        var aboutUser = "{\"aps\": {\"content-available\": 1, \"sound\":\"\"}, \"richId\": \"" + richNotificationInTheBackend.Id.ToString() + "\",  \"richMessage\": \"" + richNotificationInTheBackend.Message + "\", \"richType\": \"" + richNotificationInTheBackend.RichType + "\"}";
    
        // Send notification to apns
        await Notifications.Instance.Hub.SendAppleNativeNotificationAsync(aboutUser, usernameTag);
    
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
  8. Agora, implemente novamente esta aplicação num Site do Azure para torná-la acessível a partir de todos os dispositivos. Clique com o botão direito do rato no projeto AppBackend e selecione Publicar.

  9. Selecione Site do Azure como destino de publicação. Inicie sessão com a sua conta do Azure e selecione um Site existente ou novo e tome nota da propriedade URL de destino no separador Ligação . Referimo-nos a este URL como o ponto final de back-end mais à frente neste tutorial. Selecione Publicar.

Modificar o projeto iOS

Agora que modificou o back-end da aplicação para enviar apenas o ID de uma notificação, altere a sua aplicação iOS para processar esse ID e obtenha a mensagem avançada do seu back-end:

  1. Abra o projeto iOS e ative as notificações remotas ao aceder ao destino principal da aplicação na secção Destinos .

  2. Selecione Capacidades, ative Modos de Fundo e selecione a caixa de verificação Notificações Remotas .

    Captura de ecrã do projeto iOS a mostrar o ecrã Capacidades. Os Modos de Fundo estão ativados e a caixa de verificação Notificações remotas está selecionada.

  3. Abra Main.storyboarde certifique-se de que tem um Controlador de Vista (referido como Controlador de Vista Principal neste tutorial) no tutorial Notificar Utilizador .

  4. Adicione um Controlador de Navegação ao seu storyboard e arraste o Controlador de Vista Principal para torná-lo a vista raiz da navegação. Certifique-se de que o Controlador de Vista Inicial no inspetor atributos está selecionado apenas para o Controlador de Navegação.

  5. Adicione um Controlador de Vista ao storyboard e adicione uma Vista de Imagem. Esta é a página que os utilizadores verão assim que optarem por saber mais ao clicar na notificação. O seu storyboard deve ter o seguinte aspeto:

    Captura de ecrã de um storyboard. Estão visíveis três ecrãs de aplicação: uma vista de navegação, uma vista de base e uma vista de imagem.

  6. Clique no Controlador de Vista Principal no storyboard e certifique-se de que tem homeViewController como classe personalizada e ID de Storyboard no inspetor de Identidade.

  7. Faça o mesmo para o Controlador de Vista de Imagem, como imagemViewController.

  8. Em seguida, crie uma nova classe Controlador de Vista denominada imageViewController para processar a IU que acabou de criar.

  9. Em imageViewController.h, adicione o seguinte código às declarações de interface do controlador. Certifique-se de que controla a arrastar da vista de imagem do storyboard para estas propriedades para ligar as duas:

    @property (weak, nonatomic) IBOutlet UIImageView *myImage;
    @property (strong) UIImage* imagePayload;
    
  10. No imageViewController.m, adicione o seguinte no final de viewDidload:

    // Display the UI Image in UI Image View
    [self.myImage setImage:self.imagePayload];
    
  11. No AppDelegate.m, importe o controlador de imagem que criou:

    #import "imageViewController.h"
    
  12. Adicione uma secção de interface com a seguinte declaração:

    @interface AppDelegate ()
    
    @property UIImage* imagePayload;
    @property NSDictionary* userInfo;
    @property BOOL iOS8;
    
    // Obtain content from backend with notification id
    - (void)retrieveRichImageWithId:(int)richId completion: (void(^)(NSError*)) completion;
    
    // Redirect to Image View Controller after notification interaction
    - (void)redirectToImageViewWithImage: (UIImage *)img;
    
    @end
    
  13. No AppDelegate, certifique-se de que a sua aplicação se regista para notificações silenciosas em application: didFinishLaunchingWithOptions:

    // Software version
    self.iOS8 = [[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)] && [[UIApplication sharedApplication] respondsToSelector:@selector(registerForRemoteNotifications)];
    
    // Register for remote notifications for iOS8 and previous versions
    if (self.iOS8) {
        NSLog(@"This device is running with iOS8.");
    
        // Action
        UIMutableUserNotificationAction *richPushAction = [[UIMutableUserNotificationAction alloc] init];
        richPushAction.identifier = @"richPushMore";
        richPushAction.activationMode = UIUserNotificationActivationModeForeground;
        richPushAction.authenticationRequired = NO;
        richPushAction.title = @"More";
    
        // Notification category
        UIMutableUserNotificationCategory* richPushCategory = [[UIMutableUserNotificationCategory alloc] init];
        richPushCategory.identifier = @"richPush";
        [richPushCategory setActions:@[richPushAction] forContext:UIUserNotificationActionContextDefault];
    
        // Notification categories
        NSSet* richPushCategories = [NSSet setWithObjects:richPushCategory, nil];
    
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound |
                                                UIUserNotificationTypeAlert |
                                                UIUserNotificationTypeBadge
                                                                                    categories:richPushCategories];
    
        [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    
    }
    else {
        // Previous iOS versions
        NSLog(@"This device is running with iOS7 or earlier versions.");
    
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes: UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeNewsstandContentAvailability];
    }
    
    return YES;
    
  14. Substitua a seguinte implementação por ter application:didRegisterForRemoteNotificationsWithDeviceToken em conta as alterações à IU do storyboard:

    // Access navigation controller which is at the root of window
    UINavigationController *nc = (UINavigationController *)self.window.rootViewController;
    // Get home view controller from stack on navigation controller
    homeViewController *hvc = (homeViewController *)[nc.viewControllers objectAtIndex:0];
    hvc.deviceToken = deviceToken;
    
  15. Em seguida, adicione os seguintes métodos para AppDelegate.m obter a imagem do ponto final e enviar uma notificação local quando a obtenção estiver concluída. Certifique-se de que substitui o marcador {backend endpoint} de posição pelo ponto final de back-end:

    NSString *const GetNotificationEndpoint = @"{backend endpoint}/api/notifications";
    
    // Helper: retrieve notification content from backend with rich notification id
    - (void)retrieveRichImageWithId:(int)richId completion: (void(^)(NSError*)) completion {
        UINavigationController *nc = (UINavigationController *)self.window.rootViewController;
        homeViewController *hvc = (homeViewController *)[nc.viewControllers objectAtIndex:0];
        NSString* authenticationHeader = hvc.registerClient.authenticationHeader;
        // Check if authenticated
        if (!authenticationHeader) return;
    
        NSURLSession* session = [NSURLSession
                                sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                delegate:nil
                                delegateQueue:nil];
    
        NSURL* requestURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%d", GetNotificationEndpoint, richId]];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"GET"];
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@", authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    
        NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
            if (!error && httpResponse.statusCode == 200) {
                // From NSData to UIImage
                self.imagePayload = [UIImage imageWithData:data];
    
                completion(nil);
            }
            else {
                NSLog(@"Error status: %ld, request: %@", (long)httpResponse.statusCode, error);
                if (error)
                    completion(error);
                else {
                    completion([NSError errorWithDomain:@"APICall" code:httpResponse.statusCode userInfo:nil]);
                }
            }
        }];
        [dataTask resume];
    }
    
    // Handle silent push notifications when id is sent from backend
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler {
        self.userInfo = userInfo;
        int richId = [[self.userInfo objectForKey:@"richId"] intValue];
        NSString* richType = [self.userInfo objectForKey:@"richType"];
    
        // Retrieve image data
        if ([richType isEqualToString:@"img"]) {  
            [self retrieveRichImageWithId:richId completion:^(NSError* error) {
                if (!error){
                    // Send local notification
                    UILocalNotification* localNotification = [[UILocalNotification alloc] init];
    
                    // "5" is arbitrary here to give you enough time to quit out of the app and receive push notifications
                    localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];
                    localNotification.userInfo = self.userInfo;
                    localNotification.alertBody = [self.userInfo objectForKey:@"richMessage"];
                    localNotification.timeZone = [NSTimeZone defaultTimeZone];
    
                    // iOS8 categories
                    if (self.iOS8) {
                        localNotification.category = @"richPush";
                    }
    
                    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
    
                    handler(UIBackgroundFetchResultNewData);
                }
                else{
                    handler(UIBackgroundFetchResultFailed);
                }
            }];
        }
        // Add "else if" here to handle more types of rich content such as url, sound files, etc.
    }
    
  16. Processe a notificação local anterior ao abrir o controlador AppDelegate.m de vista de imagem com os seguintes métodos:

    // Helper: redirect users to image view controller
    - (void)redirectToImageViewWithImage: (UIImage *)img {
        UINavigationController *navigationController = (UINavigationController*) self.window.rootViewController;
        UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil];
        imageViewController *imgViewController = [mainStoryboard instantiateViewControllerWithIdentifier: @"imageViewController"];
        // Pass data/image to image view controller
        imgViewController.imagePayload = img;
    
        // Redirect
        [navigationController pushViewController:imgViewController animated:YES];
    }
    
    // Handle local notification sent above in didReceiveRemoteNotification
    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
        if (application.applicationState == UIApplicationStateActive) {
            // Show in-app alert with an extra "more" button
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Notification" message:notification.alertBody delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"More", nil];
            [alert show];
        }
        // App becomes active from user's tap on notification
        else {
            [self redirectToImageViewWithImage:self.imagePayload];
        }
    }
    
    // Handle buttons in in-app alerts and redirect with data/image
    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
        // Handle "more" button
        if (buttonIndex == 1)
        {
            [self redirectToImageViewWithImage:self.imagePayload];
        }
        // Add "else if" here to handle more buttons
    }
    
    // Handle notification setting actions in iOS8
    - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {
        // Handle richPush related buttons
        if ([identifier isEqualToString:@"richPushMore"]) {
            [self redirectToImageViewWithImage:self.imagePayload];
        }
        completionHandler();
    }
    

Executar a Aplicação

  1. No XCode, execute a aplicação num dispositivo iOS físico (as notificações push não funcionarão no simulador).
  2. Na IU da aplicação iOS, introduza um nome de utilizador e uma palavra-passe do mesmo valor para autenticação e clique em Iniciar Sessão.
  3. Clique em Enviar push e deverá ver um alerta na aplicação. Se clicar em Mais, será trazido para a imagem que escolheu incluir no back-end da aplicação.
  4. Também pode clicar em Enviar push e premir imediatamente o botão base do seu dispositivo. Dentro de momentos, receberá uma notificação push. Se tocar no mesmo ou clicar em Mais, será levado para a sua aplicação e para o conteúdo da imagem avançada.