import Foundation /// Der Lebenslauf eines Downloads. /// /// Bewusst als geschlossene Aufzählung mit einer geprüften Übergangstabelle: /// die Engine läuft im Hintergrund weiter, während die App beendet ist, und /// beim nächsten Start muss aus dem gespeicherten Zustand eindeutig /// hervorgehen, was zu tun ist. public enum DownloadState: String, Sendable, Equatable, Codable, CaseIterable { /// Steht in der Warteschlange, hat noch keinen Platz bekommen. case queued /// Überträgt gerade. case downloading /// Vom Nutzer angehalten — der Wiederaufnahme-Datensatz liegt bereit. case paused /// Wartet auf Bedingungen: WLAN fehlt, Speicherlimit erreicht. case waiting case completed case failed /// Ob aus diesem Zustand heraus übertragen werden darf. public var isTransferable: Bool { switch self { case .queued, .waiting: true case .downloading, .paused, .completed, .failed: false } } /// Ob der Eintrag Platz auf dem Gerät belegt. public var occupiesStorage: Bool { switch self { // Angehaltene und laufende Downloads haben bereits Daten auf der // Platte. Wer sie beim Speicherlimit nicht mitzählt, wundert sich, // warum das Gerät trotz gesetzter Grenze volläuft. case .downloading, .paused, .completed: true case .queued, .waiting, .failed: false } } public func canTransition(to next: DownloadState) -> Bool { switch (self, next) { case (.queued, .downloading), (.queued, .waiting), (.queued, .failed): true case (.waiting, .downloading), (.waiting, .queued), (.waiting, .failed): true case (.downloading, .paused), (.downloading, .completed), (.downloading, .failed), (.downloading, .waiting): true case (.paused, .queued), (.paused, .downloading), (.paused, .failed): true // Ein gescheiterter Download darf neu in die Warteschlange — das ist // der Wiederholen-Knopf. case (.failed, .queued): true case (.completed, _): false default: false } } }