图形通知使应用能够跨多个设备发送和管理用户目标通知。
借助 iOS 上的 Project Rome 客户端 SDK,iOS 应用可以注册以接收从以登录用户为目标的应用服务器发布的通知。 SDK 使应用客户端能够接收新的传入通知有效负载、管理现有通知的状态以及检索通知历史记录。 有关通知以及如何实现以人为中心的通知传递的详细信息,请参阅 Microsoft Graph 通知概述
Project Rome SDK 中的所有功能(包括 Graph 通知等)都基于称为“连接设备平台”的基础平台构建。 本指南旨在指导你完成开始使用连接设备平台的必要步骤,并介绍如何在 SDK 中使用 API 来实现图形通知 -specific 功能。
以下步骤将引用 GitHub 上提供的 Project Rome iOS 示例应用中 的代码。
如需与通知方案相关的参考文档链接,请参阅 API 参考页。
设置连接设备平台和通知
注册应用
Project Rome SDK 的几乎所有功能都需要Microsoft帐户(MSA)或 Azure Active Directory (AAD) 身份验证(异常是附近的共享 API)。 如果还没有 MSA 并希望使用 MSA,请注册 account.microsoft.com。
注释
设备中继 API 不支持 Azure Active Directory (AAD) 帐户。
使用所选的身份验证方法,必须按照应用程序 注册门户上的说明将应用注册到Microsoft。 如果没有Microsoft开发人员帐户,则需要创建一个。
使用 MSA 注册应用时,应会收到客户端 ID 字符串。 保存此项供以后使用。 这将允许你的应用访问Microsoft的联网设备平台资源。 如果使用 AAD,请参阅 Azure Active Directory 身份验证库 ,获取客户端 ID 字符串的说明。
添加 SDK
将连接的设备平台添加到 iOS 应用的最简单方法是使用 CocoaPods 依赖项管理器。 转到 iOS 项目的 Podfile 并插入以下条目:
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 接口。
如果使用 MSA,请在登录请求中包含以下范围: "wl.offline_access"
、、 "ccs.ReadWrite"
、 "dds.read"
、 "dds.register"
、 "wns.connect"
、 "asimovrome.telemetry"
和 "https://activity.windows.com/UserActivity.ReadWrite.CreatedByApp"
。
注释
设备中继 API 不支持 Azure Active Directory (AAD) 帐户。
如果使用 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活动流服务
- 传递和修改此应用的用户通知
- 将应用活动读取和写入到用户的活动源
- Windows 通知服务
- 将设备连接到 Windows 通知服务
- Microsoft设备目录服务
- 查看设备列表
- 添加到设备和应用列表
- Microsoft命令服务
- 与用户设备通信
- 读取用户设备
为推送通知注册应用程序
向 Apple 注册应用程序以获取 Apple 推送通知 支持。 请务必记下收到的发送方 ID 和服务器密钥,因为稍后将需要它们。
注册后,必须在应用中将推送通知功能与连接的设备平台相关联。
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 设备,则无需完成此步骤。
为 Microsoft开发人员仪表板的跨设备体验功能注册应用。 这是与上述 MSA 和 AAD 应用注册不同的过程。 此过程的主要目标是将特定平台的应用标识映射到由已连接设备平台识别的跨平台应用标识。 此步骤还将使用与应用使用的移动平台相对应的本机推送通知服务启用发送通知。 对于 iOS,它允许通过 APNS – Apple 推送通知服务将通知发送到 iOS 应用终结点。
转到开发人员中心仪表板,从左侧导航窗格中导航到跨设备体验,然后选择配置新的跨设备应用。
开发人员中心加入过程需要执行以下步骤:
选择支持的平台 – 选择您的应用将覆盖并支持跨设备体验的平台。 对于 Graph 通知集成,可以根据所使用的平台从 Windows、Android 和/或 iOS 中进行选择。
提供应用 ID - 为所使用的每个平台提供应用 ID。 对于 iOS 应用,这是创建项目时分配给应用的包名称。 请注意,可以为每个平台添加不同的 ID(最多 10 个),在这种情况下,你可能有多个版本的同一应用甚至不同的应用,他们希望能够接收针对同一用户的应用服务器发送的相同通知。
提供或选择在上述 MSA/AAD 应用注册步骤中获取的 MSA 和/或 AAD 应用注册中的应用 ID。
提供与应用相关的本机通知平台(即适用于 Windows 的 WNS、适用于 Android 的 FCM 和/或适用于 iOS 的 APNS)的凭据,以便在发布面向用户的通知时从应用服务器传递通知。
最后,验证跨设备应用域,确保应用拥有该域的所有权,并将其用作应用的跨设备标识。
使用平台
创建平台的实例
要开始使用,只需实例化该平台即可。
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 通知通道
Project Rome SDK 允许应用订阅不同的频道,以便接收和管理各种类型的用户数据,包括图形通知、用户活动等。 这些都存储在 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 与连接设备服务器执行完全同步,该服务器包含您应用服务器发布的所有 MCDUserNotifications。 这将拉取与此肩部点击对应的应用服务器发布的完整通知有效负载(如果之前发布的通知但由于设备连接或其他问题而未在此应用客户端上收到,也会将其拉取)。 通过 SDK 不断执行这些实时同步,应用客户端能够访问此登录用户的 MCDUserNotification 数据馈送的本地缓存。 在这种情况下,MCDUserNotificationReader 使应用客户端能够访问此数据馈送 – 通过事件侦听器接收最新的通知有效负载,或访问完整的 MCDUserNotification 集合,该集合可用作用户通知历史记录的视图模型。
接收 MCDUserNotifications
首先,需要实例化 MCDUserNotificationReader,并获取读取器中已有的所有现有 MCDUserNotification(如果你有兴趣使用该信息获取尝试启用的体验)。 如果此特定设备终结点可能不是用户安装应用的唯一终结点或第一个终结点,则始终假定应用服务器已向此登录用户发布通知,这是安全的。 然后,添加一个事件侦听器,当连接设备平台完成同步并有新的更改需要通知你时,该侦听器会触发。 在图形通知的情况下,新更改可能是由您的应用服务器发布的新的传入 MCDUserNotification,或者是服务器或同一用户登录的其他已注册终端点产生的 MCDUserNotification 的更新、删除和过期事件。
小窍门
你将在此事件侦听器中处理主要业务逻辑,以及根据自己的方案“使用”通知有效负载的内容。 如果您当前使用 APNS 静默通知在操作系统级别的通知中心构建视觉通知,或者使用静默通知中的内容更新应用内的 UI,那么这就是进行这些操作的地方。
// 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 的通知。用户 A 在安装了应用客户端的电脑和手机上接收此通知。 该用户在电脑上单击该通知,并观察应用如何处理相应的任务。 然后,此电脑上的应用客户端将调用连接的设备平台 SDK 来更新相应用户通知的状态,以使此更新在所有用户的设备上同步。 其他应用客户端在实时收到此状态更新后,会从设备的通知中心/通知托盘/操作中心删除相应的可视警报/消息/toast 通知。 通知就是以这种方式在各个用户设备上全局消除的。
小窍门
MCDUserNotification 类当前提供 2 种类型的状态更新 - 可以修改 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);
}];
}
}