如何在应用程序未运行并点击推送通知时调试远程推送通知?
Posted
技术标签:
【中文标题】如何在应用程序未运行并点击推送通知时调试远程推送通知?【英文标题】:How to debug remote push notification when app is not running and tap push notification? 【发布时间】:2021-05-10 07:23:46 【问题描述】:当应用程序运行并收到推送通知时,会调用 didReceive。
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
)
因此,当调用上述委托时,我会使用收到的有效负载呈现一个屏幕。这里没有问题。
当应用程序没有运行并且用户点击通知时,它应该显示与上面相同的屏幕。它不起作用,因为我没有在 didFinishLaunchingWithOptions 中添加代码。
所以,然后我添加了以下代码-
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool
......
if let userInfo = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] as? [AnyHashable: Any]
......
return true
但这不起作用,我无法调试,因为在调试模式下我必须从后台终止应用程序并点击通知,但在这种情况下调试器将无法工作。我尝试了替代方法,即显示警报,但警报也不起作用
let aps = remoteNotif["aps"] as? [AnyHashable: Any]
let string = "\n Custom: \(String(describing: aps))"
let string1 = "\n Custom: \(String(describing: remoteNotif))"
DispatchQueue.main.asyncAfter(deadline: .now() + 5) [weak self] in
if var topController = application.windows.first?.rootViewController
while let presentedViewController = topController.presentedViewController
topController = presentedViewController
let ac = UIAlertController(title: string1, message: string, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
topController.present(ac, animated: true)
我应该如何解决这个问题?
【问题讨论】:
你有后台模式功能吗? @ElTomato - 这有什么关系?因为我正在点击通知。根据苹果的说法,后台模式是-“应用程序提供的需要它在后台运行的服务”。后台没有运行任何东西。应用正在接收推送,我只需点击通知即可打开应用。 你写过“当应用程序没有运行并且用户点击通知时,它应该呈现与上面相同的屏幕”,但如果你这么说...... 【参考方案1】:“但在这种情况下,调试器将无法工作”不是真的!即使 Xcode 没有启动它,您也可以在启动时附加调试器。
编辑方案,然后在“运行”操作中,在“信息”下的“启动”处,单击第二个单选按钮:“等待启动可执行文件”。运行应用程序;它没有启动。现在通过推送通知启动应用程序。调试器工作正常。
【讨论】:
见我的biteinteractive.com/tricks-for-testing-external-links 谢谢让我看看。 11年来从未使用过这个技巧。如果在 xcode 未运行时可以激活调试器,那就太棒了。我只是想测试一下为什么 launchOptions 在 didFinishLaunchingWithOptions 中不起作用【参考方案2】:您必须创建一个 NotificationServiceExtension 并在那里处理有效负载
在 XCode 中,
-
选择您的项目
从左下角选择添加目标
添加通知服务扩展
然后尝试做这样的事情。以下代码适用于 FCM,但您可以根据自己的有效负载进行修改。
例如-
import UserNotifications
import UIKit
class NotificationService: UNNotificationServiceExtension
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void)
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent
// Modify the notification content here...
bestAttemptContent.title = "\(bestAttemptContent.title)"
guard let fcmOptions = bestAttemptContent.userInfo["fcm_options"] as? [String: Any] else
contentHandler(bestAttemptContent)
return
guard let imageURLString = fcmOptions["image"] as? String else
contentHandler(bestAttemptContent)
return
getMediaAttachment(for: imageURLString) [weak self] (image, error) in
guard let self = self,
let image = image,
let fileURL = self.saveImageAttachment(image: image, forIdentifier: "attachment.png")
else
// bestAttemptContent.body = "Error - \(String(describing: error))"
contentHandler(bestAttemptContent)
return
let imageAttachment = try? UNNotificationAttachment(
identifier: "image",
url: fileURL,
options: nil)
if let imageAttachment = imageAttachment
bestAttemptContent.attachments = [imageAttachment]
contentHandler(bestAttemptContent)
override func serviceExtensionTimeWillExpire()
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent
contentHandler(bestAttemptContent)
private func saveImageAttachment(image: UIImage, forIdentifier identifier: String) -> URL?
let tempDirectory = URL(fileURLWithPath: NSTemporaryDirectory())
let directoryPath = tempDirectory.appendingPathComponent(
ProcessInfo.processInfo.globallyUniqueString,
isDirectory: true)
do
try FileManager.default.createDirectory(
at: directoryPath,
withIntermediateDirectories: true,
attributes: nil)
let fileURL = directoryPath.appendingPathComponent(identifier)
guard let imageData = image.pngData() else
return nil
try imageData.write(to: fileURL)
return fileURL
catch
return nil
private func getMediaAttachment(for urlString: String, completion: @escaping (UIImage?, Error?) -> Void)
guard let url = URL(string: urlString) else
completion(nil, NotificationError.cannotParseURL)
return
ImageDownloader.shared.downloadImage(forURL: url) (result) in
switch result
case .success(let image):
completion(image, nil)
case .failure(let error):
completion(nil, error)
enum NotificationError: Error
case cannotParseURL
【讨论】:
【参考方案3】:我已经通过实现 sceneDelegate willConnectTo
方法解决了这个问题。不需要在didFinishLaunchingWithOptions中处理
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
//Remote notification response
if let response = connectionOptions.notificationResponse
print(response.notification.request.content.userInfo)
....
这就够了
【讨论】:
以上是关于如何在应用程序未运行并点击推送通知时调试远程推送通知?的主要内容,如果未能解决你的问题,请参考以下文章