当应用程序处于非活动状态并运行代码时,Swift iOS 应用程序会收到推送通知
Posted
技术标签:
【中文标题】当应用程序处于非活动状态并运行代码时,Swift iOS 应用程序会收到推送通知【英文标题】:Swift iOS app receive push notification when app is inactive and run code 【发布时间】:2020-05-15 19:09:01 【问题描述】:平台
斯威夫特 5
ios 13+
xCode 11
节点 v14.2.0
Firebase/Firestore 最新
设置
Alice 向 Bob 发送推送通知,而 Bob 的电话是 .inactive
或 .background
。 Bob 的手机应该会收到通知并立即触发代码。
问题
这个问题有很多答案,但我能找到的大部分内容都围绕着破解 PushKit 和 CallKit 原生 API 以发送 .voIP
推送。根据这个问题 (iOS 13 not getting VoIP Push Notifications in background),Apple 不再允许您发送 .voIP
推送而不触发 CallKit 的本机电话响铃例程。
在 iOS 端,我在 AppDelegate.swift
中有以下位
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
registerForPushNotifications()
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
print(">>> I would like this to be triggered even if the app is asleep")
switch application.applicationState
case .active:
print(">>>>>>> the app is in [FOREGROUND]: \(userInfo)")
break
case .inactive, .background:
print(">>>>>>>> the app is in [BACKGROUND]: \(userInfo)")
break
default:
break
func registerForPushNotifications()
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter
.current()
.requestAuthorization(options:[.alert, .sound, .badge]) [weak self] granted, error in
guard granted else return
self?.getNotificationSettings()
func getNotificationSettings()
UNUserNotificationCenter.current().getNotificationSettings settings in
guard settings.authorizationStatus == .authorized else return
Messaging.messaging().delegate = self
DispatchQueue.main.async
// Register with Apple Push Notification service
UIApplication.shared.registerForRemoteNotifications()
/// cache token client side and save in `didRegisterForRemoteNotificationsWithDeviceToken`
if let token = Messaging.messaging().fcmToken
self.firebaseCloudMessagingToken = token
//@Use: listen for device token and save it in DB, so notifications can be sent to this phone
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
if (firebaseCloudMessagingToken != nil)
self.updateMyUserData(
name : nil
, pushNotificationToken: firebaseCloudMessagingToken!
)
func application(_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error)
///print(">>> Failed to register: \(error)")
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
// @NOTE: this fires when the app is open. So you can go the call screen right away
let payload = notification.request.content.userInfo as! [String:Any?]
let type = payload["notificationType"]
print(">> this fires if the app is currently open")
/// @NOTE: we are using backward compatible API to access user notification when the app is in the background
/// @source: https://firebase.google.com/docs/cloud-messaging/ios/receive#swift:-ios-10
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void)
print(" this fires when the user taps on the notification message")
在服务器/Node.js 端,我以这种方式发送推送通知:
// Declare app push notification provider for PushKit
const _ApnConfig_ =
token:
key : fileP8
, keyId : "ABCDEFG"
, teamId: "opqrst"
,
production: false
;
var apnProvider = new apn.Provider(_ApnConfig_);
exports.onSendNotification = functions.https.onRequest((request, response) =>
var date = new Date();
var timeStamp = date.getTime();
const deviceTok = "..."
var recepients = [apn.token( deviceTok )]
const notification = new apn.Notification();
notification.topic = "com.thisisnt.working"
notification.body = "Hello, world!";
notification.payload =
from: "node-apn"
, source: "web"
, aps:
"content-available": 1
, "data" : "custom_key":"custom value", "custom_key_2":"custom value 2"
;
notification.body = "Hello, world @ " + timeStamp;
return apnProvider.send(notification, recepients).then(function(res)
console.log("res.sent: ", res.sent)
console.log("res.failed: ", res.failed)
res.failed.forEach( (item) =>
console.log(" \t\t\t failed with error:", item.error)
)
return response.send("finished!");
).catch( function (error)
console.log("Faled to send message: ", error)
return response.send("failed!");
)
)
两者都很标准。我已将content-availabe
设置为1
。现在消息正在通过 Apple 推送通知中心显示并显示,它们只是没有按预期触发带有 didReceiveRemoteNotification
的块。
【问题讨论】:
【参考方案1】:您需要启用后台模式 - 远程通知功能。
要接收后台通知,您必须将远程通知后台模式添加到您的应用中。在 Signing & Capability 选项卡中,添加 Background Modes 功能,然后选中 Remote notification 复选框。
启用远程通知后台模式:
对于 watchOS,将此功能添加到您的 WatchKit 扩展中。
来源:Pushing Background Updates to Your App | Apple Developer Documentation
【讨论】:
以上是关于当应用程序处于非活动状态并运行代码时,Swift iOS 应用程序会收到推送通知的主要内容,如果未能解决你的问题,请参考以下文章
通过Swift中的触摸检测应用程序何时处于活动状态或非活动状态[重复]
当应用程序处于非活动状态时,GCM 推送通知不适用于 iOS
当应用程序处于非活动状态时可以接收 UIAccelerometer 更新吗?
当用户/选项卡/浏览器处于非活动状态时 JavaScript 播放声音