You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS中使用WKScriptMessageHandler监听WhatsApp Web消息删除事件时messageDeleted未触发的问题

iOS中使用WKScriptMessageHandler监听WhatsApp Web消息删除事件时messageDeleted未触发的问题

看起来你已经做了不少基础排查,不过从你的描述和代码来看,问题大概率出在JavaScript的监听逻辑或者消息传递的环节上,我来一步步帮你梳理可能的原因和解决办法:

首先,先确认JavaScript里的postMessage真的执行了吗?

你说Observer在元素移除时触发,但有没有可能是extractMessageContent函数报错,导致postMessage根本没发出去?或者messageElement没找到?

先给你的JS代码加几个调试日志,然后在iOS端监听WebView的控制台输出,看看JS里到底发生了什么:

  1. 在JS的mutation.removedNodes回调里,找到messageElement后立刻加console.log:
if (messageElement) {
    console.log("Detected deleted message, ID:", messageElement.getAttribute('data-id'));
    // 然后再执行postMessage和extractMessageContent
}
  1. 在iOS端添加控制台日志监听,这样就能看到WebView里的JS日志:
// 在WebView配置里加这个
config.preferences.javaScriptEnabled = true;
let logger = ConsoleMessageLogger()
userContentController.add(logger, name: "console")

// 自定义一个控制台消息处理器
class ConsoleMessageLogger: NSObject, WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print("JS Console:", message.body)
    }
}
// 同时注入一段JS把console.log转发给iOS
let consoleScript = """
(function(){
    const originalLog = console.log;
    console.log = function(...args) {
        originalLog.apply(console, args);
        window.webkit.messageHandlers.console.postMessage(args.join(' '));
    };
})();
"""
userContentController.addUserScript(WKUserScript(source: consoleScript, injectionTime: .atDocumentStart, forMainFrameOnly: true))

这样你就能确认JS里到底有没有走到postMessage那一步。

检查MutationObserver的消息元素检测逻辑

你现在用node.querySelector('[data-id]')来查找被删除的消息元素,但mutation.removedNodes里的node本身可能就是带data-id的消息元素,不需要再querySelector。比如当一条消息被删除时,被移除的节点就是消息本身,而不是它的父容器。修改这段逻辑:

mutation.removedNodes.forEach((node) => {
    if (node.nodeType === Node.ELEMENT_NODE) {
        // 先检查当前节点是否是消息元素,再找子节点
        let messageElement = node.hasAttribute('data-id') ? node : node.querySelector('[data-id]');
        if (messageElement) {
            console.log("Found deleted message element:", messageElement);
            // 继续执行postMessage
        }
    }
});

给extractMessageContent加错误捕获

如果你的extractMessageContent函数里有报错(比如正则匹配失败、某个DOM元素找不到),会直接导致整个回调终止,postMessage根本发不出去。给它加个try-catch:

function extractMessageContent(element) {
    try {
        let messageContainer = element.querySelector('[data-pre-plain-text]');
        let messageText = element.querySelector('.selectable-text');
        let mediaContainer = element.querySelector('.media-container');
        let timestampElement = element.querySelector('div[data-pre-plain-text]');
        let timestamp = '';
        let sender = '';
        let mediaUrl = '';
        let mediaName = '';
        
        if (timestampElement) {
            let preText = timestampElement.getAttribute('data-pre-plain-text');
            if (preText) {
                // 修正正则的转义问题
                let matches = preText.match(/\[(.*?)\].*?([^:]+):/);
                if (matches) {
                    timestamp = matches[1];
                    sender = matches[2].trim();
                }
            }
        }
        if (mediaContainer) {
            let mediaElement = mediaContainer.querySelector('img, video');
            if (mediaElement) {
                mediaUrl = mediaElement.src;
                mediaName = mediaElement.alt || mediaElement.src.split('/').pop();
            }
        }
        return {
            id: element.getAttribute('data-id'),
            text: messageText ? messageText.innerText : '',
            sender: sender,
            timestamp: timestamp,
            hasMedia: !!mediaContainer,
            mediaUrl: mediaUrl,
            mediaName: mediaName
        };
    } catch (err) {
        console.error("Error extracting message content:", err);
        // 即使出错,也要返回基础数据,保证postMessage能发出去
        return { id: element.getAttribute('data-id'), error: err.message };
    }
}

检查iOS端的消息解析是否失败

你现在的代码里,如果message.body无法转成[String: Any],会直接return,不会有任何日志。先把这段的日志加上,看看是不是解析失败了:

@objc func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    print("Received raw message:", message.body, "Type:", type(of: message.body))
    guard let body = message.body as? [String: Any],
          let type = body["type"] as? String,
          let data = body["data"] as? [String: Any] else { 
              print("Failed to parse message body, raw content:", message.body)
              return 
          }
    // 原来的switch逻辑
}

有时候WKWebView会把复杂的JS对象转成NSString(比如存在循环引用或者无法序列化的类型),这时候你需要在JS里把对象转成JSON字符串,再在iOS端解析:

// JS里修改postMessage:
window.webkit.messageHandlers.messageObserver.postMessage(JSON.stringify({
    type: 'messageDeleted',
    data: extractMessageContent(messageElement)
}));
// iOS端修改解析:
guard let rawString = message.body as? String,
      let data = rawString.data(using: .utf8),
      let body = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { 
          print("Failed to parse JSON message:", message.body)
          return 
      }
// 后续解析body的逻辑不变

考虑WhatsApp Web的DOM结构变化或删除逻辑

WhatsApp Web有时候不会直接把删除的消息从DOM里移除,而是修改消息内容为“消息已删除”,或者给元素加特定的class。这时候你的MutationObserver只监听childList变化就不够了,需要同时监听属性和内容变化:

messageObserver.observe(chatContainer, {
    childList: true,
    subtree: true,
    attributes: true,
    characterData: true
});

然后在mutation回调里处理属性/内容变化的情况:

} else if (mutation.type === 'attributes' || mutation.type === 'characterData') {
    let target = mutation.target;
    let messageElement = target.closest('[data-id]');
    if (messageElement) {
        // 检查是否是已删除的消息,比如文本包含“已删除”,或者有特定class
        let isDeleted = messageElement.textContent.includes("已删除") || messageElement.classList.contains("message-deleted");
        if (isDeleted) {
            console.log("Message marked as deleted:", messageElement.getAttribute('data-id'));
            window.webkit.messageHandlers.messageObserver.postMessage({
                type: 'messageDeleted',
                data: extractMessageContent(messageElement)
            });
        }
    }
}

最后,确认聊天容器的选择器是否正确

WhatsApp Web的DOM结构可能更新了,原来的#main div.copyable-area可能不是现在的聊天消息容器。你可以在Chrome开发者工具里打开WhatsApp Web,找到聊天消息的父容器,复制它的选择器。比如现在可能是div[data-testid="conversation-panel-messages"]或者其他,替换掉observeChat里的选择器。

按照这些步骤一步步排查,应该能找到问题所在。大概率是JS里的某个环节没走到postMessage,或者iOS端解析消息失败导致的。

备注:内容来源于stack exchange,提问作者Vivek Warde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 03:22:59