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
| struct Theme { | |
| static let light = Theme(type: .light, colors: .light) | |
| static let dark = Theme(type: .dark, colors: .dark) | |
| @available(iOS 13.0, *) | |
| static let adaptive = Theme(type: .adaptive, colors: .adaptive) | |
| enum `Type` { | |
| case light | |
| case dark | |
| @available(iOS 13.0, *) |
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
| protocol ThemeProvider: class { | |
| var theme: Theme { get } | |
| func register<Observer: Themeable>(observer: Observer) | |
| func toggleTheme() | |
| } | |
| @available(iOS 13.0, *) | |
| public class DefaultThemeProvider: NSObject, ThemeProvider { | |
| static let shared = DefaultThemeProvider() | |
| let theme: Theme = .adaptive |
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
| extension Themeable where Self: UITraitEnvironment { | |
| var themeProvider: ThemeProvider { | |
| if #available(iOS 13.0, *) { | |
| return DefaultThemeProvider.shared | |
| } else { | |
| return LegacyThemeProvider.shared | |
| } | |
| } | |
| } |
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
| extension UIImage { | |
| func decodedImage() -> UIImage { | |
| guard let cgImage = cgImage else { return self } | |
| let size = CGSize(width: cgImage.width, height: cgImage.height) | |
| let colorSpace = CGColorSpaceCreateDeviceRGB() | |
| let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: cgImage.bytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) | |
| context?.draw(cgImage, in: CGRect(origin: .zero, size: size)) | |
| guard let decodedImage = context?.makeImage() else { return self } | |
| return UIImage(cgImage: decodedImage) |
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
| // Declares in-memory image cache | |
| protocol ImageCacheType: class { | |
| // Returns the image associated with a given url | |
| func image(for url: URL) -> UIImage? | |
| // Inserts the image of the specified url in the cache | |
| func insertImage(_ image: UIImage?, for url: URL) | |
| // Removes the image of the specified url in the cache | |
| func removeImage(for url: URL) | |
| // Removes all images from the cache | |
| func removeAllImages() |
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
| final class ImageCache { | |
| // 1st level cache, that contains encoded images | |
| private lazy var imageCache: NSCache<AnyObject, AnyObject> = { | |
| let cache = NSCache<AnyObject, AnyObject>() | |
| cache.countLimit = config.countLimit | |
| return cache | |
| }() | |
| // 2nd level cache, that contains decoded images | |
| private lazy var decodedImageCache: NSCache<AnyObject, AnyObject> = { |
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
| extension ImageCache: ImageCacheType { | |
| func insertImage(_ image: UIImage?, for url: URL) { | |
| guard let image = image else { return removeImage(for: url) } | |
| let decodedImage = image.decodedImage() | |
| lock.lock(); defer { lock.unlock() } | |
| imageCache.setObject(decodedImage, forKey: url as AnyObject) | |
| decodedImageCache.setObject(image as AnyObject, forKey: url as AnyObject, cost: decodedImage.diskSize) | |
| } |
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
| extension ImageCache { | |
| func image(for url: URL) -> UIImage? { | |
| lock.lock(); defer { lock.unlock() } | |
| // the best case scenario -> there is a decoded image | |
| if let decodedImage = decodedImageCache.object(forKey: url as AnyObject) as? UIImage { | |
| return decodedImage | |
| } | |
| // search for image data | |
| if let image = imageCache.object(forKey: url as AnyObject) as? UIImage { | |
| let decodedImage = image.decodedImage() |
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
| extension ImageCache { | |
| subscript(_ key: URL) -> UIImage? { | |
| get { | |
| return image(for: key) | |
| } | |
| set { | |
| return insertImage(newValue, for: key) | |
| } | |
| } | |
| } |
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
| final class ImageLoader { | |
| func loadImage(from url: URL) -> AnyPublisher<UIImage?, Never> { | |
| return URLSession.shared.dataTaskPublisher(for: url) ➊ | |
| .map { (data, _) -> UIImage? in return UIImage(data: data) } ➋ | |
| .catch { error in return Just(nil) } ➌ | |
| .subscribe(on: backgroundQueue) ➍ | |
| .receive(on: RunLoop.main) ➎ | |
| .eraseToAnyPublisher() ➏ | |
| } |