diff --git a/AsyncImage.xcodeproj/project.pbxproj b/AsyncImage.xcodeproj/project.pbxproj index 4d22e0f..6061cb1 100644 --- a/AsyncImage.xcodeproj/project.pbxproj +++ b/AsyncImage.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 1F6284DD29525A5E0060AAD8 /* ImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6284DC29525A5E0060AAD8 /* ImageProcessor.swift */; }; D4B7E90A2676204F006271B3 /* AsyncImageDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4B7E9092676204F006271B3 /* AsyncImageDemoApp.swift */; }; D4B7E90C2676204F006271B3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4B7E90B2676204F006271B3 /* ContentView.swift */; }; D4B7E90E26762050006271B3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D4B7E90D26762050006271B3 /* Assets.xcassets */; }; @@ -78,6 +79,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 1F6284DC29525A5E0060AAD8 /* ImageProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProcessor.swift; sourceTree = ""; }; "AsyncImage::AsyncImage::Product" /* AsyncImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = AsyncImage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; "AsyncImage::AsyncImageTests::Product" /* AsyncImageTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = AsyncImageTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D4B7E9072676204F006271B3 /* AsyncImageDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AsyncImageDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -207,6 +209,7 @@ OBJ_9 /* AsyncImage.swift */, OBJ_10 /* ImageLoader.swift */, OBJ_11 /* ImageService.swift */, + 1F6284DC29525A5E0060AAD8 /* ImageProcessor.swift */, D4B7E93F26774B8F006271B3 /* SessionDelegate.swift */, D4B7E93126766DB2006271B3 /* DataTask.swift */, OBJ_12 /* LRUCache.swift */, @@ -351,6 +354,7 @@ D4B7E94026774B8F006271B3 /* SessionDelegate.swift in Sources */, OBJ_26 /* AsyncImage.swift in Sources */, OBJ_27 /* ImageLoader.swift in Sources */, + 1F6284DD29525A5E0060AAD8 /* ImageProcessor.swift in Sources */, OBJ_28 /* ImageService.swift in Sources */, OBJ_29 /* LRUCache.swift in Sources */, ); diff --git a/Sources/AsyncImage/AsyncImage.swift b/Sources/AsyncImage/AsyncImage.swift index 7d5883b..fcf4df4 100644 --- a/Sources/AsyncImage/AsyncImage.swift +++ b/Sources/AsyncImage/AsyncImage.swift @@ -37,17 +37,18 @@ public struct AsyncImage: View { .onDisappear { loader.cancelDownload() } } - public init(url: URL, scale: CGFloat = 1) where Content == Image { - loader = ImageLoader(url: url, scale: scale) + public init(url: URL, scale: CGFloat = 1, processor: ImageProcessor? = nil) where Content == Image { + loader = ImageLoader(url: url, scale: scale, processor: processor) } public init( url: URL?, scale: CGFloat = 1, + processor: ImageProcessor? = nil, content: @escaping (Image) -> I, placeholder: @escaping () -> P ) where Content == _ConditionalContent { - self.init(url: url, scale: scale) { phase in + self.init(url: url, scale: scale, processor: processor) { phase in if let image = phase.image { content(image) } else { @@ -59,10 +60,11 @@ public struct AsyncImage: View { public init( url: URL?, scale: CGFloat = 1, + processor: ImageProcessor? = nil, transaction: Transaction = Transaction(), @ViewBuilder content: @escaping (AsyncImagePhase) -> Content ) { self.content = content - loader = ImageLoader(url: url, scale: scale) + loader = ImageLoader(url: url, scale: scale, processor: processor) } } diff --git a/Sources/AsyncImage/ImageLoader.swift b/Sources/AsyncImage/ImageLoader.swift index 36904dd..09c7ea8 100644 --- a/Sources/AsyncImage/ImageLoader.swift +++ b/Sources/AsyncImage/ImageLoader.swift @@ -7,16 +7,16 @@ final class ImageLoader: ObservableObject { @Published var asyncImagePhase = AsyncImagePhase.empty let scale: CGFloat let url: URL? + let processor: ImageProcessor? var downloadTask: DownloadTask? - init(url: URL?, scale: CGFloat) { + init(url: URL?, scale: CGFloat, processor: ImageProcessor?) { self.url = url self.scale = scale + self.processor = processor } - deinit { - print(true) - } + deinit {} func loadImage() { guard let url = url, asyncImagePhase.image == nil else { return } @@ -27,6 +27,11 @@ final class ImageLoader: ObservableObject { guard let self = self else { return } switch result { case .success(let image): + var image = image + if let processor = self.processor { + // Processing the fetched image here to keep the cached image untouched. + image = processor.process(image: image) + } self.asyncImagePhase = .success(Image(uiImage: image)) case .failure(let error): if error.isCanceled { return } diff --git a/Sources/AsyncImage/ImageProcessor.swift b/Sources/AsyncImage/ImageProcessor.swift new file mode 100644 index 0000000..e231672 --- /dev/null +++ b/Sources/AsyncImage/ImageProcessor.swift @@ -0,0 +1,12 @@ +// +// ImageProcessor.swift +// AsyncImage +// +// Created by Fabian Thies on 20.12.22. +// + +import UIKit + +public protocol ImageProcessor { + func process(image: UIImage) -> UIImage +}