diff --git a/Sources/Nuke/ImageTask.swift b/Sources/Nuke/ImageTask.swift index fdd29ab94..98cb3d820 100644 --- a/Sources/Nuke/ImageTask.swift +++ b/Sources/Nuke/ImageTask.swift @@ -207,11 +207,26 @@ public final class ImageTask: Hashable, CustomStringConvertible, @unchecked Send // MARK: Events func process(_ event: Event) { + switch event { + case .progress(let progress): + currentProgress = progress + case .finished: + // TODO: do we need to check state? + _ = setState(.completed) + default: + break + } + let context = sync { _context } + process(event, in: context) + onEvent?(event, self) + pipeline?.imageTask(self, didProcessEvent: event) + } + + private func process(_ event: Event, in context: AsyncContext) { context.events?.1.yield(event) switch event { case .progress(let progress): - currentProgress = progress context.progress?.1.yield(progress) case .preview(let response): context.stream?.1.yield(response) @@ -221,16 +236,12 @@ public final class ImageTask: Hashable, CustomStringConvertible, @unchecked Send context.stream?.1.finish(throwing: CancellationError()) continuation?.resume(throwing: CancellationError()) case .finished(let result): - _ = setState(.completed) let result = result.mapError { $0 as Error } context.events?.1.finish() context.progress?.1.finish() context.stream?.1.yield(with: result) continuation?.resume(with: result) } - - onEvent?(event, self) - pipeline?.imageTask(self, didProcessEvent: event) } /// An event produced during the runetime of the task. diff --git a/Sources/Nuke/Pipeline/ImagePipeline.swift b/Sources/Nuke/Pipeline/ImagePipeline.swift index 81fee8c0d..e8af322d2 100644 --- a/Sources/Nuke/Pipeline/ImagePipeline.swift +++ b/Sources/Nuke/Pipeline/ImagePipeline.swift @@ -288,6 +288,7 @@ public final class ImagePipeline: @unchecked Sendable { private func makeStartedImageTask(with request: ImageRequest, isDataTask: Bool = false) -> ImageTask { let task = ImageTask(taskId: nextTaskId, request: request, isDataTask: isDataTask) task.pipeline = self + // TODO: we've added a dealy to startImageTask and that can be a problem task.task = Task { try await withUnsafeThrowingContinuation { continuation in queue.async { @@ -305,6 +306,10 @@ public final class ImagePipeline: @unchecked Sendable { return task.process(.finished(.failure(.pipelineInvalidated))) } // TODO: Check this and other .cancelled callbacks + // The problem is that if cancelled is called before that + // `startImageTask` is called, then the tasks[task] is empty + // in `ImageTask` callback and cancelation logic fails to run + // This can happen if the task is cancelled from `imageTaskCreated` guard task.state != .cancelled else { return task.process(.cancelled) }