在iOS中使用Firebase Cloud Messaging进行推送通知

Firebase云消息传递(FCM)

Cloud Messaging是Firebase的免费酷功能之一。 将推送通知从服务器发送到FCM很容易,它可以处理iOS,Android和Web的其余部分。 我将在本文中跳过设置。 您可以按照精心编写的设置指南,将Firebase添加到您的项目中。

您可以通过Firebase控制台或应用服务器连接到Firebase Cloud Messaging。 Firebase控制台非常简单,但这是手动工作。 我将在这篇文章中介绍应用服务器。 通过FCM发送通知的方式有三种 。 您可以将通知发送到一个设备设备组主题 。 我想向特定用户的所有设备发送通知,并且我不想存储令牌。 在这里,我将跳过仅将通知发送到一种设备的方式。 设备组也用于将一个用户的所有设备分组。 这样,您可以将通知发送到一个用户的所有设备。 因此,它们两个都需要存储令牌。 如果您需要将每个用户的消息发送到多个设备,Firebase建议使用设备组。 但是正如我所说,我不想存储令牌。 因此,我将只关注本文中的主题。 如果您更喜欢存储令牌并进行处理,则可以使用其他两种方式。

什么是主题 ? 主题基于发布/订阅模型。 每个设备可以订阅不同的主题。 服务器向主题发送请求。 FCM处理休息,所有订阅的设备都收到通知。 这样简单而强大的方法。 但是,作为每项服务,它都有优点和缺点。 主题未针对快速交付进行优化。 因此,发送通知和接收通知之间可能会有延迟。 主题针对吞吐量进行了优化。

现实生活场景

我们有一个微服务,当它接收到事件时会创建推送有效载荷。 我们可爱的推送程序不了解客户端设备,也没有任何存储空间。 当基础系统上发生新事件时,它将得到通知。 然后,它将通知发送到移动平台。

我们的推送程序将HTPP请求发送到FCM。 您可以在此处了解有关FCM的服务器实现的更多信息。 我们的基本请求结构如下:

  https://fcm.googleapis.com/fcm/send 
内容类型:application / json
授权:key = SERVER_API_KEY
  { 
“ condition”:“主题中的“ condition1”和主题中的“ condition2””,
“通知”:{
“ category”:“ notification_category”,
“ title_loc_key”:“ notification_title”,
“ body_loc_key”:“ notification_body”,
“徽章”:1
},
“数据”:{
“ data_type”:“ notification_data_type”,
“ data_id”:“ 111111”,
“ data_detail”:“ FOO”,
“ data_detail_body”:“ BAR”
}
}

该请求通过FCM将适当的结构发送到APN。 您可以从此处了解有关通知有效负载键及其值类型的更多信息。 另外,您还可以将要在应用程序中处理的其他信息放在“数据”部分。 在请求中,您将看到条件键。 它用于向主题发送推送通知。 每个条件都是这里的主题。 该请求将通知发送到已订阅condition1condition2的设备 。 我们可以在那里使用不同的条件。 例如,我们可以使用||。 代替&& 。 在这种情况下,FCM将通知发送给预订condition1condition2的设备 。 可以组合主题并创建诸如condition1 &&(condition2 || condition3)之类的复杂条件。 唯一的限制是高级只允许一个操作员。 无法创建条件(如condition1 && condition1 && condition3) ,必须将其分组。

我们有足够的有关服务器和FCM的信息。 让我们在iOS方面前进。 (我不会涉及通知处理。这完全是另一个主题。)

FCM使iOS方面的一切变得如此简单。 有几件事情要做。 创建APNs证书并完成设置指南中的必要步骤后,您应该为FCM配置应用。 为此,请在AppDelegateapplication:didFinishLaunchingWithOptions:方法中添加FIRApp.configure()这行。 此方法使用GoogleService-Info.plist文件配置您的应用。

Firebase使用混淆方法将APNs令牌映射到FCM注册令牌。 也用于捕获有关邮件传递信息的分析。 我更喜欢自己禁止打扰和处理这些步骤。 要禁用滚动,您需要在应用程序Info.plist文件中将FirebaseAppDelegateProxyEnabled密钥设置为NO 。 从现在开始,我们负责APN的关键映射和分析数据。 让我们先将APNs令牌映射到FCM令牌。 方法如下:

  func application(_ application:UIApplication, 
didRegisterForRemoteNotificationsWithDeviceToken deviceToken:数据){
#if PROD_BUILD
FIRInstanceID.instanceID()。setAPNSToken(deviceToken,类型:.prod)
#其他
FIRInstanceID.instanceID()。setAPNSToken(deviceToken,类型:.sandbox)
#万一
}

这里重要的是FIRInstanceIDAPNSTokenType 。 共有三种类型的.prod.sandbox.none 。 要获得通知,必须使用生产或Sanbox令牌类型之一。

没有方法混乱的最后要做的就是将消息分析发送到FCM。 因此,我们将能够从Firebase控制台使用Google Analytics(分析)。 方法如下:

  func application(_ application:UIApplication, 
didReceiveRemoteNotification userInfo:[AnyHashable:任意],
fetchCompletionHandlercompleteHandler:@转义(UIBackgroundFetchResult)-> Swift.Void){
FIRMessaging.messaging()。appDidReceiveMessage(userInfo)
}

我们设置了FCM,将令牌映射到FCM令牌并发送了消息分析。 剩下的最后一件事就是订阅主题。 订阅新主题真的很容易。 这只是一行:

  FIRMessaging.messaging()。subscribe(toTopic:“ / topics / condition1”) 
//订阅时不要忘记添加/ topics /前缀

如果FCM中没有主题,则在您订阅后它将自动创建一个新主题。 您还记得我们谈到过在HTTP请求的条件键中组合主题。 同样在应用程序端,我们可以订阅多个主题以获取更多通知。

让我们回到案例。 我们想向特定用户发送新通知。 为此,我们使用唯一的用户ID创建了主题。 这听起来像是对主题的滥用,但这是我们案例中的唯一方法。 每个用户都订阅名为“ userID_11111”之类的主题。 在我们可爱的推送程序中,我们使用了用户ID作为条件。

大多数应用程序都有用户登录和注销案例。 用户注销后,您不想向他们发送通知。 要涵盖这些情况,您可以退订主题。 FCM从主题列表中删除该设备令牌(总是有主题。您不能删除主题)。 取消订阅类似于订阅:

  FIRMessaging.messaging()。unsubscribe(fromTopic:“ / topics / condition1”) 
//退订时不要忘记添加/ topics /前缀

到目前为止,我们讨论了发送推送通知和FCM主题。 FCM非常强大且简单。 但是像其他服务一样,我们也面临一些问题。

第一个问题是订阅新主题可能需要一段时间才能在FCM上使用。 如果您在订阅后没有直接收到通知,则应等待几分钟。

另一个问题是在Android方面,处理推送通知有所不同。 如果要创建自定义精美通知,则应发送其他有效负载。 在上述HTPP请求中,我们有通知数据密钥。 如果Android应用获取通知密钥,则无法对其进行控制。 它无法自定义通知,并且设备会显示具有给定标题和正文的默认通知。 要创建自定义样式通知,他们需要使用数据键并创建通知。 面对此解决方案时,我们想到了iOSandroid主题。 我们另外创建了两个主题。 iOS设备订阅iOS主题,Android设备订阅Android主题。 我们可爱的推送程序针对每个事件发送两个请求。 对于Android, 条件键的值为“主题中的’userID_1111’,主题中的&android’android’”,并且它没有通知键。 对于iOS,它是“主题中的’userID_1111’和主题中的’iOS’” 。 也许这不是处理这些案件的理想方法。 但是它工作得很好,我们不在乎设备令牌的处理。 如果您有更好的想法或方法,请告诉我。

最后一个问题是为Firebase配置沙箱和生产类型。 大多数iOS应用程序没有针对不同发行模式的单独目标。 对于Debug,Test和Release等不同的配置,您必须在Firebase Console中设置不同的应用程序。 然后,您将拥有用于不同配置的单独的GoogleService-Info.plist。 重命名它们并使用这些文件配置Firebase将不起作用。 我能找到的唯一解决方案是运行脚本。 将不同Firebase应用程序的不同.plist文件放在单独的文件夹下。 您可以将它们命名为prod,beta等。在应用目标中的“构建阶段”中,在“编译源”之前添加运行以下脚本。 (不要忘记使用文件夹命名和路径配置以下脚本)

  isRelease = expr“ $ GCC_PREPROCESSOR_DEFINITIONS”:“。* release = \([0-9] * \)”` 
RESOURCE_PATH = $ {SRCROOT} / firebase / beta
如果[$ isRelease = 1]; 然后
RESOURCE_PATH = $ {SRCROOT} / firebase / release
科幻
BUILD_APP_DIR = $ {BUILT_PRODUCTS_DIR} / $ {PRODUCT_NAME} .app
echo“将$ {RESOURCE_PATH}下的所有文件复制到$ {BUILD_APP_DIR}
cp -v“ $ {RESOURCE_PATH} /” *“ $ {BUILD_APP_DIR} /”

该脚本根据应用程序构建配置获取找到的配置文件(GoogleService-Info.plist)。 并在编译源代码之前将其复制到构建应用程序目录。 然后,Firebase会将.plist文件标识为相同,就像只有一个文件一样。 您可以配置脚本并仅复制.plist文件。 逻辑是一样的。

就是这个。 易于设置,易于配置。 将FCM与Firebase的其他功能结合使用的可能性更大。 也许这将是其他帖子的主题。