|
@@ -62,26 +62,40 @@ public struct ImageLoadingResult: Sendable {
|
|
|
/// When a download starts in Kingfisher, the involved methods always return you an instance of ``DownloadTask``. If you
|
|
/// When a download starts in Kingfisher, the involved methods always return you an instance of ``DownloadTask``. If you
|
|
|
/// need to cancel the task during the download process, you can keep a reference to the instance and call ``cancel()``
|
|
/// need to cancel the task during the download process, you can keep a reference to the instance and call ``cancel()``
|
|
|
/// on it.
|
|
/// on it.
|
|
|
-public final class DownloadTask: Sendable {
|
|
|
|
|
|
|
+public final class DownloadTask: @unchecked Sendable {
|
|
|
|
|
+
|
|
|
|
|
+ private let propertyQueue = DispatchQueue(label: "com.onevcat.Kingfisher.DownloadTaskPropertyQueue")
|
|
|
|
|
|
|
|
init(sessionTask: SessionDataTask, cancelToken: SessionDataTask.CancelToken) {
|
|
init(sessionTask: SessionDataTask, cancelToken: SessionDataTask.CancelToken) {
|
|
|
- self.sessionTask = sessionTask
|
|
|
|
|
- self.cancelToken = cancelToken
|
|
|
|
|
|
|
+ _sessionTask = sessionTask
|
|
|
|
|
+ _cancelToken = cancelToken
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ init() { }
|
|
|
|
|
|
|
|
|
|
+ private var _sessionTask: SessionDataTask? = nil
|
|
|
|
|
+
|
|
|
/// The ``SessionDataTask`` object associated with this download task. Multiple `DownloadTask`s could refer to the
|
|
/// The ``SessionDataTask`` object associated with this download task. Multiple `DownloadTask`s could refer to the
|
|
|
/// same `sessionTask`. This is an optimization in Kingfisher to prevent multiple downloading tasks for the same
|
|
/// same `sessionTask`. This is an optimization in Kingfisher to prevent multiple downloading tasks for the same
|
|
|
/// URL resource simultaneously.
|
|
/// URL resource simultaneously.
|
|
|
///
|
|
///
|
|
|
/// When you call ``DownloadTask/cancel()``, this ``SessionDataTask`` and its cancellation token will be passed
|
|
/// When you call ``DownloadTask/cancel()``, this ``SessionDataTask`` and its cancellation token will be passed
|
|
|
/// along. You can use them to identify the cancelled task.
|
|
/// along. You can use them to identify the cancelled task.
|
|
|
- public let sessionTask: SessionDataTask
|
|
|
|
|
|
|
+ public private(set) var sessionTask: SessionDataTask? {
|
|
|
|
|
+ get { propertyQueue.sync { _sessionTask! } }
|
|
|
|
|
+ set { propertyQueue.sync { _sessionTask = newValue } }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ private var _cancelToken: SessionDataTask.CancelToken? = nil
|
|
|
|
|
+
|
|
|
/// The cancellation token used to cancel the task.
|
|
/// The cancellation token used to cancel the task.
|
|
|
///
|
|
///
|
|
|
/// This is solely for identifying the task when it is cancelled. To cancel a ``DownloadTask``, call
|
|
/// This is solely for identifying the task when it is cancelled. To cancel a ``DownloadTask``, call
|
|
|
/// ``DownloadTask/cancelToken``.
|
|
/// ``DownloadTask/cancelToken``.
|
|
|
- public let cancelToken: SessionDataTask.CancelToken
|
|
|
|
|
|
|
+ public private(set) var cancelToken: SessionDataTask.CancelToken? {
|
|
|
|
|
+ get { propertyQueue.sync { _cancelToken } }
|
|
|
|
|
+ set { propertyQueue.sync { _cancelToken = newValue } }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/// Cancel this single download task if it is running.
|
|
/// Cancel this single download task if it is running.
|
|
|
///
|
|
///
|
|
@@ -100,8 +114,20 @@ public final class DownloadTask: Sendable {
|
|
|
/// ``ImageDownloader/cancel(url:)``. If you need to cancel all downloading tasks of an ``ImageDownloader``,
|
|
/// ``ImageDownloader/cancel(url:)``. If you need to cancel all downloading tasks of an ``ImageDownloader``,
|
|
|
/// use ``ImageDownloader/cancelAll()``.
|
|
/// use ``ImageDownloader/cancelAll()``.
|
|
|
public func cancel() {
|
|
public func cancel() {
|
|
|
|
|
+ guard let sessionTask, let cancelToken else { return }
|
|
|
sessionTask.cancel(token: cancelToken)
|
|
sessionTask.cancel(token: cancelToken)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ public var isInitialized: Bool {
|
|
|
|
|
+ propertyQueue.sync {
|
|
|
|
|
+ _sessionTask != nil && _cancelToken != nil
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ func linkToTask(_ task: DownloadTask) {
|
|
|
|
|
+ self.sessionTask = task.sessionTask
|
|
|
|
|
+ self.cancelToken = task.cancelToken
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
actor CancellationDownloadTask {
|
|
actor CancellationDownloadTask {
|
|
@@ -294,7 +320,7 @@ open class ImageDownloader: @unchecked Sendable {
|
|
|
private func createDownloadContext(
|
|
private func createDownloadContext(
|
|
|
with url: URL,
|
|
with url: URL,
|
|
|
options: KingfisherParsedOptionsInfo,
|
|
options: KingfisherParsedOptionsInfo,
|
|
|
- done: @escaping ((Result<DownloadingContext, KingfisherError>) -> Void)
|
|
|
|
|
|
|
+ done: @escaping (@Sendable (Result<DownloadingContext, KingfisherError>) -> Void)
|
|
|
)
|
|
)
|
|
|
{
|
|
{
|
|
|
@Sendable func checkRequestAndDone(r: URLRequest) {
|
|
@Sendable func checkRequestAndDone(r: URLRequest) {
|
|
@@ -392,8 +418,7 @@ open class ImageDownloader: @unchecked Sendable {
|
|
|
{
|
|
{
|
|
|
let downloadTask = addDownloadTask(context: context, callback: callback)
|
|
let downloadTask = addDownloadTask(context: context, callback: callback)
|
|
|
|
|
|
|
|
- let sessionTask = downloadTask.sessionTask
|
|
|
|
|
- guard !sessionTask.started else {
|
|
|
|
|
|
|
+ guard let sessionTask = downloadTask.sessionTask, !sessionTask.started else {
|
|
|
return downloadTask
|
|
return downloadTask
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -452,9 +477,9 @@ open class ImageDownloader: @unchecked Sendable {
|
|
|
open func downloadImage(
|
|
open func downloadImage(
|
|
|
with url: URL,
|
|
with url: URL,
|
|
|
options: KingfisherParsedOptionsInfo,
|
|
options: KingfisherParsedOptionsInfo,
|
|
|
- completionHandler: ((Result<ImageLoadingResult, KingfisherError>) -> Void)? = nil) -> DownloadTask?
|
|
|
|
|
|
|
+ completionHandler: (@Sendable (Result<ImageLoadingResult, KingfisherError>) -> Void)? = nil) -> DownloadTask
|
|
|
{
|
|
{
|
|
|
- var downloadTask: DownloadTask?
|
|
|
|
|
|
|
+ let downloadTask = DownloadTask()
|
|
|
createDownloadContext(with: url, options: options) { result in
|
|
createDownloadContext(with: url, options: options) { result in
|
|
|
switch result {
|
|
switch result {
|
|
|
case .success(let context):
|
|
case .success(let context):
|
|
@@ -463,10 +488,11 @@ open class ImageDownloader: @unchecked Sendable {
|
|
|
// `AsyncImageDownloadRequestModifier` is used the returned `downloadTask` of this method will be `nil`
|
|
// `AsyncImageDownloadRequestModifier` is used the returned `downloadTask` of this method will be `nil`
|
|
|
// and the actual "delayed" task is given in `AsyncImageDownloadRequestModifier.onDownloadTaskStarted`
|
|
// and the actual "delayed" task is given in `AsyncImageDownloadRequestModifier.onDownloadTaskStarted`
|
|
|
// callback.
|
|
// callback.
|
|
|
- downloadTask = self.startDownloadTask(
|
|
|
|
|
|
|
+ let actualDownloadTask = self.startDownloadTask(
|
|
|
context: context,
|
|
context: context,
|
|
|
callback: self.createTaskCallback(completionHandler, options: options)
|
|
callback: self.createTaskCallback(completionHandler, options: options)
|
|
|
)
|
|
)
|
|
|
|
|
+ downloadTask.linkToTask(actualDownloadTask)
|
|
|
if let modifier = options.requestModifier {
|
|
if let modifier = options.requestModifier {
|
|
|
modifier.onDownloadTaskStarted?(downloadTask)
|
|
modifier.onDownloadTaskStarted?(downloadTask)
|
|
|
}
|
|
}
|
|
@@ -521,7 +547,7 @@ open class ImageDownloader: @unchecked Sendable {
|
|
|
open func downloadImage(
|
|
open func downloadImage(
|
|
|
with url: URL,
|
|
with url: URL,
|
|
|
options: KingfisherOptionsInfo? = nil,
|
|
options: KingfisherOptionsInfo? = nil,
|
|
|
- completionHandler: ((Result<ImageLoadingResult, KingfisherError>) -> Void)? = nil) -> DownloadTask?
|
|
|
|
|
|
|
+ completionHandler: ((Result<ImageLoadingResult, KingfisherError>) -> Void)? = nil) -> DownloadTask
|
|
|
{
|
|
{
|
|
|
downloadImage(
|
|
downloadImage(
|
|
|
with: url,
|
|
with: url,
|
|
@@ -552,7 +578,7 @@ extension ImageDownloader {
|
|
|
continuation.resume(with: result)
|
|
continuation.resume(with: result)
|
|
|
}
|
|
}
|
|
|
if Task.isCancelled {
|
|
if Task.isCancelled {
|
|
|
- downloadTask?.cancel()
|
|
|
|
|
|
|
+ downloadTask.cancel()
|
|
|
} else {
|
|
} else {
|
|
|
Task {
|
|
Task {
|
|
|
await task.setTask(downloadTask)
|
|
await task.setTask(downloadTask)
|