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

Swift中构建兼容多数据类型的播放列表数组并实现曲目切换

解决方案:用协议抽象统一两种Track类型

这个需求用Swift的协议抽象就能完美解决——两种Track本质上都是可播放的曲目,只是存储/来源不同,我们只需要提取它们的共同行为和属性,就能让播放列表同时兼容两者。

1. 定义通用可播放曲目协议

首先创建一个协议,把两种Track都具备的核心属性(比如曲目名、专辑名)抽象出来,如果后续有其他共同属性(比如时长、艺术家ID)也可以补充进去:

protocol PlayableTrack {
    var trackTitle: String { get }
    var albumTitle: String { get }
    // 可选:添加唯一标识符,避免同名同专辑歌曲的索引判断错误
    // var uniqueTrackId: String { get }
}

注意:如果JSONTrack是引用类型(Class),可以给协议加上AnyObject限制(protocol PlayableTrack: AnyObject),和Track(NSManagedObject是引用类型)保持一致;如果是值类型(Struct),就不用加。

2. 让两种Track类型遵守协议

分别给Track和JSONTrack扩展,让它们自动遵守这个协议(因为它们本来就有对应的属性,不需要额外实现):

// 本地存储的Track(NSManagedObject)
extension Track: PlayableTrack {}

// 网络获取的JSONTrack
extension JSONTrack: PlayableTrack {}

3. 修改播放列表与当前曲目类型

把原来的[Track]类型的播放列表,改成协议类型数组;同时当前播放的曲目也改成协议类型:

// 原来的 var playlistTracks = [Track]()
var playlistTracks = [PlayableTrack]()

// 原来的 var track: Track?
var track: PlayableTrack?

4. 调整上下曲切换逻辑

只需要把原来代码中具体的Track类型替换成PlayableTrack,核心逻辑完全不用变——协议已经保证了所有元素都具备trackTitle和albumTitle属性:

@objc fileprivate func handlePrevTrack() {
    guard !playlistTracks.isEmpty, let currentTrack = track else { return }
    
    // 查找当前曲目在列表中的索引
    let currentTrackIndex = playlistTracks.index { tr in
        currentTrack.trackTitle == tr.trackTitle && currentTrack.albumTitle == tr.albumTitle
        // 如果用了唯一标识符,改成下面的判断更准确:
        // currentTrack.uniqueTrackId == tr.uniqueTrackId
    }
    
    guard let index = currentTrackIndex else { return }
    
    let prevTrack: PlayableTrack
    if index == 0 {
        // 第一首的上一曲是最后一首
        prevTrack = playlistTracks[playlistTracks.count - 1]
    } else {
        prevTrack = playlistTracks[index - 1]
    }
    
    self.track = prevTrack
}

@objc func handleNextTrack() {
    guard !playlistTracks.isEmpty, let currentTrack = track else { return }
    
    let currentTrackIndex = playlistTracks.index { tr in
        currentTrack.trackTitle == tr.trackTitle && currentTrack.albumTitle == tr.albumTitle
        // currentTrack.uniqueTrackId == tr.uniqueTrackId
    }
    
    guard let index = currentTrackIndex else { return }
    
    let nextTrack: PlayableTrack
    if index == playlistTracks.count - 1 {
        // 最后一首的下一曲是第一首
        nextTrack = playlistTracks[0]
    } else {
        nextTrack = playlistTracks[index + 1]
    }
    
    self.track = nextTrack
}

额外优化建议

用trackTitle+albumTitle判断曲目索引存在风险——如果有同名同专辑的不同歌曲,会导致索引判断错误。建议给协议添加一个唯一标识符属性(比如uniqueTrackId),用它来做索引判断,这样逻辑会更可靠。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:59:46