KFImageModel.swift (3745B)
1 // 2 // KFImageModel.swift 3 // damus 4 // 5 // Created by Oleg Abalonski on 1/11/23. 6 // 7 8 import UIKit 9 import Kingfisher 10 11 class KFImageModel: ObservableObject { 12 13 let url: URL? 14 let fallbackUrl: URL? 15 let processor: ImageProcessor 16 let serializer: CacheSerializer 17 18 @Published var refreshID = "" 19 20 init(url: URL?, fallbackUrl: URL?, maxByteSize: Int, downsampleSize: CGSize) { 21 self.url = url 22 self.fallbackUrl = fallbackUrl 23 self.processor = CustomImageProcessor(maxSize: maxByteSize, downsampleSize: downsampleSize) 24 self.serializer = CustomCacheSerializer(maxSize: maxByteSize, downsampleSize: downsampleSize) 25 } 26 27 func refresh() -> Void { 28 DispatchQueue.main.async { 29 self.refreshID = UUID().uuidString 30 } 31 } 32 33 func cache(_ image: UIImage, forKey key: String) -> Void { 34 KingfisherManager.shared.cache.store(image, forKey: key, processorIdentifier: processor.identifier) { _ in 35 self.refresh() 36 } 37 } 38 39 func downloadFailed() -> Void { 40 guard let url = url, let fallbackUrl = fallbackUrl else { return } 41 42 DispatchQueue.global(qos: .background).async { 43 KingfisherManager.shared.downloader.downloadImage(with: fallbackUrl) { result in 44 45 var fallbackImage: UIImage { 46 switch result { 47 case .success(let imageLoadingResult): 48 return imageLoadingResult.image 49 case .failure(let error): 50 print(error) 51 return UIImage() 52 } 53 } 54 55 self.cache(fallbackImage, forKey: url.absoluteString) 56 } 57 } 58 } 59 } 60 61 struct CustomImageProcessor: ImageProcessor { 62 63 let maxSize: Int 64 let downsampleSize: CGSize 65 66 let identifier = "com.damus.customimageprocessor" 67 68 func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? { 69 70 switch item { 71 case .image(_): 72 // This case will never run 73 return DefaultImageProcessor.default.process(item: item, options: options) 74 case .data(let data): 75 76 // Handle large image size 77 if data.count > maxSize { 78 return KingfisherWrapper.downsampledImage(data: data, to: downsampleSize, scale: options.scaleFactor) 79 } 80 81 // Handle SVG image 82 if let dataString = String(data: data, encoding: .utf8), 83 let svg = SVG(dataString) { 84 85 let render = UIGraphicsImageRenderer(size: svg.size) 86 let image = render.image { context in 87 svg.draw(in: context.cgContext) 88 } 89 90 return image.kf.scaled(to: options.scaleFactor) 91 } 92 93 return DefaultImageProcessor.default.process(item: item, options: options) 94 } 95 } 96 } 97 98 struct CustomCacheSerializer: CacheSerializer { 99 100 let maxSize: Int 101 let downsampleSize: CGSize 102 103 func data(with image: Kingfisher.KFCrossPlatformImage, original: Data?) -> Data? { 104 return DefaultCacheSerializer.default.data(with: image, original: original) 105 } 106 107 func image(with data: Data, options: Kingfisher.KingfisherParsedOptionsInfo) -> Kingfisher.KFCrossPlatformImage? { 108 if data.count > maxSize { 109 return KingfisherWrapper.downsampledImage(data: data, to: downsampleSize, scale: options.scaleFactor) 110 } 111 112 return DefaultCacheSerializer.default.image(with: data, options: options) 113 } 114 }