如何在WKWebView中捕获通知?macOS Swift4桌面应用实现
解决macOS Swift 4应用中WKWebView网页通知的显示与拦截计数问题
我来帮你搞定这个问题!在Swift 4的macOS应用里,要让WKWebView加载的网页能显示通知,还能拦截计数,得从权限申请、JS注入拦截和原生处理这几个方面入手,下面是具体的实现步骤:
1. 先申请应用的通知权限
macOS里不管是原生还是网页触发的通知,都需要先获得用户授权。你可以在应用启动时(比如AppDelegate的applicationDidFinishLaunching方法里)请求权限:
import Cocoa import UserNotifications func applicationDidFinishLaunching(_ aNotification: Notification) { // 针对macOS 10.14+使用UNUserNotificationCenter,更早版本用NSUserNotificationCenter if #available(macOS 10.14, *) { UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in if granted { print("通知权限已授权") } else { print("通知权限被拒绝") } } } else { let notificationCenter = NSUserNotificationCenter.default notificationCenter.requestAuthorization(options: [.alert, .sound]) { granted, error in // 根据需求处理授权结果 } } }
2. 注入JavaScript拦截网页的Notification API
网页默认的new Notification()调用不会触发原生权限请求,也无法被直接拦截。我们需要注入JS脚本重写Notification构造函数,把通知的标题、内容等信息发送给原生代码,同时模拟网页的权限状态,避免网页因权限问题不发送通知:
在ViewController中配置WKWebView并实现拦截逻辑:
class ViewController: NSViewController, WKUIDelegate, WKScriptMessageHandler { var webView: WKWebView! var notificationCount = 0 // 通知计数器 override func viewDidLoad() { super.viewDidLoad() let contentController = WKUserContentController() // 添加脚本消息处理器,名字要和JS里的postMessage对应 contentController.add(self, name: "notificationHandler") // 注入重写Notification的JS脚本 let scriptSource = """ // 保存原始的Notification构造函数 const OriginalNotification = window.Notification; // 重写构造函数,拦截通知请求 window.Notification = function(title, options) { // 把通知数据发送给原生代码 window.webkit.messageHandlers.notificationHandler.postMessage({ title: title, body: options.body || '', icon: options.icon || '' }); // 如果需要让网页自身也能触发通知事件,可以取消下面的注释 // return new OriginalNotification(title, options); }; // 模拟网页已获得通知权限,避免网页反复请求权限 Object.defineProperty(window.Notification, 'permission', { get: () => 'granted' }); // 重写requestPermission方法,直接返回授权状态 window.Notification.requestPermission = function(callback) { callback('granted'); return Promise.resolve('granted'); }; """ let userScript = WKUserScript(source: scriptSource, injectionTime: .atDocumentStart, forMainFrameOnly: true) contentController.addUserScript(userScript) // 配置WKWebView并添加到视图 let config = WKWebViewConfiguration() config.userContentController = contentController webView = WKWebView(frame: view.bounds, configuration: config) webView.uiDelegate = self view.addSubview(webView) // 替换成你的目标网页URL if let url = URL(string: "https://your-target-webpage.com") { webView.load(URLRequest(url: url)) } } // 实现WKScriptMessageHandler方法,接收网页发来的通知数据 func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard message.name == "notificationHandler", let notificationData = message.body as? [String: String] else { return } // 更新通知计数器 notificationCount += 1 print("当前通知总数:\(notificationCount)") // 这里可以绑定UI控件(比如NSTextField)实时显示计数器 // 调用原生方法显示系统通知 showNativeNotification(title: notificationData["title"] ?? "", body: notificationData["body"] ?? "") } // 封装显示原生通知的方法,兼容不同macOS版本 func showNativeNotification(title: String, body: String) { if #available(macOS 10.14, *) { let content = UNMutableNotificationContent() content.title = title content.body = body content.sound = UNNotificationSound.default let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) UNUserNotificationCenter.current().add(request) { error in if let error = error { print("显示通知失败:\(error.localizedDescription)") } } } else { let notification = NSUserNotification() notification.title = title notification.informativeText = body notification.soundName = NSUserNotificationDefaultSoundName NSUserNotificationCenter.default.deliver(notification) } } // 记得在销毁时移除脚本处理器,避免内存泄漏 deinit { webView.configuration.userContentController.removeScriptMessageHandler(forName: "notificationHandler") } }
3. 关键细节说明
- JS注入时机:使用
.atDocumentStart确保脚本在网页加载前生效,保证所有网页的Notification调用都会被拦截。 - 权限模拟:重写
permission属性和requestPermission方法是为了让网页认为已经获得授权,避免网页因权限判断逻辑不发送通知。 - 计数器扩展:你可以把
notificationCount绑定到应用的状态栏、侧边栏等UI位置,实现更直观的计数展示。
这样操作后,网页发送的通知会被完整拦截,原生代码会显示系统通知,同时你的计数器也能正常累加啦!
内容的提问来源于stack exchange,提问作者BAndonovski
相关产品推荐
相关产品推荐

