72 lines
2.6 KiB
Swift
72 lines
2.6 KiB
Swift
import Foundation
|
|
|
|
/// Persists the current playback queue and position to UserDefaults so the app
|
|
/// can restore them after being terminated by iOS (memory pressure, system kill).
|
|
///
|
|
/// Restoration is intentionally conservative:
|
|
/// - Only offline/downloaded songs are auto-resumed (streams need live URLs)
|
|
/// - Playback restarts paused at the saved position — the user taps Play
|
|
/// - If the saved songs no longer exist in the library the state is discarded
|
|
struct PlaybackStateStore {
|
|
|
|
static let shared = PlaybackStateStore()
|
|
|
|
private let queueKey = "playback_saved_queue"
|
|
private let indexKey = "playback_saved_index"
|
|
private let timeKey = "playback_saved_time"
|
|
private let songIdKey = "playback_saved_song_id"
|
|
|
|
// MARK: - Save
|
|
|
|
/// Call whenever the current song, queue, or playback position changes.
|
|
/// Writes are cheap — just JSON-encoding Song structs to UserDefaults.
|
|
func save(queue: [Song], index: Int, currentTime: TimeInterval, currentSongId: String?) {
|
|
guard !queue.isEmpty else { return }
|
|
|
|
let encoder = JSONEncoder()
|
|
if let data = try? encoder.encode(queue) {
|
|
UserDefaults.standard.set(data, forKey: queueKey)
|
|
}
|
|
UserDefaults.standard.set(index, forKey: indexKey)
|
|
UserDefaults.standard.set(currentTime, forKey: timeKey)
|
|
UserDefaults.standard.set(currentSongId, forKey: songIdKey)
|
|
}
|
|
|
|
// MARK: - Load
|
|
|
|
struct RestoredState {
|
|
let queue: [Song]
|
|
let index: Int
|
|
let currentTime: TimeInterval
|
|
let currentSong: Song
|
|
}
|
|
|
|
/// Returns saved state if valid, nil if nothing was saved or data is stale.
|
|
func load() -> RestoredState? {
|
|
guard let data = UserDefaults.standard.data(forKey: queueKey),
|
|
let queue = try? JSONDecoder().decode([Song].self, from: data),
|
|
!queue.isEmpty else { return nil }
|
|
|
|
let index = UserDefaults.standard.integer(forKey: indexKey)
|
|
let time = UserDefaults.standard.double(forKey: timeKey)
|
|
|
|
guard queue.indices.contains(index) else { return nil }
|
|
|
|
return RestoredState(
|
|
queue: queue,
|
|
index: index,
|
|
currentTime: time,
|
|
currentSong: queue[index]
|
|
)
|
|
}
|
|
|
|
// MARK: - Clear
|
|
|
|
/// Call after the user explicitly stops playback or clears the queue.
|
|
func clear() {
|
|
UserDefaults.standard.removeObject(forKey: queueKey)
|
|
UserDefaults.standard.removeObject(forKey: indexKey)
|
|
UserDefaults.standard.removeObject(forKey: timeKey)
|
|
UserDefaults.standard.removeObject(forKey: songIdKey)
|
|
}
|
|
}
|