Skip to content

Commit

Permalink
Soft-deprecate closure-based APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
kean committed Aug 18, 2024
1 parent 1b7547a commit 7e34b50
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 99 deletions.
6 changes: 5 additions & 1 deletion Nuke.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
0C1453A02657EFA7005E24B3 /* ImagePipelineObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C14539F2657EFA7005E24B3 /* ImagePipelineObserver.swift */; };
0C1453A12657EFA7005E24B3 /* ImagePipelineObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C14539F2657EFA7005E24B3 /* ImagePipelineObserver.swift */; };
0C16C85F2C7150C800B2A560 /* ImagePublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C88C578263DAF1E0061A008 /* ImagePublisherTests.swift */; };
0C16C8632C726B1B00B2A560 /* ImagePipeline+Closures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C16C8622C726B1B00B2A560 /* ImagePipeline+Closures.swift */; };
0C179C7B2283597F008AB488 /* ImageEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C179C7A2283597F008AB488 /* ImageEncoding.swift */; };
0C1B9880294E28D800C09310 /* Nuke.docc in Sources */ = {isa = PBXBuildFile; fileRef = 0C1B987F294E28D800C09310 /* Nuke.docc */; };
0C1C201D29ABBF19004B38FD /* Nuke.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C9174901BAE99EE004A7905 /* Nuke.framework */; };
Expand Down Expand Up @@ -350,6 +351,7 @@
0C0FD5D81CA47FE1002A78FB /* ImageProcessing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageProcessing.swift; sourceTree = "<group>"; };
0C0FD5D91CA47FE1002A78FB /* ImageRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRequest.swift; sourceTree = "<group>"; };
0C14539F2657EFA7005E24B3 /* ImagePipelineObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePipelineObserver.swift; sourceTree = "<group>"; };
0C16C8622C726B1B00B2A560 /* ImagePipeline+Closures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ImagePipeline+Closures.swift"; sourceTree = "<group>"; };
0C179C772282AC50008AB488 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .swiftlint.yml; sourceTree = "<group>"; };
0C179C7A2283597F008AB488 /* ImageEncoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageEncoding.swift; sourceTree = "<group>"; };
0C1B987F294E28D800C09310 /* Nuke.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Nuke.docc; sourceTree = "<group>"; };
Expand Down Expand Up @@ -952,11 +954,12 @@
isa = PBXGroup;
children = (
0C0FD5D31CA47FE1002A78FB /* ImagePipeline.swift */,
0CC04B092C5698D500F1164D /* ImagePipelineActor.swift */,
0CF1754B22913F9800A8946E /* ImagePipeline+Configuration.swift */,
0C53C8B0263C968200E62D03 /* ImagePipeline+Delegate.swift */,
0C78A2A6263F4E680051E0FF /* ImagePipeline+Cache.swift */,
0CBA07852852DA8B00CE29F4 /* ImagePipeline+Error.swift */,
0C16C8622C726B1B00B2A560 /* ImagePipeline+Closures.swift */,
0CC04B092C5698D500F1164D /* ImagePipelineActor.swift */,
);
path = Pipeline;
sourceTree = "<group>";
Expand Down Expand Up @@ -1729,6 +1732,7 @@
0CA4ECC426E685F500BAC8E5 /* ImageProcessors+GaussianBlur.swift in Sources */,
0CA4EC9B26E67D3000BAC8E5 /* ImageDecoders+Empty.swift in Sources */,
0CB26802208F2565004C83F4 /* DataCache.swift in Sources */,
0C16C8632C726B1B00B2A560 /* ImagePipeline+Closures.swift in Sources */,
0CA4EC9F26E67D6200BAC8E5 /* ImageDecoderRegistry.swift in Sources */,
0CA4ECBA26E6850B00BAC8E5 /* Graphics.swift in Sources */,
0CA4ECB426E6844B00BAC8E5 /* ImageProcessors.swift in Sources */,
Expand Down
107 changes: 107 additions & 0 deletions Sources/Nuke/Pipeline/ImagePipeline+Closures.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// The MIT License (MIT)
//
// Copyright (c) 2015-2024 Alexander Grebenyuk (github.com/kean).

import Foundation

extension ImagePipeline {
/// Loads an image for the given request.
///
/// - warning: Soft-deprecated in Nuke 13.0.
///
/// - parameters:
/// - request: An image request.
/// - completion: A closure to be called on the main thread when the request
/// is finished.
@discardableResult public nonisolated func loadImage(
with url: URL,
completion: @MainActor @Sendable @escaping (_ result: Result<ImageResponse, Error>) -> Void
) -> ImageTask {
_loadImage(with: ImageRequest(url: url), progress: nil, completion: completion)
}

/// Loads an image for the given request.
///
/// - warning: Soft-deprecated in Nuke 13.0.
///
/// - parameters:
/// - request: An image request.
/// - completion: A closure to be called on the main thread when the request
/// is finished.
@discardableResult public nonisolated func loadImage(
with request: ImageRequest,
completion: @MainActor @Sendable @escaping (_ result: Result<ImageResponse, Error>) -> Void
) -> ImageTask {
_loadImage(with: request, progress: nil, completion: completion)
}

/// Loads an image for the given request.
///
/// - warning: Soft-deprecated in Nuke 13.0.
///
/// - parameters:
/// - request: An image request.
/// - progress: A closure to be called periodically on the main thread when
/// the progress is updated.
/// - completion: A closure to be called on the main thread when the request
/// is finished.
@discardableResult public nonisolated func loadImage(
with request: ImageRequest,
progress: (@MainActor @Sendable (_ response: ImageResponse?, _ completed: Int64, _ total: Int64) -> Void)?,
completion: @MainActor @Sendable @escaping (_ result: Result<ImageResponse, Error>) -> Void
) -> ImageTask {
_loadImage(with: request, progress: {
progress?($0, $1.completed, $1.total)
}, completion: completion)
}

/// Loads the image data for the given request. The data doesn't get decoded
/// or processed in any other way.
///
/// You can call ``loadImage(with:completion:)-43osv`` for the request at any point after calling
/// ``loadData(with:completion:)-6cwk3``, the pipeline will use the same operation to load the data,
/// no duplicated work will be performed.
///
/// - warning: Soft-deprecated in Nuke 13.0.
///
/// - parameters:
/// - request: An image request.
/// - progress: A closure to be called periodically on the main thread when the progress is updated.
/// - completion: A closure to be called on the main thread when the request is finished.
@discardableResult public nonisolated func loadData(
with request: ImageRequest,
progress progressHandler: (@MainActor @Sendable (_ completed: Int64, _ total: Int64) -> Void)? = nil,
completion: @MainActor @Sendable @escaping (Result<(data: Data, response: URLResponse?), Error>) -> Void
) -> ImageTask {
_loadImage(with: request, isDataTask: true) { _, progress in
progressHandler?(progress.completed, progress.total)
} completion: { result in
let result = result.map { response in
// Data should never be empty
(data: response.container.data ?? Data(), response: response.urlResponse)
}
completion(result)
}
}

private nonisolated func _loadImage(
with request: ImageRequest,
isDataTask: Bool = false,
progress: (@MainActor @Sendable (ImageResponse?, ImageTask.Progress) -> Void)?,
completion: @MainActor @Sendable @escaping (Result<ImageResponse, Error>) -> Void
) -> ImageTask {
makeImageTask(with: request, isDataTask: isDataTask) { event, task in
DispatchQueue.main.async {
// The callback-based API guarantees that after cancellation no
// event are called on the callback queue.
guard !task.isCancelling else { return }
switch event {
case .progress(let value): progress?(nil, value)
case .preview(let response): progress?(response, task.currentProgress)
case .cancelled: break // The legacy APIs do not send cancellation events
case .finished(let result): completion(result)
}
}
}
}
}
100 changes: 2 additions & 98 deletions Sources/Nuke/Pipeline/ImagePipeline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public final class ImagePipeline {
}
}

// MARK: - Loading Images (Async/Await)
// MARK: - Loading Images

/// Creates a task with the given URL.
///
Expand All @@ -127,7 +127,7 @@ public final class ImagePipeline {
try await imageTask(with: request).image
}

// MARK: - Loading Data (Async/Await)
// MARK: - Loading Data

/// Returns image data for the given request.
///
Expand All @@ -138,102 +138,6 @@ public final class ImagePipeline {
return (response.container.data ?? Data(), response.urlResponse)
}

// MARK: - Loading Images (Closures)

/// Loads an image for the given request.
///
/// - parameters:
/// - request: An image request.
/// - completion: A closure to be called on the main thread when the request
/// is finished.
@discardableResult public nonisolated func loadImage(
with url: URL,
completion: @MainActor @Sendable @escaping (_ result: Result<ImageResponse, Error>) -> Void
) -> ImageTask {
_loadImage(with: ImageRequest(url: url), progress: nil, completion: completion)
}

/// Loads an image for the given request.
///
/// - parameters:
/// - request: An image request.
/// - completion: A closure to be called on the main thread when the request
/// is finished.
@discardableResult public nonisolated func loadImage(
with request: ImageRequest,
completion: @MainActor @Sendable @escaping (_ result: Result<ImageResponse, Error>) -> Void
) -> ImageTask {
_loadImage(with: request, progress: nil, completion: completion)
}

/// Loads an image for the given request.
///
/// - parameters:
/// - request: An image request.
/// - progress: A closure to be called periodically on the main thread when
/// the progress is updated.
/// - completion: A closure to be called on the main thread when the request
/// is finished.
@discardableResult public nonisolated func loadImage(
with request: ImageRequest,
progress: (@MainActor @Sendable (_ response: ImageResponse?, _ completed: Int64, _ total: Int64) -> Void)?,
completion: @MainActor @Sendable @escaping (_ result: Result<ImageResponse, Error>) -> Void
) -> ImageTask {
_loadImage(with: request, progress: {
progress?($0, $1.completed, $1.total)
}, completion: completion)
}

private nonisolated func _loadImage(
with request: ImageRequest,
isDataTask: Bool = false,
progress: (@MainActor @Sendable (ImageResponse?, ImageTask.Progress) -> Void)?,
completion: @MainActor @Sendable @escaping (Result<ImageResponse, Error>) -> Void
) -> ImageTask {
makeImageTask(with: request, isDataTask: isDataTask) { event, task in
DispatchQueue.main.async {
// The callback-based API guarantees that after cancellation no
// event are called on the callback queue.
guard !task.isCancelling else { return }
switch event {
case .progress(let value): progress?(nil, value)
case .preview(let response): progress?(response, task.currentProgress)
case .cancelled: break // The legacy APIs do not send cancellation events
case .finished(let result): completion(result)
}
}
}
}

// MARK: - Loading Data (Closures)

/// Loads the image data for the given request. The data doesn't get decoded
/// or processed in any other way.
///
/// You can call ``loadImage(with:completion:)-43osv`` for the request at any point after calling
/// ``loadData(with:completion:)-6cwk3``, the pipeline will use the same operation to load the data,
/// no duplicated work will be performed.
///
/// - parameters:
/// - request: An image request.
/// - progress: A closure to be called periodically on the main thread when the progress is updated.
/// - completion: A closure to be called on the main thread when the request is finished.
@discardableResult public nonisolated func loadData(
with request: ImageRequest,
progress progressHandler: (@MainActor @Sendable (_ completed: Int64, _ total: Int64) -> Void)? = nil,
completion: @MainActor @Sendable @escaping (Result<(data: Data, response: URLResponse?), Error>) -> Void
) -> ImageTask {
_loadImage(with: request, isDataTask: true) { _, progress in
progressHandler?(progress.completed, progress.total)
} completion: { result in
let result = result.map { response in
// Data should never be empty
(data: response.container.data ?? Data(), response: response.urlResponse)
}
completion(result)
}
}

// MARK: - ImageTask (Internal)

nonisolated func makeImageTask(with request: ImageRequest, isDataTask: Bool = false, onEvent: ((ImageTask.Event, ImageTask) -> Void)? = nil) -> ImageTask {
Expand Down

0 comments on commit 7e34b50

Please sign in to comment.