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
相关产品推荐
相关产品推荐

