Firebase Cloud Messaging (FCM) 如何切换 Apple 推送通知服务 (APN) 的环境?

Yuk*_*oto 5 ios firebase firebase-cloud-messaging

在 Apple 推送通知服务 (APN) 中,服务器端开发人员必须选择环境类型(沙箱或生产)作为 HTTP/2 URL(api.sandbox.push.apple.com 或 api.push.apple.com)。[1]

另一方面,在 APN 上的 Firebase 云消息传递 (FCM) 中,似乎没有明确的接口来指定环境类型。[ 2 ]

所以我猜 FCM 在内部以某种方式决定环境类型,但我不知道它如何检测环境类型。

有人了解这方面的知识吗?任何见解都会有所帮助。谢谢!

Ben*_*rth 5

一些先决条件信息

  • 当应用程序在调试、发布或配置文件配置中通过 Xcode 构建和运行,或者应用程序通过方法分发时,始终使用沙箱环境development
  • App Store Connect (App Store and TestFlight)通过、Ad Hoc、方法分发应用程序时使用生产环境Enterprise。有关更多信息,请参阅“分发您的应用程序以进行 Beta 测试和发布”
  • 要了解使用的分发模式/ APNs 环境,您必须阅读配置文件。在 iOS、watchOS 和 tvOS 上,它是embedded.mobileprovision,在 macOS 或 Catalyst 上,它是embedded.provisionprofile。您无法读取App.entitlements,因为该文件并不总是可用。相反,embedded.mobileprovision包含一个字典(XML 格式)。这是我从测试应用程序中提取的该文件的示例。除其他外,它还包含:
<key>Entitlements</key>
<dict>
<key>aps-environment</key>
<string>development</string>
...
Run Code Online (Sandbox Code Playgroud)

如果你自己生成一个(存档Xcode项目),你可以查看xcarchive/Users/username/Library/Developer/Xcode/Archives/2021-08-28/projectName\ 28-08-2021,\ 08.17.xcarchive/Products/Applications/projectName.app/embedded.mobileprovision)的包内容,并且它在finder预览中很好地显示。

Firebase iOS SDK中还有一条注释:

 *  @param type  The type of APNs token. Debug builds should use
 *  FIRMessagingAPNSTokenTypeSandbox. Alternatively, you can supply
 *  FIRMessagingAPNSTokenTypeUnknown to have the type automatically
 *  detected based on your provisioning profile.
Run Code Online (Sandbox Code Playgroud)

Firebase 的解决方案

您可以阅读FIRMessagingTokenManager.m,或阅读我对不同文件的分析:

Firebase iOS SDK中,如果您传递不传递类型(沙箱/生产)或显式传递FIRMessagingAPNSTokenTypeUnknown,则此代码将运行:

  if (type == FIRMessagingAPNSTokenTypeUnknown) {
    isSandboxApp = FIRMessagingIsSandboxApp();
  }
Run Code Online (Sandbox Code Playgroud)

这是

BOOL FIRMessagingIsSandboxApp(void) {
  static BOOL isSandboxApp = YES;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    isSandboxApp = !FIRMessagingIsProductionApp();
  });
  return isSandboxApp;
}
Run Code Online (Sandbox Code Playgroud)

FIRMessagingIsProductionApp是一个长达 119 行的方法。它有什么作用?它几乎总是默认为生产应用程序,有很多 Firebase 特定配置逻辑,并检查生产是否在 iOS 模拟器上运行、应用程序是否交付 AppStore 或 TestFlight

从根本上来说,它检查embedded.provisionprofileor embedded.mobileprovision(这就是plistMap生成方式):

// plistMap is loaded from the provisioning profile in a multi step process.
NSString *apsEnvironment = [plistMap valueForKeyPath:kEntitlementsAPSEnvironmentKey];

  if ([apsEnvironment isEqualToString:kAPSEnvironmentDevelopmentValue]) {
    return NO;
  }
Run Code Online (Sandbox Code Playgroud)

他们在配置文件中引用以下键:

#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
static NSString *const kEntitlementsAPSEnvironmentKey = @"Entitlements.aps-environment";
#else
static NSString *const kEntitlementsAPSEnvironmentKey =
    @"Entitlements.com.apple.developer.aps-environment";
#endif
static NSString *const kAPSEnvironmentDevelopmentValue = @"development";
Run Code Online (Sandbox Code Playgroud)

如果您有兴趣了解如何读取配置文件,请阅读源文件。

  • 创建配置文件的完整路径
  • 从文件路径加载数据
  • 清理数据(配置文件包含0“停止”ASCII 解析器的内容,或者大于 127 的值,这是无效的。)
  • 将数据转换为字符串
  • 使用NSScanner扫描字符串。他们这样做是因为配置文件包含更多的非 xml/非 plist 结构。查看示例文件。
  • 将此字符串转换回数据。
  • 使用以下方法将此数据转换为字典NSPropertyListSerialization
  • 查找ProvisionedDevices密钥,如果有,则它是开发人员配置文件。
  • 从字典中获取环境kEntitlementsAPSEnvironmentKey

Firebase 服务器如何知道要使用哪个端点?

最后,一旦 Firebase iOS SDK 知道设备(和 APNs 设备令牌)正在生产/开发中运行,它就可以告诉 APNs 提供者(与 APNs 连接的服务器)使用正确的api.push.apple.com:443端点api.sandbox.push.apple.com:443api.development.push.apple.com:443(它只是一个指向沙箱的 CNAME)。该值isProductionisSandbox布尔值可能会与 Firebase 数据库中的 APNs 设备令牌一起存在。


Yuk*_*oto 1

我在文档中找到了答案FIRInstanceIDAPNSTokenType

http://cocoadocs.org/docsets/FirebaseInstanceID/1.0.6/Constants/FIRInstanceIDAPNSTokenType.html

应用程序的 APNS 令牌类型。如果令牌类型设置为 UNKNOWN InstanceID 将隐式尝试从配置文件中找出实际令牌类型。

因此,答案是“实际令牌类型由配置文件确定”,可能由“aps-environment”键确定。