Swift中请求YouTube Shorts当前时长后视频无响应问题排查
问题原因分析
- 未清理的定时器引发资源泄漏:每次加载新视频时,旧的
SmartReelView实例及其关联的定时器并未被销毁,多个定时器会同时调用evaluateJavaScript,频繁干扰YouTube播放器的内部状态,最终导致播放异常甚至冻结。 - 错误的跨端通信逻辑:通过抛出
Error传递当前时间违背了evaluateJavaScript的设计意图——该方法的错误回调仅用于捕获JS执行异常,频繁触发错误处理会导致WebKit内部状态紊乱,进而影响播放器稳定性。 - 时间精度丢失与匹配失败:原正则表达式
Current Time: (\\d+)仅能匹配整数,而YouTube的getCurrentTime()返回浮点数,不仅会丢失时间精度,还可能因匹配失败引发不必要的逻辑执行,加重状态异常。
修复方案
1. 管理定时器生命周期,避免资源泄漏
在Coordinator中持有定时器引用,确保视图销毁或加载新内容时及时清理旧定时器:
class Coordinator: NSObject, WKNavigationDelegate { var parent: SmartReelView var updateTimer: Timer? // 持有定时器引用 init(_ parent: SmartReelView) { self.parent = parent } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { // 先销毁旧定时器,避免重复触发 updateTimer?.invalidate() updateTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in guard let self = self, self.parent.isPlaying else { return } webView.evaluateJavaScript("sendCurrentTime()", completionHandler: { (result, _) in // 直接接收返回值,不再依赖错误回调 if let currentTime = result as? Double { self.parent.currentTime = currentTime } }) } } // 视图销毁时自动清理定时器 deinit { updateTimer?.invalidate() } }
2. 修正JS函数,直接返回当前时间
修改sendCurrentTime函数,不再抛出错误,而是直接返回时间值,符合evaluateJavaScript的正常使用逻辑:
function sendCurrentTime() { var videoState = player.getPlayerState(); if (videoState === YT.PlayerState.PLAYING) { return player.getCurrentTime(); } return null; // 非播放状态返回null,避免无效值传递 }
3. 处理视频切换时的资源清理
在updateUIView中检测视频链接变化,及时清理旧定时器并重新加载内容:
func updateUIView(_ uiView: WKWebView, context: Context) { // 检查当前加载的视频是否与新链接一致,避免重复加载 let currentVideoId = uiView.configuration.userContentController.userScripts.first?.source.contains(link) ?? false if !currentVideoId { context.coordinator.updateTimer?.invalidate() loadInitialContent(web: uiView) } }
4. 优化WebView初始化逻辑
将滚动禁用逻辑移到初始化阶段,避免重复设置:
func makeUIView(context: Context) -> WKWebView { let webConfiguration = WKWebViewConfiguration() webConfiguration.allowsInlineMediaPlayback = true let webview = WKWebView(frame: .zero, configuration: webConfiguration) webview.navigationDelegate = context.coordinator webview.scrollView.isScrollEnabled = false loadInitialContent(web: webview) return webview }
额外优化建议
- 利用YouTube IFrame API的
onStateChange事件同步播放状态,减少定时器轮询的频率。 - 改用
WKUserContentController的消息推送机制(window.webkit.messageHandlers.postMessage),让JS主动向Swift发送当前时间,替代定时器轮询,提升性能与稳定性。
内容的提问来源于stack exchange,提问作者Ahmed Zaidan
相关产品推荐
相关产品推荐

