Skip to content

Commit

Permalink
Merge branch 'develop' into small-fix-for-downloading-cancelling
Browse files Browse the repository at this point in the history
  • Loading branch information
rnr committed Jul 9, 2024
2 parents 53ec9fd + bd270b7 commit 8ecb4f6
Show file tree
Hide file tree
Showing 19 changed files with 464 additions and 456 deletions.
11 changes: 5 additions & 6 deletions Core/Core/Data/Persistence/CorePersistenceProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@ public protocol CorePersistenceProtocol {
func set(userId: Int)
func getUserID() -> Int?
func publisher() -> AnyPublisher<Int, Never>
func addToDownloadQueue(blocks: [CourseBlock], downloadQuality: DownloadQuality)
func nextBlockForDownloading() -> DownloadDataTask?
func addToDownloadQueue(blocks: [CourseBlock], downloadQuality: DownloadQuality) async
func nextBlockForDownloading() async -> DownloadDataTask?
func updateDownloadState(id: String, state: DownloadState, resumeData: Data?)
func deleteDownloadDataTask(id: String) throws
func deleteDownloadDataTask(id: String) async throws
func saveDownloadDataTask(_ task: DownloadDataTask)
func downloadDataTask(for blockId: String) -> DownloadDataTask?
func downloadDataTask(for blockId: String, completion: @escaping (DownloadDataTask?) -> Void)
func getDownloadDataTasks(completion: @escaping ([DownloadDataTask]) -> Void)
func getDownloadDataTasksForCourse(_ courseId: String, completion: @escaping ([DownloadDataTask]) -> Void)
func getDownloadDataTasks() async -> [DownloadDataTask]
func getDownloadDataTasksForCourse(_ courseId: String) async -> [DownloadDataTask]
}

public final class CoreBundle {
Expand Down
115 changes: 48 additions & 67 deletions Core/Core/Network/DownloadManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public protocol DownloadManagerProtocol {
func publisher() -> AnyPublisher<Int, Never>
func eventPublisher() -> AnyPublisher<DownloadManagerEvent, Never>

func addToDownloadQueue(blocks: [CourseBlock]) throws
func addToDownloadQueue(blocks: [CourseBlock]) async throws

func getDownloadTasks() async -> [DownloadDataTask]
func getDownloadTasksForCourse(_ courseId: String) async -> [DownloadDataTask]
Expand All @@ -119,10 +119,9 @@ public protocol DownloadManagerProtocol {
func deleteFile(blocks: [CourseBlock]) async
func deleteAllFiles() async

func fileUrl(for blockId: String) async -> URL?

func resumeDownloading() throws
func fileUrl(for blockId: String) -> URL?

func resumeDownloading() async throws
func isLargeVideosSize(blocks: [CourseBlock]) -> Bool

func removeAppSupportDirectoryUnusedContent()
Expand All @@ -142,7 +141,6 @@ public enum DownloadManagerEvent {
}

public class DownloadManager: DownloadManagerProtocol {

// MARK: - Properties

public var currentDownloadTask: DownloadDataTask?
Expand Down Expand Up @@ -173,7 +171,9 @@ public class DownloadManager: DownloadManagerProtocol {
self.appStorage = appStorage
self.connectivity = connectivity
self.backgroundTask()
try? self.resumeDownloading()
Task {
try? await self.resumeDownloading()
}
}

// MARK: - Publishers
Expand All @@ -197,37 +197,29 @@ public class DownloadManager: DownloadManagerProtocol {
}

public func getDownloadTasks() async -> [DownloadDataTask] {
await withCheckedContinuation { continuation in
persistence.getDownloadDataTasks { downloads in
continuation.resume(returning: downloads)
}
}
await persistence.getDownloadDataTasks()
}

public func getDownloadTasksForCourse(_ courseId: String) async -> [DownloadDataTask] {
await withCheckedContinuation { continuation in
persistence.getDownloadDataTasksForCourse(courseId) { downloads in
continuation.resume(returning: downloads)
}
}
await persistence.getDownloadDataTasksForCourse(courseId)
}

public func addToDownloadQueue(blocks: [CourseBlock]) throws {
public func addToDownloadQueue(blocks: [CourseBlock]) async throws {
if userCanDownload() {
persistence.addToDownloadQueue(
await persistence.addToDownloadQueue(
blocks: blocks,
downloadQuality: downloadQuality
)
currentDownloadEventPublisher.send(.added)
guard !isDownloadingInProgress else { return }
try newDownload()
try await newDownload()
} else {
throw NoWiFiError()
}
}

public func resumeDownloading() throws {
try newDownload()
public func resumeDownloading() async throws {
try await newDownload()
}

public func cancelDownloading(courseId: String, blocks: [CourseBlock]) async throws {
Expand All @@ -240,46 +232,46 @@ public class DownloadManager: DownloadManagerProtocol {
downloaded.forEach {
currentDownloadEventPublisher.send(.canceled($0))
}
try newDownload()
try await newDownload()
}

public func cancelDownloading(task: DownloadDataTask) async throws {
downloadRequest?.cancel()
do {
if let fileUrl = await fileUrl(for: task.id) {
if let fileUrl = fileUrl(for: task.id) {
try FileManager.default.removeItem(at: fileUrl)
}
try persistence.deleteDownloadDataTask(id: task.id)
try await persistence.deleteDownloadDataTask(id: task.id)
currentDownloadEventPublisher.send(.canceled(task))
} catch {
NSLog("Error deleting file: \(error.localizedDescription)")
}
try newDownload()
try await newDownload()
}

public func cancelDownloading(courseId: String) async throws {
let tasks = await getDownloadTasksForCourse(courseId)
await cancel(tasks: tasks)
currentDownloadEventPublisher.send(.courseCanceled(courseId))
downloadRequest?.cancel()
try newDownload()
try await newDownload()
}

public func cancelAllDownloading() async throws {
let tasks = await getDownloadTasks().filter { $0.state != .finished }
await cancel(tasks: tasks)
currentDownloadEventPublisher.send(.allCanceled)
downloadRequest?.cancel()
try newDownload()
try await newDownload()
}

public func deleteFile(blocks: [CourseBlock]) async {
for block in blocks {
do {
if let fileURL = await fileUrl(for: block.id) {
if let fileURL = fileUrl(for: block.id) {
try FileManager.default.removeItem(at: fileURL)
}
try persistence.deleteDownloadDataTask(id: block.id)
try await persistence.deleteDownloadDataTask(id: block.id)
currentDownloadEventPublisher.send(.deletedFile(block.id))
} catch {
debugLog("Error deleting file: \(error.localizedDescription)")
Expand All @@ -290,7 +282,7 @@ public class DownloadManager: DownloadManagerProtocol {
public func deleteAllFiles() async {
let downloadsData = await getDownloadTasks()
for downloadData in downloadsData {
if let fileURL = await fileUrl(for: downloadData.id) {
if let fileURL = fileUrl(for: downloadData.id) {
do {
try FileManager.default.removeItem(at: fileURL)
} catch {
Expand All @@ -301,36 +293,25 @@ public class DownloadManager: DownloadManagerProtocol {
currentDownloadEventPublisher.send(.clearedAll)
}

public func fileUrl(for blockId: String) async -> URL? {
await withCheckedContinuation { continuation in
persistence.downloadDataTask(for: blockId) { [weak self] data in
guard let data = data, data.url.count > 0, data.state == .finished else {
continuation.resume(returning: nil)
return
}
let path = self?.videosFolderUrl
let fileName = data.fileName
continuation.resume(returning: path?.appendingPathComponent(fileName))
}
}
}

public func fileUrl(for blockId: String) -> URL? {
guard let data = persistence.downloadDataTask(for: blockId),
data.url.count > 0,
data.state == .finished else { return nil }
data.state == .finished
else {
return nil
}
let path = videosFolderUrl
let fileName = data.fileName
return path?.appendingPathComponent(fileName)
}

// MARK: - Private Intents

private func newDownload() throws {
private func newDownload() async throws {
guard userCanDownload() else {
throw NoWiFiError()
}
guard let downloadTask = persistence.nextBlockForDownloading() else {
guard let downloadTask = await persistence.nextBlockForDownloading() else {
isDownloadingInProgress = false
return
}
Expand Down Expand Up @@ -391,34 +372,32 @@ public class DownloadManager: DownloadManagerProtocol {
)
self.currentDownloadTask?.state = .finished
self.currentDownloadEventPublisher.send(.finished(download))
try? self.newDownload()
Task {
try? await self.newDownload()
}
}
}

private func waitingAll() {
persistence.getDownloadDataTasks { [weak self] tasks in
guard let self else { return }
Task {
for task in tasks.filter({ $0.state == .inProgress }) {
self.persistence.updateDownloadState(
id: task.id,
state: .waiting,
resumeData: nil
)
self.currentDownloadEventPublisher.send(.added)
}
self.downloadRequest?.cancel()
}
private func waitingAll() async {
let tasks = await persistence.getDownloadDataTasks()
for task in tasks.filter({ $0.state == .inProgress }) {
self.persistence.updateDownloadState(
id: task.id,
state: .waiting,
resumeData: nil
)
self.currentDownloadEventPublisher.send(.added)
}
self.downloadRequest?.cancel()
}

private func cancel(tasks: [DownloadDataTask]) async {
for task in tasks {
do {
if let fileUrl = await fileUrl(for: task.id) {
if let fileUrl = fileUrl(for: task.id) {
try FileManager.default.removeItem(at: fileUrl)
}
try persistence.deleteDownloadDataTask(id: task.id)
try await persistence.deleteDownloadDataTask(id: task.id)
} catch {
debugLog("Error deleting file: \(error.localizedDescription)")
}
Expand All @@ -429,9 +408,11 @@ public class DownloadManager: DownloadManagerProtocol {
backgroundTaskProvider.eventPublisher()
.sink { [weak self] state in
guard let self else { return }
switch state {
case.didBecomeActive: try? self.resumeDownloading()
case .didEnterBackground: self.waitingAll()
Task {
switch state {
case.didBecomeActive: try? await self.resumeDownloading()
case .didEnterBackground: await self.waitingAll()
}
}
}
.store(in: &cancellables)
Expand Down
8 changes: 4 additions & 4 deletions Course/Course/Data/CourseRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Core

public protocol CourseRepositoryProtocol {
func getCourseBlocks(courseID: String) async throws -> CourseStructure
func getLoadedCourseBlocks(courseID: String) throws -> CourseStructure
func getLoadedCourseBlocks(courseID: String) async throws -> CourseStructure
func blockCompletionRequest(courseID: String, blockID: String) async throws
func getHandouts(courseID: String) async throws -> String?
func getUpdates(courseID: String) async throws -> [CourseUpdate]
Expand Down Expand Up @@ -50,8 +50,8 @@ public class CourseRepository: CourseRepositoryProtocol {
return parsedStructure
}

public func getLoadedCourseBlocks(courseID: String) throws -> CourseStructure {
let localData = try persistence.loadCourseStructure(courseID: courseID)
public func getLoadedCourseBlocks(courseID: String) async throws -> CourseStructure {
let localData = try await persistence.loadCourseStructure(courseID: courseID)
return parseCourseStructure(course: localData)
}

Expand Down Expand Up @@ -85,7 +85,7 @@ public class CourseRepository: CourseRepositoryProtocol {
}

public func getSubtitles(url: String, selectedLanguage: String) async throws -> String {
if let subtitlesOffline = persistence.loadSubtitles(url: url + selectedLanguage) {
if let subtitlesOffline = await persistence.loadSubtitles(url: url + selectedLanguage) {
return subtitlesOffline
} else {
let result = try await api.requestData(CourseEndpoint.getSubtitles(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import CoreData
import Core

public protocol CoursePersistenceProtocol {
func loadEnrollments() throws -> [Core.CourseItem]
func loadEnrollments() async throws -> [Core.CourseItem]
func saveEnrollments(items: [Core.CourseItem])
func loadCourseStructure(courseID: String) throws -> DataLayer.CourseStructure
func loadCourseStructure(courseID: String) async throws -> DataLayer.CourseStructure
func saveCourseStructure(structure: DataLayer.CourseStructure)
func saveSubtitles(url: String, subtitlesString: String)
func loadSubtitles(url: String) -> String?
func loadSubtitles(url: String) async -> String?
func saveCourseDates(courseID: String, courseDates: CourseDates)
func loadCourseDates(courseID: String) throws -> CourseDates
}
Expand Down
2 changes: 1 addition & 1 deletion Course/Course/Domain/CourseInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public class CourseInteractor: CourseInteractorProtocol {
}

public func getLoadedCourseBlocks(courseID: String) async throws -> CourseStructure {
return try repository.getLoadedCourseBlocks(courseID: courseID)
return try await repository.getLoadedCourseBlocks(courseID: courseID)
}

public func blockCompletionRequest(courseID: String, blockID: String) async throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,12 +346,12 @@ public class CourseContainerViewModel: BaseCourseViewModel {
return tasks
}

func continueDownload() {
func continueDownload() async {
guard let blocks = waitingDownloads else {
return
}
do {
try manager.addToDownloadQueue(blocks: blocks)
try await manager.addToDownloadQueue(blocks: blocks)
} catch let error {
if error is NoWiFiError {
errorMessage = CoreLocalization.Error.wifi
Expand Down Expand Up @@ -481,7 +481,7 @@ public class CourseContainerViewModel: BaseCourseViewModel {
do {
switch state {
case .available:
try manager.addToDownloadQueue(blocks: blocks)
try await manager.addToDownloadQueue(blocks: blocks)
case .downloading:
try await manager.cancelDownloading(courseId: courseStructure?.id ?? "", blocks: blocks)
case .finished:
Expand All @@ -507,7 +507,9 @@ public class CourseContainerViewModel: BaseCourseViewModel {
self.router.dismiss(animated: true)
},
okTapped: {
self.continueDownload()
Task {
await self.continueDownload()
}
self.router.dismiss(animated: true)
},
type: .default(positiveAction: CourseLocalization.Alert.accept, image: nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public class CourseVerticalViewModel: BaseCourseViewModel {
do {
switch state {
case .available:
try manager.addToDownloadQueue(blocks: blocks)
try await manager.addToDownloadQueue(blocks: blocks)
downloadState[vertical.id] = .downloading
case .downloading:
try await manager.cancelDownloading(courseId: vertical.courseId, blocks: blocks)
Expand Down
Loading

0 comments on commit 8ecb4f6

Please sign in to comment.