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

Swift中WKMessageHandler消息未发送:YouTube Shorts时长获取排查

问题排查与修复方案

核心问题分析

你遇到的问题本质是YouTube IFrame API 尚未完成初始化,player对象还未就绪时就调用了sendCurrentTime(),导致player.getDuration()无法获取有效时长,甚至可能抛出错误中断消息发送流程。同时WebView的配置顺序也存在潜在问题。

分步排查与修复

1. 修正WebView配置顺序

当前代码先创建WKWebView再替换userContentController,可能导致配置未生效。正确流程是先配置好WKWebViewConfiguration,再用它初始化WebView:

func makeUIView(context: Context) -> WKWebView {
    let webConfiguration = WKWebViewConfiguration()
    webConfiguration.allowsInlineMediaPlayback = true
    
    // 先配置userContentController
    let userContentController = WKUserContentController()
    userContentController.add(context.coordinator, name: "observe")
    webConfiguration.userContentController = userContentController
    
    // 用配置好的configuration创建WebView
    let webview = WKWebView(frame: .zero, configuration: webConfiguration)
    webview.navigationDelegate = context.coordinator

    loadInitialContent(web: webview)
    
    return webview
}

2. 等待YouTube Player就绪后再获取时长

YouTube IFrame API的onYouTubeIframeAPIReady仅初始化player对象,真正就绪需要等待onReady事件。修改HTML脚本,在player就绪后调用sendCurrentTime():

<script>
    var tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

    var player;
    var isPlaying = false;
    function onYouTubeIframeAPIReady() {
        player = new YT.Player('player', {
            width: '100%',
            videoId: '\(link)',
            playerVars: { 'playsinline': 1, 'controls': 0},
            events: {
                'onReady': function(event) {
                    // 播放器就绪后才调用sendCurrentTime
                    sendCurrentTime();
                },
                'onStateChange': function(event) {
                    if (event.data === YT.PlayerState.ENDED) {
                        player.seekTo(0);
                        player.playVideo();
                    }
                }
            }
        });
    }
    function sendCurrentTime() {
        // 添加错误处理,避免player未就绪导致的报错
        if (!player) {
            console.error("Player not initialized");
            return;
        }
        const length = player.getDuration();
        // 检查时长是否有效,NaN表示未加载完成
        if (isNaN(length)) {
            // 延迟重试,给视频加载时长的时间
            setTimeout(sendCurrentTime, 500);
            return;
        }
        window.webkit.messageHandlers.observe.postMessage(length);
    }
</script>

3. 添加JavaScript执行错误日志

修改Swift中evaluateJavaScript的调用,添加错误回调,确认sendCurrentTime()执行时是否有报错:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    webView.evaluateJavaScript("sendCurrentTime()") { result, error in
        if let error = error {
            print("JS执行错误: \(error)")
        } else {
            print("JS执行结果: \(result ?? "无结果")")
        }
    }
}

(注:如果已改用player的onReady事件触发sendCurrentTime,可移除这个didFinish中的调用,避免重复触发)

4. 验证消息发送的有效性

在sendCurrentTime()中添加控制台日志,确认是否执行到postMessage步骤:

function sendCurrentTime() {
    console.log("sendCurrentTime called");
    if (!player) {
        console.error("Player not initialized");
        return;
    }
    const length = player.getDuration();
    console.log("Video duration:", length);
    if (isNaN(length)) {
        setTimeout(sendCurrentTime, 500);
        return;
    }
    console.log("Sending message to Swift:", length);
    window.webkit.messageHandlers.observe.postMessage(length);
}

通过Safari的Web Inspector查看WebView的控制台日志,确认这些信息是否输出。

额外注意事项

  • 确保YouTube视频ID(link参数)有效,无效ID会导致player初始化失败
  • 如果视频有版权限制,可能无法加载,导致getDuration()始终返回NaN
  • iOS上WKWebView的window.webkit.messageHandlers仅在配置了对应的userContentController名称时可用,确保name: "observe"两端一致

内容的提问来源于stack exchange,提问作者Ahmed Zaidan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 15:57:48