Created
March 3, 2021 13:15
-
-
Save IuriiIaremenko/9fbef6861d9c62a5f3fccdadded5517a to your computer and use it in GitHub Desktop.
Example of simple and lightweight service for image caching without any 3rd party dependencies. UIKit + SwiftUI usage example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ImageLoader.swift | |
// | |
// Created by Iurii Iaremenko on 05.02.2021. | |
// | |
import Foundation | |
import Combine | |
import class UIKit.UIImage | |
final class ImageLoader: ObservableObject { | |
@Published var image: UIImage | |
private var subscriptions = Set<AnyCancellable>() | |
init(url: URL?, placeholder: UIImage = #imageLiteral(resourceName: "placeholder")) { | |
image = placeholder | |
guard let url = url else { | |
return | |
} | |
ImageURLStorage.shared | |
.cachedImage(with: url) | |
.map { image = $0 } | |
ImageURLStorage.shared | |
.getImage(for: url) | |
.compactMap { $0 } | |
.receive(on: RunLoop.main) | |
.sink( | |
receiveCompletion: { _ in }, | |
receiveValue: { [weak self] in | |
self?.image = $0 | |
}) | |
.store(in: &subscriptions) | |
} | |
} | |
final class OptionalImageLoader: ObservableObject { | |
@Published var image: UIImage? | |
private var subscriptions = Set<AnyCancellable>() | |
init(url: URL?, placeholder: UIImage? = nil) { | |
image = placeholder | |
guard let url = url else { | |
return | |
} | |
ImageURLStorage.shared | |
.cachedImage(with: url) | |
.map { image = $0 } | |
ImageURLStorage.shared | |
.getImage(for: url) | |
.compactMap { $0 } | |
.receive(on: RunLoop.main) | |
.sink( | |
receiveCompletion: { _ in }, | |
receiveValue: { [weak self] in | |
self?.image = $0 | |
}) | |
.store(in: &subscriptions) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ImageURLStorage.swift | |
// | |
// Created by Iurii Iaremenko on 05.02.2021. | |
// | |
import Foundation | |
import Combine | |
import class UIKit.UIImage | |
public protocol ImageStorage: AnyObject { | |
func getImage(for url: URL) -> AnyPublisher<UIImage?, Error> | |
func cachedImage(with url: URL) -> UIImage? | |
func clearStorage() | |
} | |
/// Simple and lightweight image caching without any 3rd party dependencies. | |
public final class ImageURLStorage: ImageStorage { | |
public static let shared: ImageStorage = ImageURLStorage() | |
private let cache: URLCache | |
private let session: URLSession | |
private let cacheSize: Int = .megaBytes(150) | |
private init() { | |
let config = URLSessionConfiguration.default | |
cache = URLCache(memoryCapacity: cacheSize, diskCapacity: cacheSize) | |
config.urlCache = cache | |
config.requestCachePolicy = .reloadRevalidatingCacheData | |
config.httpMaximumConnectionsPerHost = 5 | |
session = URLSession(configuration: config) | |
} | |
public func getImage(for url: URL) -> AnyPublisher<UIImage?, Error> { | |
latestData(with: url) | |
.map(UIImage.init) | |
.eraseToAnyPublisher() | |
} | |
public func cachedImage(with url: URL) -> UIImage? { | |
let request = URLRequest(url: url) | |
let data = cache.cachedResponse(for: request)?.data | |
return data.flatMap(UIImage.init) | |
} | |
public func clearStorage() { | |
cache.removeAllCachedResponses() | |
} | |
} | |
extension ImageURLStorage { | |
private func latestData(with url: URL) -> AnyPublisher<Data, Error> { | |
let request = URLRequest(url: url) | |
return session | |
.dataTaskPublisher(for: request) | |
.map(\.data) | |
.mapError { $0 as Error } | |
.eraseToAnyPublisher() | |
} | |
} | |
private extension Int { | |
static func megaBytes(_ number: Int) -> Int { | |
number * 1024 * 1024 | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// UIImageView+Additions.swift | |
// | |
// Created by Iurii Iaremenko on 05.05.2020. | |
// | |
import class UIKit.UIImageView | |
import class UIKit.UIImage | |
import Combine | |
public extension UIImageView { | |
func setImage(with url: URL?, placeholder: UIImage? = nil, _ completion: ((UIImageView, UIImage?) -> Void)? = nil) -> AnyCancellable { | |
image = placeholder | |
guard let url = url else { | |
return Empty<Void, Never>(completeImmediately: true).sink(receiveCompletion: { [weak self] _ in | |
guard let self = self else { return } | |
completion?(self, nil) | |
}, receiveValue: {}) | |
} | |
ImageURLStorage.shared | |
.cachedImage(with: url) | |
.map { image = $0 } | |
return ImageURLStorage.shared | |
.getImage(for: url) | |
.receiveOnMain() | |
.sink( | |
receiveCompletion: { _ in }, | |
receiveValue: { [weak self] in | |
guard let self = self else { return } | |
self.image = $0 | |
completion?(self, $0) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment