Практическое руководство. Интеграция с уведомлениями Graph (iOS)

Служба уведомлений Graph позволяет приложению отправлять уведомления, предназначенные для пользователей на нескольких устройствах, а также управлять этими уведомлениями.

Используя клиентский пакет SDK для Project Rome в iOS, ваше приложение iOS может зарегистрироваться для получения уведомлений, публикуемых с сервера приложений, которые предназначены для вошедшего в систему пользователя. Этот пакет SDK позволяет клиенту приложения получать полезные данные новых входящих уведомлений, управлять состоянием существующих уведомлений и получать журнал уведомлений. Дополнительные сведения о службе уведомлений и ее использовании для доставки уведомлений, предназначенных для пользователей, см. в разделе Microsoft Graph Notifications (уведомления Microsoft Graph).

Все компоненты в пакете SDK для Project Rome, включая уведомления Graph и многое другое, созданы на основе базовой платформы, которая называется платформой подключенных устройств. Это руководство ознакомит вас с тем, что необходимо сделать, чтобы приступить к использованию платформы подключенных устройств. В нем объясняется, как использовать интерфейсы API в пакете SDK для реализации функций уведомлений Graph.

В приведенных ниже инструкциях используется код из примера приложения iOS для Project Rome, доступного на сайте GitHub.

На странице Справочник по API приведены ссылки на справочную документацию, относящуюся к сценариям с использованием уведомлений.

Настройка платформы подключенных устройств и уведомлений

Регистрация приложения

Аутентификация с помощью учетных записей Майкрософт (MSA) или Azure Active Directory (AAD) является обязательной почти для всех компонентов пакета SDK для Project Rome (за исключением интерфейсов API обмена с устройствами поблизости). Если у вас нет учетной записи Майкрософт и вы хотите ее использовать, зарегистрируйтесь на сайте account.microsoft.com.

Примечание

Учетные записи Azure Active Directory (AAD) не поддерживаются интерфейсами API Device Relay.

Выбрав метод проверки подлинности, необходимо зарегистрировать приложение в корпорации Майкрософт, следуя указаниям на портале регистрации приложений. Если у вас нет учетной записи разработчика Майкрософт, необходимо будет ее создать.

При регистрации приложения с помощью учетной записи Майкрософт (MSA) вы должны получить строку идентификатора клиента. Сохраните ее последующего использования. Она позволит вашему приложению обращаться к ресурсам платформы подключенных устройств корпорации Майкрософт. Если вы используете AAD, обратитесь к разделу Библиотеки проверки подлинности Azure Active Directory за инструкциями по получению строки идентификатора клиента.

Добавление пакета SDK

Самый простой способ добавить платформу подключенных устройств в приложение iOS — с помощью диспетчера зависимостей CocoaPods. Перейдите к файлу Podfile своего проекта iOS и вставьте в него следующую запись.

platform :ios, "10.0"
workspace 'iOSSample'

target 'iOSSample' do
  # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
  # use_frameworks!

	pod 'ProjectRomeSdk'

  # Pods for iOSSample

Примечание

Чтобы воспользоваться CocoaPod, необходимо использовать XCWORKSPACE-файл в проекте.

Настройка аутентификации и управление учетными записями

В процессе регистрации платформе подключенных устройств требуется допустимый маркер OAuth. Вы можете использовать предпочтительный способ создания маркеров OAuth и управления ими. Тем не менее, чтобы помочь разработчикам приступить к работе с платформой, мы добавили поставщик аутентификации в пример приложения iOS, который можно использовать для создания маркеров обновления и управления ими в приложении.

Если вы не используете предоставленный код, необходимо реализовать интерфейс MCDConnectedDevicesAccountManager самостоятельно.

Если вы используете учетную запись Майкрософт, включите следующие области в запрос на вход: "wl.offline_access", "ccs.ReadWrite", "dds.read", "dds.register", "wns.connect", "asimovrome.telemetry" и "https://activity.windows.com/UserActivity.ReadWrite.CreatedByApp".

Примечание

Учетные записи Azure Active Directory (AAD) не поддерживаются интерфейсами API Device Relay.

Если вы используете учетную запись AAD, необходимо запросить следующие аудитории: "https://cdpcs.access.microsoft.com", "https://cs.dds.microsoft.com", "https://wns.windows.com/" и "https://activity.microsoft.com".

Вне зависимости от того, применяете вы предоставленную реализацию MCDConnectedDevicesAccountManager или нет, если вы используете AAD, то потребуется указать следующие разрешения в регистрации приложения на портале Azure (portal.azure.com > Azure Active Directory > "Регистрация приложений"):

  • Microsoft Activity Feed Service:
    • доставка и изменение уведомлений пользователя для этого приложения;
    • чтение и запись действий приложений в веб-канале действий пользователей;
  • Windows Notification Service:
    • подключение устройства к службе уведомлений Windows;
  • Microsoft Device Directory Service:
    • просмотр списка устройств;
    • добавление в список устройств и приложений;
  • Microsoft Command Service:
    • взаимодействие с устройствами пользователей;
    • чтение устройств пользователей.

Регистрация приложения для работы с push-уведомлениями

Зарегистрируйте свое приложение в Apple для поддержки push-уведомлений Apple. Обязательно запишите идентификатор отправителя и ключ сервера, которые вы получите. Они потребуются вам позже.

После регистрации необходимо связать функции push-уведомлений с платформой подключенных устройств в приложении.

self.notificationRegistration = [[MCDConnectedDevicesNotificationRegistration alloc] init];
    if ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications])
    {
        self.notificationRegistration.type = MCDNotificationTypeAPN;
    }
    else
    {
        self.notificationRegistration.type = MCDNotificationTypePolling;
    }
    self.notificationRegistration.appId = [[NSBundle mainBundle] bundleIdentifier];
    self.notificationRegistration.appDisplayName = (NSString*)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
    self.notificationRegistration.token = deviceToken;
    self.isRegisteredWithToken = YES;

Регистрация приложения в Центре разработки для Microsoft Windows для работы на нескольких устройствах

Предупреждение

Это действие необходимо, только если вы хотите использовать возможности Project Rome для доступа к данным на устройствах, не использующих Windows, или для отправки запросов к этим устройствам. Если ваше приложение нацелено только на устройства с Windows, выполнять этот шаг не нужно.

Зарегистрируйте свое приложение для использования возможностей работы на нескольких устройствах, предлагаемых панелью мониторинга для разработчиков Майкрософт. Эта процедура отличается от регистрации приложения для использования учетных записей Майкрософт и AAD, описанной выше. Основная цель этого процесса — сопоставить удостоверения приложения для конкретных платформ с удостоверением кроссплатформенного приложения, которое распознается платформой подключенных устройств. Этот шаг также делает возможной отправку уведомлений с помощью собственных служб push-уведомлений, соответствующих мобильным платформам, которые использует ваше приложение. Для iOS становится возможной отправка уведомлений на конечные точки приложения iOS с помощью APNs.

Перейдите к панели мониторинга Центра разработки, выберите "Cross-Device Experiences" (Работа на нескольких устройствах) в области навигации слева и выберите "Configure a new cross-device app" (Настроить новое приложение для нескольких устройств). Панель мониторинга Центра разработки — работа на нескольких устройствах

Процесс подключения в Центре разработки включает в себя следующие действия.

  • Выберите поддерживаемые платформы. Выберите целевые платформы для своего приложения, поддерживающего работу на нескольких устройствах. В случае интеграции уведомлений Graph можно выбрать Windows, Android и (или) iOS, в зависимости от применяемых платформ. Работа на нескольких устройствах — поддерживаемые платформы

  • Укажите идентификаторы приложения для каждой платформы, которую вы используете. Для приложений iOS это имя пакета, назначенное приложению при создании проекта. Обратите внимание на то, что можно добавить разные идентификаторы (до десяти) для каждой платформы. Эта возможность предоставляется на случай, если у вас есть несколько версий одного приложения или даже разных приложений, которые должны получать одни и те же уведомления от вашего сервера приложений, предназначенные для одного пользователя. Работа на нескольких устройствах — идентификаторы приложения

  • Введите или выберите идентификаторы приложения из данных регистрации приложения, полученных ранее при регистрации приложения для использования учетных записей Майкрософт или AAD. Работа на нескольких устройствах — регистрация приложения для использования учетных записей Майкрософт и AAD

  • Укажите учетные данные для собственных платформ уведомлений, относящихся к вашему приложению (например, WNS для Windows, FCM для Android или APNs для iOS), чтобы обеспечить доставку уведомлений от сервера приложений при публикации уведомлений, предназначенных для пользователей. Работа на нескольких устройствах — учетные данные службы push-уведомлений

  • Наконец, проверьте домен приложения для нескольких устройств, чтобы убедиться, что ваше приложение является владельцем этого домена и может его использовать в качестве удостоверения на нескольких устройствах. Работа на нескольких устройствах — проверка домена

Использование платформы

Создание экземпляра платформы

Чтобы приступить к работе, просто создайте экземпляр платформы.

MCDConnectedDevicesPlatform* platform = [MCDConnectedDevicesPlatform new];

Подписка на MCDConnectedDevicesAccountManager

Только аутентифицированный пользователь может получить доступ к платформе. Необходимо подписаться на события MCDConnectedDevicesAccountManager, чтобы обеспечить использование действительной учетной записи.

[MCDConnectedDevicesPlatform* platform.accountManager.accessTokenRequested
     subscribe:^(MCDConnectedDevicesAccountManager* _Nonnull manager __unused,
                 MCDConnectedDevicesAccessTokenRequestedEventArgs* _Nonnull request __unused) {

                    // Get access token

                 }
[MCDConnectedDevicesPlatform* platform.platform.accountManager.accessTokenInvalidated
     subscribe:^(MCDConnectedDevicesAccountManager* _Nonnull manager __unused,
                 MCDConnectedDevicesAccessTokenInvalidatedEventArgs* _Nonnull request) {

                      // Refresh and renew existing access token

                 }

Подписка на MCDConnectedDevicesNotificationRegistrationManager

Аналогичным образом платформа использует уведомления для передачи команд между устройствами. Поэтому необходимо подписаться на события MCDConnectedDevicesNotificationRegistrationManager, чтобы обеспечить допустимые состояния регистрации в облаке для используемой учетной записи. Проверка состояния с помощью MCDConnectedDevicesNotificationRegistrationState

[MCDConnectedDevicesPlatform* platform.notificationRegistrationManager.notificationRegistrationStateChanged
     subscribe:^(MCDConnectedDevicesNotificationRegistrationManager* manager __unused,
                 MCDConnectedDevicesNotificationRegistrationStateChangedEventArgs* args __unused) {

                     // Check state using MCDConnectedDevicesNotificationRegistrationState enum

                 }

Запуск платформы

Теперь, когда платформа инициализирована, а обработчики событий размещены, все готово к тому, чтобы начать обнаружение устройств в удаленной системе.

[MCDConnectedDevicesPlatform* platform start];

Получение учетных записей пользователей, известных приложению

Важно, чтобы список учетных записей пользователей, известных приложению, был корректно синхронизирован с MCDConnectedDevicesAccountManager.

Используйте MCDConnectedDevicesAccountManager.addAccountAsync для добавления новой учетной записи пользователя.

[MCDConnectedDevicesPlatform* platform.accountManager
     addAccountAsync:self.mcdAccount
     callback:^(MCDConnectedDevicesAddAccountResult* _Nonnull result, NSError* _Nullable error) {

     // Check state using **MCDConnectedDevicesAccountAddedStatus** enum

     }

Чтобы удалить недопустимую учетную запись, можно использовать MCDConnectedDevicesAccountManager.removeAccountAsync.

 [MCDConnectedDevicesPlatform* platform.accountManager
     removeAccountAsync:existingAccount
     callback:^(MCDConnectedDevicesRemoveAccountResult* _Nonnull result __unused, NSError* _Nullable error) {

                    // Remove invalid user account

     }

Инициализация канала уведомлений Graph

Пакет SDK для Project Rome позволяет вашему приложению подписаться на различные каналы, чтобы получать различного типа данные пользователей и управлять этими данными, включая уведомления Graph, действия пользователей и многое другое. Для их хранения и синхронизации используется MCDUserDataFeed. MCDUserNotification — это класс и тип данных, соответствующий уведомлению для пользователя, отправленного с помощью службы уведомлений Graph. Чтобы интегрировать уведомления Graph и начать прием MCDUserNotification, публикуемых сервером приложений, необходимо сначала инициализировать веб-канал данных пользователя, создав MCDUserNotificationChannel. Это действие следует рассматривать как инициализацию платформы, описанную выше. Оно должно проверяться и, возможно, повторяться каждый раз, когда приложение перемещается на передний план (но не перед инициализацией платформы).

Приведенные ниже методы инициализируют MCDUserNotificationChannel.

// You must be logged in to use UserNotifications
NSArray<MCDUserAccount*>* accounts = [[AppDataSource sharedInstance].accountProvider getUserAccounts];
if (accounts.count > 0)
{
    // Get a UserNotification channel, getting the default channel
    NSLog(@"Creating UserNotificationChannel");
    NSArray<MCDUserAccount*>* accounts = [[AppDataSource sharedInstance].accountProvider getUserAccounts];
    MCDUserDataFeed* userDataFeed = [MCDUserDataFeed userDataFeedForAccount:accounts[0]
        platform:[AppDataSource sharedInstance].platform
        activitySourceHost:CROSS_PLATFORM_APP_ID];
    NSArray<MCDSyncScope*>* syncScopes = @[ [MCDUserNotificationChannel syncScope] ];
    [userDataFeed addSyncScopes:syncScopes];
    self.channel = [MCDUserNotificationChannel userNotificationChannelWithUserDataFeed:userDataFeed];
}
else
{
    NSLog(@"Must log in to receive notifications for the logged in user!");
    self.createNotificationStatusField.text = @"Need to be logged in!";
}

На этом этапе в channel должна быть ссылка на MCDUserNotificationChannel.

Создайте MCDUserNotificationReader для получения входящих MCDUserNotifications и доступа к журналу MCDUserNotification.

Как мы показали ранее, начальное автоматическое сообщение APNs, поступающее в клиент приложения, содержит только сигнал. Необходимо передать полезные данные этого сигнала в платформу подключенных устройств, чтобы активировать пакет SDK и выполнить полную синхронизацию с сервером подключенных устройств, который содержит все объекты MCDUserNotification, опубликованные вашим сервером приложений. Это позволит получить полные полезные данные уведомления, опубликованного сервером приложений и соответствующего этому сигналу (и в случае, если какие-либо уведомления были ранее опубликованы, но не получены на этом клиенте приложения из-за проблем с подключением устройств или других проблем, они также будут получены). С помощью этих непрерывных операций синхронизации в реальном времени, выполняемых пакетом SDK, клиент приложения может обращаться к локальному кэшу веб-канала данных MCDUserNotification данного вошедшего в систему пользователя. В данном случае MCDUserNotificationReader обеспечивает доступ клиента приложения к этому веб-каналу данных для получения последних полезных данных уведомления через прослушиватель событий или для обращения к полной коллекции MCDUserNotification, которую можно использовать как модель представления для журнала уведомлений пользователя.

Получение объектов MCDUserNotification

Сначала необходимо создать экземпляр MCDUserNotificationReader и получить все существующие объекты MCDUserNotification, уже полученные средством чтения, если вы хотите использовать эту информацию для возможностей, которые вы пытаетесь включить. Всегда разумно предполагать, что сервер приложений уже публиковал уведомления для этого выполнившего вход пользователя, учитывая, что эта конкретная конечная точка устройства может быть не единственной или не первой конечной точкой, на которую пользователь установил ваше приложение. Затем добавьте прослушиватель событий, который активируется после завершения синхронизации платформой подключенных устройств и уведомляет о новых изменениях. В случае уведомлений Graph новыми изменениями могут быть новые входящие объекты MCDUserNotification, опубликованные сервером приложений, или операции обновления, удаления и истечения срока действия объектов MCDUserNotifcation, которые были выполнены на сервере или других зарегистрированных конечных точках, на которые выполнил вход этот же пользователь.

Совет

Этот прослушиватель событий выполняет основную бизнес-логику и "использует" содержимое полезных данных уведомления в зависимости от ваших сценариев. Если вы сейчас используете автоматическое уведомление APNs для создания видимого уведомления в центре уведомлений уровня ОС или используете содержимое автоматического уведомления для обновления каких-либо элементов пользовательского интерфейса в приложении, это следует делать именно здесь.

// Instantiate the reader from a MCDUserNotificationChannel
// Add a data change listener to subscribe to new changes when new notifications or notification updates are received
- (void)setupWithAccount:(MCDUserAccount*)account {
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
        @synchronized (self) {
            MCDUserDataFeed* dataFeed = [MCDUserDataFeed userDataFeedForAccount:account platform:_platform activitySourceHost:@"graphnotifications.sample.windows.com"];
            [dataFeed addSyncScopes:@[[MCDUserNotificationChannel syncScope]]];
            self.channel = [MCDUserNotificationChannel userNotificationChannelWithUserDataFeed:dataFeed];
            self.reader = [self.channel createReader];
            
            __weak typeof(self) weakSelf = self;
            _readerRegistrationToken = [self.reader addDataChangedListener:^(__unused MCDUserNotificationReader* source) {
                NSLog(@"ME123 Got a change!");
                if (weakSelf) {
                    [weakSelf forceRead];
                } else {
                    NSLog(@"ME123 WEAKSELF FOR CHANGES IS NULL!!!");
                }
            }];
            
            [self forceRead];
        }
    });
}

// this is your own business logic when the event listener is fired
// In this case, the app reads the existing batch of notifications in the store and handle any new incoming notifications or notification updates after that
- (void)forceRead {
    NSLog(@"ME123 Forced to read!");
    [self.reader readBatchAsyncWithMaxSize:NSUIntegerMax completion:^(NSArray<MCDUserNotification *> * _Nullable notifications, NSError * _Nullable error) {
        if (error) {
            NSLog(@"ME123 Failed to read batch with error %@", error);
        } else {
            [self _handleNotifications:notifications];
            NSLog(@"ME123 Have %ld listeners", self.listenerMap.count);
            for (void (^listener)(void) in self.listenerMap.allValues) {
                NSLog(@"ME123 Calling a listener about an update!");
                listener();
            }
        }
    }];
}

Обновление состояния существующего объекта MCDUserNotification

В предыдущем разделе мы уже упоминали, что иногда изменение MCDUserNotification, полученное из средства чтения, может быть обновлением состояния существующего объекта MCDUserNotification, вне зависимости от того, помечен он как закрытый или прочитанный. В этом случае клиент приложения может выбрать, что делать дальше. Например, он может выполнить универсальное закрытие, удалив соответствующее видимое уведомление на этом устройстве. Если вернуться немного назад, часто именно клиент приложения изначально является инициатором этого изменения объекта MCDUserNotification, только на другом устройстве. Можно выбрать время для обновления состояния объектов MCDUserNotifications, но обычно они обновляются, когда пользователь реагирует на соответствующее видимое уведомление на этом устройстве или когда уведомление подвергается дальнейшей обработке пользователем в специально реализованном вами интерфейсе приложения. Ниже приведен пример такого потока операций. Сервер приложений публикует уведомление, адресованное пользователю А. Пользователь A получает это уведомление на своем компьютере и телефоне, на которых установлены клиенты приложения. Пользователь щелкает это уведомление на компьютере и переходит в приложение, чтобы выполнить соответствующую задачу. Клиент приложения на этом компьютере отправит вызов к пакету SDK платформы подключенных устройств, чтобы обновить состояние соответствующего уведомления пользователя. Это позволит синхронизировать это обновление на всех устройствах данного пользователя. Другие клиенты приложения, получив это обновление состояния в реальном времени, удалят соответствующее видимое оповещение, сообщение или всплывающее уведомление из центра уведомлений, области уведомлений или центра действий на устройстве. Так происходит универсальное закрытие уведомлений на устройствах пользователя.

Совет

Класс MCDUserNotification в настоящее время предоставляет два типа обновления состояния. Вы можете изменить MCDUserNotificationReadState или MCDUserNotificationUserActionState и определить собственную логику, выполняемую после обновления уведомлений. Например, можно помечать состояние действий как "активированное" или "закрытое" и выводить сводку по этому значению, чтобы реализовать универсальное закрытие. В качестве альтернативного или дополнительного метода можно помечать состояние прочтения как "прочитанное" и "непрочитанное", и на основе этого состояния определять, какие уведомления должны отображаться в представлении журнала уведомлений в приложении.

- (void)dismissNotification:(MCDUserNotification*)notification {
    @synchronized (self) {
        notification.userActionState = MCDUserNotificationUserActionStateDismissed;
        [notification saveAsync:^(__unused MCDUserNotificationUpdateResult * _Nullable result, __unused NSError * _Nullable err) {
            NSLog(@"ME123 Dismiss notification with result %d error %@", result.succeeded, err);
        }];
    }
}