You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何从playbackStoreID获取MPMediaItem?解决Apple Music队列跨启动恢复问题

解决Apple Music歌曲队列跨启动保存与恢复的问题

嗨,这个问题我之前开发音乐播放器的时候也踩过坑!本地歌曲用persistentID确实靠谱,但Apple Music的媒体项因为是云端关联的,它的persistentID是动态生成的,跨应用启动就会失效,所以必须用playbackStoreID来持久化和恢复,下面是具体的实现思路和代码示例:

一、队列保存:区分本地与Apple Music歌曲

首先在保存队列时,我们需要给不同类型的歌曲标记不同的存储字段:

  • 本地歌曲:依然保存persistentID(UInt64类型)
  • Apple Music歌曲:保存playbackStoreID(字符串类型,这是唯一的云端标识)

代码示例(Swift):

// 假设你的当前队列是[MPMediaItem]类型的数组
func saveQueue(_ queue: [MPMediaItem]) {
    let queueData = queue.map { item -> [String: Any] in
        // 判断是否为Apple Music/云媒体项
        if let storeID = item.playbackStoreID, !storeID.isEmpty {
            return [
                "itemType": "appleMusic",
                "identifier": storeID
            ]
        } else {
            return [
                "itemType": "local",
                "identifier": item.persistentID
            ]
        }
    }
    
    // 这里可以选择存到UserDefaults、Core Data或者其他本地存储
    UserDefaults.standard.set(queueData, forKey: "savedPlaybackQueue")
}

二、队列恢复:通过playbackStoreID获取MPMediaItem

恢复时,针对保存的playbackStoreID,使用MPMediaQuery搭配对应的谓词来查询媒体项:

代码示例(Swift):

func restoreQueue() -> [MPMediaItem] {
    guard let savedQueue = UserDefaults.standard.array(forKey: "savedPlaybackQueue") as? [[String: Any]] else {
        return []
    }
    
    var restoredItems = [MPMediaItem]()
    
    for itemData in savedQueue {
        guard let itemType = itemData["itemType"] as? String,
              let identifier = itemData["identifier"] else {
            continue
        }
        
        if itemType == "appleMusic" {
            guard let storeID = identifier as? String else { continue }
            // 创建基于playbackStoreID的谓词
            let predicate = MPMediaPropertyPredicate(
                value: storeID,
                forProperty: MPMediaItemPropertyPlaybackStoreID
            )
            let query = MPMediaQuery()
            query.addFilterPredicate(predicate)
            
            // 取查询结果的第一个(理论上唯一)
            if let appleMusicItem = query.items?.first {
                restoredItems.append(appleMusicItem)
            }
        } else {
            guard let persistentID = identifier as? UInt64 else { continue }
            // 本地歌曲用persistentID查询
            let predicate = MPMediaPropertyPredicate(
                value: persistentID,
                forProperty: MPMediaItemPropertyPersistentID
            )
            let query = MPMediaQuery()
            query.addFilterPredicate(predicate)
            
            if let localItem = query.items?.first {
                restoredItems.append(localItem)
            }
        }
    }
    
    return restoredItems
}

三、关键注意事项

  • 权限检查:在查询媒体库前,必须确保已经获取了媒体库访问权限,否则查询会返回空。记得在启动时调用MPMediaLibrary.requestAuthorization()来请求权限。
  • 异常处理:如果用户之后从Apple Music库中移除了某首歌,查询会返回nil,这时候你需要从恢复的队列中移除该条目,或者给用户提示。
  • 避免误用persistentID:Apple Music媒体项的persistentID是本地临时标识,每次应用重启或系统同步后都可能变化,绝对不能用它来持久化云端歌曲。

这样处理后,你的应用就能稳定地跨启动保存和恢复包含Apple Music歌曲的播放队列了!

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

火山引擎 最新活动