我正在通过 APN 向我的 iOS 应用程序发送静默推送通知。有效负载似乎正确,并且 APNs 接受通知(响应显示 success=true)。但是,应用程序在收到通知时不会触发任何日志或后台处理。
我之前有一个NodeJS后端项目。在这个项目中,我成功发送静默推送,然后我切换到 Spring Boot 项目,我开始看不到任何静默推送。然后,当我回到我的nodejs项目时,令人惊讶的是,当我再次发送静默推送时,我在设备上看不到任何日志。但它曾经有效。
我检查过的内容:
content-available
设置为 1
。
我的应用程序未处于终止状态,它处于前台或在后台挂起。
我在物理设备上使用此 iOS 版本进行了测试
15.3.1
和 17.5
当我发送简单的通知时,我可以毫无问题地看到我的 swift 项目中的日志和物理设备上的通知。
不,我的证书尚未过期,如果它有问题,我将无法看到正常的通知。
我的手机未处于省电模式,并且应用程序设置中的
“Background App Refresh”
选项已打开。
应用程序功能:
Background Modes
通过 Remote Notifications
启用。
当我在静默推送的有效负载中仅提供徽章属性时,我会成功记录
“Received Push Notification: \(userInfo)”
。但是当我提供徽章以外的其他属性时,我无法显示任何日志。
我在 Spring Boot 项目中使用
com.eatthepath.pushy
库来处理 APNs 请求。
更新:
我在 Apple 开发者控制台中使用新的 CloudKit 工具进行了测试,我在其中创建了静默通知并将其发送到我的设备。根据日志,通知已成功发送到设备,但我在设备上仍然看不到任何日志。
重要更新2: 我在 iOS 版本 16.5.1 的设备上进行了尝试,静默推送工作正常。造成差异的原因可能是什么?
我的 Swift 项目:
import SwiftUI
import UIKit
import UserNotifications
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if granted {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error)")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
logInfo("Received Push Notification: \(userInfo)")
if let aps = userInfo["aps"] as? [String: AnyObject], aps["content-available"] as? Int == 1 {
logInfo("This is a silent push notification")
//DO SOME WORK
completionHandler(.newData)
}
completionHandler(.newData)
}
}
我的 Spring Boot 项目:(KOTLIN)
package io.initial.apptrackbe.service
import com.eatthepath.pushy.apns.*
import com.eatthepath.pushy.apns.auth.ApnsSigningKey
import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification
import com.eatthepath.pushy.apns.util.TokenUtil
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.io.File
import java.util.concurrent.CompletableFuture
import com.eatthepath.pushy.apns.DeliveryPriority
import com.eatthepath.pushy.apns.PushType
import com.eatthepath.pushy.apns.util.ApnsPayloadBuilder
@Service
class PushNotificationService(
@Value("\${apns.key-file}") private val keyFilePath: String,
@Value("\${apns.team-id}") private val teamId: String,
@Value("\${apns.key-id}") private val keyId: String
) {
private val apnsClient: ApnsClient
init {
try {
apnsClient = ApnsClientBuilder()
.setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST)
.setSigningKey(
ApnsSigningKey.loadFromPkcs8File(
File(keyFilePath),
teamId,
keyId
)
)
.build()
} catch (e: Exception) {
throw IllegalStateException("Failed to initialize APNs client", e)
}
}
//THIS SILENT PUSH DIDINT WORK
fun sendSilentNotification(deviceToken: String): CompletableFuture<PushNotificationResponse<SimpleApnsPushNotification>> {
val sanitizedToken = TokenUtil.sanitizeTokenString(deviceToken)
val payloadBuilder = object : ApnsPayloadBuilder() {
override fun build(): String {
setContentAvailable(true)
return super.buildPayloadMap().toString()
}
override fun buildMdmPayload(p0: String?): String {
return super.buildPayloadMap().toString()
}
}
val payload = payloadBuilder.build()
val notification = SimpleApnsPushNotification(
sanitizedToken,
"io.myapp.package",
payload,
null,
DeliveryPriority.IMMEDIATE,
PushType.BACKGROUND,
null
)
return apnsClient.sendNotification(notification)
}
//THIS SIMPLE PUSH WORKING
fun sendExampleNotification(deviceToken: String, threadId: String): CompletableFuture<PushNotificationResponse<SimpleApnsPushNotification>> {
val sanitizedToken = TokenUtil.sanitizeTokenString(deviceToken)
val payload = """
{
"aps": {
"alert": "Hello App With Group: $threadId",
"sound": "default",
"badge": 1,
"thread-id": "$threadId"
}
}
""".trimIndent()
val notification = SimpleApnsPushNotification(
sanitizedToken,
"io.myapp.package",
payload
)
return apnsClient.sendNotification(notification)
}
}
APNS 的回应:
{token='myDeviceToken', payload='{aps={content-available=1}}', invalidationTime=null, priority=IMMEDIATE, pushType=BACKGROUND, topic='io.myapp.package', collapseId='null', apnsId=null}, success=true, apnsId=********, apnsUniqueId=*********, rejectionReason='null', tokenExpirationTimestamp=null}
静默推送通知在 iOS 上未触发可能是由于多种原因造成的,例如不正确的通知负载、应用程序状态限制或设备设置。以下是排查和解决问题的综合指南:
1。确保有效负载正确
{
"aps": {
"content-available": 1
},
"customKey": "customValue"
}
要点: content-available: 1 向 iOS 发出信号,表明这是静默通知。 避免包含警报、徽章或声音键,因为它们会将其变成可见的通知。 有效负载大小不得超过 4 KB。
2。检查应用程序权限 无声推送通知与后台获取权限相关。 确保应用程序已被授予接收通知的权限:
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
if granted {
print("Notifications permission granted")
} else {
print("Notifications permission denied: \(String(describing: error))")
}
}
在后台模式下启用远程通知: 转到目标设置 > 签名和功能 > 后台模式。
检查远程通知。
3.应用程序状态和后台获取 静默通知仅在以下情况下有效:
应用程序位于前台或后台。后台获取是 已启用。
启用后台获取:转到目标设置 > 签名和 功能 > 背景模式。检查后台获取。实施 背景获取