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

无法触发Completion Block求助:playAudio回调不执行排查

playAudio回调未触发的原因及解决办法

问题描述

在playAudio方法中,completion(audioFileURL)处断点能正常触发,但调用该方法时传入的回调闭包(print("lala")所在的闭包)从未执行,导致歌曲无法播放。相关代码如下:

playAudio方法代码

func playAudio(url: URL, completion: (URL) -> ()) {
        if savedFiles.contains(where: { $0.key == url }) {
            let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            let audioFileURL = documentsDirectoryURL.appendingPathComponent(url.lastPathComponent)
            
            do {
                let data = savedFiles[url]
                try data?.write(to: audioFileURL)
                completion(audioFileURL)
            } catch {
                delegate?.didCatchError()
            }
        } else {
            downloadSong(url: url) { [weak self] result in
                guard let self else { return }
                switch result {
                case .success(let result):
                    if let url = result.first?.key {
                        self.playAudio(url: url, completion: { _ in })
                    }
                default: break
                }
            }
        }
    }

调用代码

trackDownloader.playAudio(url: url) { [weak self] url in
                print("lala")
                guard let self else { return }
                self.player = AVPlayer(url: url)
                self.playerToggle(play: isPlaying)
                self.startPlayTimestamp = startPlayTimestamp.getTimeStamp()
                self.createProgressObserver()
            }

核心原因

递归调用playAudio时丢失了原始回调闭包:

  • 当目标文件未在savedFiles中时,会进入else分支执行下载逻辑。
  • 下载成功后,代码递归调用playAudio(url: url, completion: { _ in }),这里传入的是空闭包,而非用户最初调用时传入的包含print("lala")的回调闭包。
  • 后续即使进入if分支执行completion(audioFileURL),执行的也是这个空闭包,自然不会触发调用处的业务逻辑。

修复方案

修改else分支中的递归调用,将原始的completion闭包传递进去:

func playAudio(url: URL, completion: (URL) -> ()) {
        if savedFiles.contains(where: { $0.key == url }) {
            let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            let audioFileURL = documentsDirectoryURL.appendingPathComponent(url.lastPathComponent)
            
            do {
                let data = savedFiles[url]
                try data?.write(to: audioFileURL)
                completion(audioFileURL)
            } catch {
                delegate?.didCatchError()
            }
        } else {
            downloadSong(url: url) { [weak self] result in
                guard let self else { return }
                switch result {
                case .success(let result):
                    if let url = result.first?.key {
                        // 传递原始的completion闭包,而非空闭包
                        self.playAudio(url: url, completion: completion)
                    }
                default: break
                }
            }
        }
    }

额外检查点

  1. 确认downloadSong成功后,是否将下载的url和对应数据正确存入savedFiles,否则递归调用可能再次进入else分支,陷入循环。
  2. 检查result.first?.key是否确实是目标URL,避免因URL不匹配导致后续逻辑异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 03:23:12