From 0758293f2aa3a6551ba4ae5942acb56056b179a3 Mon Sep 17 00:00:00 2001 From: kean Date: Mon, 17 Jul 2023 15:22:07 -0400 Subject: [PATCH 1/9] Add ImageCache --- .../ViewRelated/Media/ImageCache.swift | 19 +++++++++++++++++++ WordPress/WordPress.xcodeproj/project.pbxproj | 6 ++++++ 2 files changed, 25 insertions(+) create mode 100644 WordPress/Classes/ViewRelated/Media/ImageCache.swift diff --git a/WordPress/Classes/ViewRelated/Media/ImageCache.swift b/WordPress/Classes/ViewRelated/Media/ImageCache.swift new file mode 100644 index 000000000000..f7cc18782612 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Media/ImageCache.swift @@ -0,0 +1,19 @@ +import Foundation +import AlamofireImage + +final class ImageCache { + /// A shared image cache used by the entire system. + /// + /// - warning: It's critical that there is only one cache instance to make + /// sure it can manage the RAM usage efficiently. + static let shared = ImageCache() + + private let cache = AutoPurgingImageCache( + memoryCapacity: 128_000_000, // 128 MB + preferredMemoryUsageAfterPurge: 64_000_000 // 64 MB + ) + + private init() { + + } +} diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 64ab744a04f5..e7d92f7b33dd 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -369,6 +369,8 @@ 0C6C4CD42A4F0AD90049E762 /* blaze-search-page-1.json in Resources */ = {isa = PBXBuildFile; fileRef = 0C6C4CD32A4F0AD80049E762 /* blaze-search-page-1.json */; }; 0C6C4CD62A4F0AEE0049E762 /* blaze-search-page-2.json in Resources */ = {isa = PBXBuildFile; fileRef = 0C6C4CD52A4F0AEE0049E762 /* blaze-search-page-2.json */; }; 0C6C4CD82A4F0F2C0049E762 /* Bundle+TestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C6C4CD72A4F0F2C0049E762 /* Bundle+TestExtensions.swift */; }; + 0C7073952A65CB2E00F325CE /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7073942A65CB2E00F325CE /* ImageCache.swift */; }; + 0C7073962A65CB2E00F325CE /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7073942A65CB2E00F325CE /* ImageCache.swift */; }; 0C71959B2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C71959A2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift */; }; 0C71959C2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C71959A2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift */; }; 0C7D481A2A4DB9300023CF84 /* blaze-search-response.json in Resources */ = {isa = PBXBuildFile; fileRef = 0C7D48192A4DB9300023CF84 /* blaze-search-response.json */; }; @@ -6123,6 +6125,7 @@ 0C6C4CD32A4F0AD80049E762 /* blaze-search-page-1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "blaze-search-page-1.json"; sourceTree = ""; }; 0C6C4CD52A4F0AEE0049E762 /* blaze-search-page-2.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "blaze-search-page-2.json"; sourceTree = ""; }; 0C6C4CD72A4F0F2C0049E762 /* Bundle+TestExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+TestExtensions.swift"; sourceTree = ""; }; + 0C7073942A65CB2E00F325CE /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = ""; }; 0C71959A2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteSettingsRelatedPostsView.swift; sourceTree = ""; }; 0C7D48192A4DB9300023CF84 /* blaze-search-response.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "blaze-search-response.json"; sourceTree = ""; }; 0C7E091F2A4286A00052324C /* PostMetaButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PostMetaButton.m; sourceTree = ""; }; @@ -12503,6 +12506,7 @@ D8A3A5AD206A059100992576 /* StockPhotos */, FF8A04DF1D9BFE7400523BC4 /* CachedAnimatedImageView.swift */, 7435CE7220A4B9170075A1B9 /* AnimatedImageCache.swift */, + 0C7073942A65CB2E00F325CE /* ImageCache.swift */, B5FF3BE61CAD881100C1D597 /* ImageCropOverlayView.swift */, B5A05AD81CA48601002EC787 /* ImageCropViewController.swift */, B5EEB19E1CA96D19004B6540 /* ImageCropViewController.xib */, @@ -21799,6 +21803,7 @@ F115308121B17E66002F1D65 /* EditorFactory.swift in Sources */, 982DDF92263238A6002B3904 /* LikeUser+CoreDataProperties.swift in Sources */, 9F8E38BE209C6DE200454E3C /* NotificationSiteSubscriptionViewController.swift in Sources */, + 0C7073952A65CB2E00F325CE /* ImageCache.swift in Sources */, 9826AE8621B5C72300C851FA /* PostingActivityDay.swift in Sources */, E1DB326C1D9ACD4A00C8FEBC /* ReaderTeamTopic.swift in Sources */, E12DB07B1C48D1C200A6C1D4 /* WPAccount+AccountSettings.swift in Sources */, @@ -24318,6 +24323,7 @@ FABB21DD2602FC2C00C8785C /* NSAttributedString+StyledHTML.swift in Sources */, FABB21DE2602FC2C00C8785C /* Accessible.swift in Sources */, FABB21DF2602FC2C00C8785C /* PostingActivityCell.swift in Sources */, + 0C7073962A65CB2E00F325CE /* ImageCache.swift in Sources */, DCFC097429D3549C00277ECB /* DashboardDomainsCardCell.swift in Sources */, F44293DD28E45DBA00D340AF /* AppIconListViewModel.swift in Sources */, 8B45C12727B2B08900EA3257 /* DashboardStatsCardCell.swift in Sources */, From fcbc6a5ca91e8167a871ddfd8f267e822e29cc02 Mon Sep 17 00:00:00 2001 From: kean Date: Mon, 17 Jul 2023 15:53:27 -0400 Subject: [PATCH 2/9] Register shared ImageCache with WordPressUI extensions --- .../Classes/System/WordPressAppDelegate.swift | 1 + .../ViewRelated/Media/ImageCache.swift | 25 ++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/WordPress/Classes/System/WordPressAppDelegate.swift b/WordPress/Classes/System/WordPressAppDelegate.swift index 21ff24a135f7..fa7b843d5081 100644 --- a/WordPress/Classes/System/WordPressAppDelegate.swift +++ b/WordPress/Classes/System/WordPressAppDelegate.swift @@ -91,6 +91,7 @@ class WordPressAppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) AppAppearance.overrideAppearance() + ImageCache.shared.register() // Start CrashLogging as soon as possible (in case a crash happens during startup) try? loggingStack.start() diff --git a/WordPress/Classes/ViewRelated/Media/ImageCache.swift b/WordPress/Classes/ViewRelated/Media/ImageCache.swift index f7cc18782612..664043001699 100644 --- a/WordPress/Classes/ViewRelated/Media/ImageCache.swift +++ b/WordPress/Classes/ViewRelated/Media/ImageCache.swift @@ -1,14 +1,12 @@ import Foundation import AlamofireImage +import WordPressUI final class ImageCache { /// A shared image cache used by the entire system. - /// - /// - warning: It's critical that there is only one cache instance to make - /// sure it can manage the RAM usage efficiently. static let shared = ImageCache() - private let cache = AutoPurgingImageCache( + fileprivate let cache = AutoPurgingImageCache( memoryCapacity: 128_000_000, // 128 MB preferredMemoryUsageAfterPurge: 64_000_000 // 64 MB ) @@ -17,3 +15,22 @@ final class ImageCache { } } + +extension ImageCache { + /// Registers the cache with all the image loading systems used by the app. + func register() { + WordPressUI.ImageCache.shared = WordpressUICacheAdapter(cache: .shared) + } +} + +private struct WordpressUICacheAdapter: WordPressUI.ImageCaching { + let cache: ImageCache + + func setImage(_ image: UIImage, forKey key: String) { + cache.cache.add(image, withIdentifier: key) + } + + func getImage(forKey key: String) -> UIImage? { + cache.cache.image(withIdentifier: key) + } +} From 6557d2e8ad2f180ba1471ed1426dedff383b3dcc Mon Sep 17 00:00:00 2001 From: kean Date: Mon, 17 Jul 2023 16:12:58 -0400 Subject: [PATCH 3/9] Switch to SDMemoryCache --- .../Classes/System/WordPressAppDelegate.swift | 2 +- .../ViewRelated/Media/ImageCache.swift | 36 ---------------- .../ViewRelated/Media/MemoryCache.swift | 42 +++++++++++++++++++ WordPress/WordPress.xcodeproj/project.pbxproj | 12 +++--- 4 files changed, 49 insertions(+), 43 deletions(-) delete mode 100644 WordPress/Classes/ViewRelated/Media/ImageCache.swift create mode 100644 WordPress/Classes/ViewRelated/Media/MemoryCache.swift diff --git a/WordPress/Classes/System/WordPressAppDelegate.swift b/WordPress/Classes/System/WordPressAppDelegate.swift index fa7b843d5081..d7cb5e59cbd0 100644 --- a/WordPress/Classes/System/WordPressAppDelegate.swift +++ b/WordPress/Classes/System/WordPressAppDelegate.swift @@ -91,7 +91,7 @@ class WordPressAppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) AppAppearance.overrideAppearance() - ImageCache.shared.register() + MemoryCache.shared.register() // Start CrashLogging as soon as possible (in case a crash happens during startup) try? loggingStack.start() diff --git a/WordPress/Classes/ViewRelated/Media/ImageCache.swift b/WordPress/Classes/ViewRelated/Media/ImageCache.swift deleted file mode 100644 index 664043001699..000000000000 --- a/WordPress/Classes/ViewRelated/Media/ImageCache.swift +++ /dev/null @@ -1,36 +0,0 @@ -import Foundation -import AlamofireImage -import WordPressUI - -final class ImageCache { - /// A shared image cache used by the entire system. - static let shared = ImageCache() - - fileprivate let cache = AutoPurgingImageCache( - memoryCapacity: 128_000_000, // 128 MB - preferredMemoryUsageAfterPurge: 64_000_000 // 64 MB - ) - - private init() { - - } -} - -extension ImageCache { - /// Registers the cache with all the image loading systems used by the app. - func register() { - WordPressUI.ImageCache.shared = WordpressUICacheAdapter(cache: .shared) - } -} - -private struct WordpressUICacheAdapter: WordPressUI.ImageCaching { - let cache: ImageCache - - func setImage(_ image: UIImage, forKey key: String) { - cache.cache.add(image, withIdentifier: key) - } - - func getImage(forKey key: String) -> UIImage? { - cache.cache.image(withIdentifier: key) - } -} diff --git a/WordPress/Classes/ViewRelated/Media/MemoryCache.swift b/WordPress/Classes/ViewRelated/Media/MemoryCache.swift new file mode 100644 index 000000000000..47051c18023d --- /dev/null +++ b/WordPress/Classes/ViewRelated/Media/MemoryCache.swift @@ -0,0 +1,42 @@ +import Foundation +import AlamofireImage +import WordPressUI +import SDWebImage + +final class MemoryCache { + /// A shared image cache used by the entire system. + static let shared = MemoryCache() + + private let cache = SDMemoryCache() + + private init() { + self.cache.totalCostLimit = 128_000_000 // 128 MB + } + + func setImage(_ image: UIImage, forKey key: String) { + cache.setObject(image, forKey: key as NSString, cost: image.sd_memoryCost) + } + + func getImage(forKey key: String) -> UIImage? { + cache.object(forKey: key as NSString) as? UIImage + } +} + +extension MemoryCache { + /// Registers the cache with all the image loading systems used by the app. + func register() { + WordPressUI.ImageCache.shared = WordpressUICacheAdapter(cache: .shared) + } +} + +private struct WordpressUICacheAdapter: WordPressUI.ImageCaching { + let cache: MemoryCache + + func setImage(_ image: UIImage, forKey key: String) { + cache.setImage(image, forKey: key) + } + + func getImage(forKey key: String) -> UIImage? { + cache.getImage(forKey: key) + } +} diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index e7d92f7b33dd..48e2d2a58776 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -369,8 +369,8 @@ 0C6C4CD42A4F0AD90049E762 /* blaze-search-page-1.json in Resources */ = {isa = PBXBuildFile; fileRef = 0C6C4CD32A4F0AD80049E762 /* blaze-search-page-1.json */; }; 0C6C4CD62A4F0AEE0049E762 /* blaze-search-page-2.json in Resources */ = {isa = PBXBuildFile; fileRef = 0C6C4CD52A4F0AEE0049E762 /* blaze-search-page-2.json */; }; 0C6C4CD82A4F0F2C0049E762 /* Bundle+TestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C6C4CD72A4F0F2C0049E762 /* Bundle+TestExtensions.swift */; }; - 0C7073952A65CB2E00F325CE /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7073942A65CB2E00F325CE /* ImageCache.swift */; }; - 0C7073962A65CB2E00F325CE /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7073942A65CB2E00F325CE /* ImageCache.swift */; }; + 0C7073952A65CB2E00F325CE /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7073942A65CB2E00F325CE /* MemoryCache.swift */; }; + 0C7073962A65CB2E00F325CE /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7073942A65CB2E00F325CE /* MemoryCache.swift */; }; 0C71959B2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C71959A2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift */; }; 0C71959C2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C71959A2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift */; }; 0C7D481A2A4DB9300023CF84 /* blaze-search-response.json in Resources */ = {isa = PBXBuildFile; fileRef = 0C7D48192A4DB9300023CF84 /* blaze-search-response.json */; }; @@ -6125,7 +6125,7 @@ 0C6C4CD32A4F0AD80049E762 /* blaze-search-page-1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "blaze-search-page-1.json"; sourceTree = ""; }; 0C6C4CD52A4F0AEE0049E762 /* blaze-search-page-2.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "blaze-search-page-2.json"; sourceTree = ""; }; 0C6C4CD72A4F0F2C0049E762 /* Bundle+TestExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+TestExtensions.swift"; sourceTree = ""; }; - 0C7073942A65CB2E00F325CE /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = ""; }; + 0C7073942A65CB2E00F325CE /* MemoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryCache.swift; sourceTree = ""; }; 0C71959A2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteSettingsRelatedPostsView.swift; sourceTree = ""; }; 0C7D48192A4DB9300023CF84 /* blaze-search-response.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "blaze-search-response.json"; sourceTree = ""; }; 0C7E091F2A4286A00052324C /* PostMetaButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PostMetaButton.m; sourceTree = ""; }; @@ -12506,7 +12506,7 @@ D8A3A5AD206A059100992576 /* StockPhotos */, FF8A04DF1D9BFE7400523BC4 /* CachedAnimatedImageView.swift */, 7435CE7220A4B9170075A1B9 /* AnimatedImageCache.swift */, - 0C7073942A65CB2E00F325CE /* ImageCache.swift */, + 0C7073942A65CB2E00F325CE /* MemoryCache.swift */, B5FF3BE61CAD881100C1D597 /* ImageCropOverlayView.swift */, B5A05AD81CA48601002EC787 /* ImageCropViewController.swift */, B5EEB19E1CA96D19004B6540 /* ImageCropViewController.xib */, @@ -21803,7 +21803,7 @@ F115308121B17E66002F1D65 /* EditorFactory.swift in Sources */, 982DDF92263238A6002B3904 /* LikeUser+CoreDataProperties.swift in Sources */, 9F8E38BE209C6DE200454E3C /* NotificationSiteSubscriptionViewController.swift in Sources */, - 0C7073952A65CB2E00F325CE /* ImageCache.swift in Sources */, + 0C7073952A65CB2E00F325CE /* MemoryCache.swift in Sources */, 9826AE8621B5C72300C851FA /* PostingActivityDay.swift in Sources */, E1DB326C1D9ACD4A00C8FEBC /* ReaderTeamTopic.swift in Sources */, E12DB07B1C48D1C200A6C1D4 /* WPAccount+AccountSettings.swift in Sources */, @@ -24323,7 +24323,7 @@ FABB21DD2602FC2C00C8785C /* NSAttributedString+StyledHTML.swift in Sources */, FABB21DE2602FC2C00C8785C /* Accessible.swift in Sources */, FABB21DF2602FC2C00C8785C /* PostingActivityCell.swift in Sources */, - 0C7073962A65CB2E00F325CE /* ImageCache.swift in Sources */, + 0C7073962A65CB2E00F325CE /* MemoryCache.swift in Sources */, DCFC097429D3549C00277ECB /* DashboardDomainsCardCell.swift in Sources */, F44293DD28E45DBA00D340AF /* AppIconListViewModel.swift in Sources */, 8B45C12727B2B08900EA3257 /* DashboardStatsCardCell.swift in Sources */, From da74d6abd482f5eb56dfdcd08f794f380e9a47e2 Mon Sep 17 00:00:00 2001 From: kean Date: Mon, 17 Jul 2023 16:25:01 -0400 Subject: [PATCH 4/9] Register shared MemoryCache with AlamofireImage --- .../ViewRelated/Media/MemoryCache.swift | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/WordPress/Classes/ViewRelated/Media/MemoryCache.swift b/WordPress/Classes/ViewRelated/Media/MemoryCache.swift index 47051c18023d..88a0f671d7cc 100644 --- a/WordPress/Classes/ViewRelated/Media/MemoryCache.swift +++ b/WordPress/Classes/ViewRelated/Media/MemoryCache.swift @@ -20,12 +20,26 @@ final class MemoryCache { func getImage(forKey key: String) -> UIImage? { cache.object(forKey: key as NSString) as? UIImage } + + func removeImage(forKey key: String) { + cache.removeObject(forKey: key as NSString) + } + + func removeAllImages() { + + } } extension MemoryCache { /// Registers the cache with all the image loading systems used by the app. func register() { + // WordPressUI WordPressUI.ImageCache.shared = WordpressUICacheAdapter(cache: .shared) + + // AlamofireImage + UIImageView.af_sharedImageDownloader = AlamofireImage.ImageDownloader( + imageCache: AlamofireImageCacheAdapter(cache: .shared) + ) } } @@ -40,3 +54,45 @@ private struct WordpressUICacheAdapter: WordPressUI.ImageCaching { cache.getImage(forKey: key) } } + +private struct AlamofireImageCacheAdapter: AlamofireImage.ImageRequestCache { + let cache: MemoryCache + + func image(for request: URLRequest, withIdentifier identifier: String?) -> AlamofireImage.Image? { + image(withIdentifier: cacheKey(for: request, identifier: identifier)) + } + + func add(_ image: AlamofireImage.Image, for request: URLRequest, withIdentifier identifier: String?) { + add(image, withIdentifier: cacheKey(for: request, identifier: identifier)) + } + + func removeImage(for request: URLRequest, withIdentifier identifier: String?) -> Bool { + removeImage(withIdentifier: cacheKey(for: request, identifier: identifier)) + } + + func image(withIdentifier identifier: String) -> AlamofireImage.Image? { + cache.getImage(forKey: identifier) + } + + func add(_ image: AlamofireImage.Image, withIdentifier identifier: String) { + cache.setImage(image, forKey: identifier) + } + + func removeImage(withIdentifier identifier: String) -> Bool { + cache.removeImage(forKey: identifier) + return true + } + + func removeAllImages() -> Bool { + // Do nothing (the app decides when to remove images) + return true + } + + private func cacheKey(for request: URLRequest, identifier: String?) -> String { + var key = request.url?.absoluteString ?? "" + if let identifier = identifier { + key += "-\(identifier)" + } + return key + } +} From 84cd42a373645c22c8a95299e6bdd0da2204999b Mon Sep 17 00:00:00 2001 From: kean Date: Mon, 17 Jul 2023 16:42:22 -0400 Subject: [PATCH 5/9] Use MemoryCache in CachedAnimatedImageView --- .../Media/AnimatedImageCache.swift | 88 +++++-------------- .../ViewRelated/Media/MemoryCache.swift | 18 +++- 2 files changed, 36 insertions(+), 70 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Media/AnimatedImageCache.swift b/WordPress/Classes/ViewRelated/Media/AnimatedImageCache.swift index ab086dddf8c1..88dd699b5a75 100644 --- a/WordPress/Classes/ViewRelated/Media/AnimatedImageCache.swift +++ b/WordPress/Classes/ViewRelated/Media/AnimatedImageCache.swift @@ -3,17 +3,13 @@ import UIKit /// AnimatedImageCache is an image + animated gif data cache used in /// CachedAnimatedImageView. It should be accessed via the `shared` singleton. /// -class AnimatedImageCache { +final class AnimatedImageCache { // MARK: Singleton static let shared: AnimatedImageCache = AnimatedImageCache() - private init() { - NotificationCenter.default.addObserver(self, - selector: #selector(AnimatedImageCache.handleMemoryWarning), - name: UIApplication.didReceiveMemoryWarningNotification, - object: nil) - } + + private init() {} // MARK: Private fields @@ -23,42 +19,30 @@ class AnimatedImageCache { return session }() - fileprivate static var cache: NSCache = { - let cache = NSCache() - cache.totalCostLimit = totalCostLimit() - - let bcf = ByteCountFormatter() - bcf.allowedUnits = [.useMB] - bcf.countStyle = .file - let cacheSizeinMB = bcf.string(fromByteCount: Int64(totalCostLimit())) - - return cache - }() - // MARK: Instance methods - @objc func handleMemoryWarning() { - clearCache() - } - - func clearCache() { - AnimatedImageCache.cache.removeAllObjects() + func cacheData(data: Data, url: URL?) { + guard let url = url else { + return + } + let key = url.absoluteString + Constants.keyDataSuffix + MemoryCache.shared.setData(data, forKey: key) } func cachedData(url: URL?) -> Data? { - guard let key = url else { + guard let url = url else { return nil } - return AnimatedImageCache.cache.object(forKey: key as AnyObject) as? Data + let key = url.absoluteString + Constants.keyDataSuffix + return MemoryCache.shared.geData(forKey: key) } func cacheStaticImage(url: URL?, image: UIImage?) { - guard let url = url, - let image = image else { - return + guard let url = url, let image = image else { + return } let key = url.absoluteString + Constants.keyStaticImageSuffix - AnimatedImageCache.cache.setObject(image as AnyObject, forKey: key as AnyObject) + MemoryCache.shared.setImage(image, forKey: key) } func cachedStaticImage(url: URL?) -> UIImage? { @@ -66,7 +50,7 @@ class AnimatedImageCache { return nil } let key = url.absoluteString + Constants.keyStaticImageSuffix - return AnimatedImageCache.cache.object(forKey: key as AnyObject) as? UIImage + return MemoryCache.shared.getImage(forKey: key) } func animatedImage(_ urlRequest: URLRequest, @@ -81,7 +65,7 @@ class AnimatedImageCache { let task = session.dataTask(with: urlRequest, completionHandler: { [weak self] (data, response, error) in //check if view is still here - guard let _ = self else { + guard let self = self else { return } // check if there is an error @@ -101,13 +85,8 @@ class AnimatedImageCache { let staticImage = UIImage(data: data) if let key = urlRequest.url { - let dataByteCost = AnimatedImageCache.byteCost(for: data) - AnimatedImageCache.cache.setObject(data as NSData, forKey: key as NSURL, cost: dataByteCost) - - // Creating a static image from GIF data is an expensive op, so let's try to do it once... - let imageByteCost = AnimatedImageCache.byteCost(for: staticImage) - let imageKey = key.absoluteString + Constants.keyStaticImageSuffix - AnimatedImageCache.cache.setObject(staticImage as AnyObject, forKey: imageKey as AnyObject, cost: imageByteCost) + self.cacheData(data: data, url: key) + self.cacheStaticImage(url: key, image: staticImage) } success?(data, staticImage) }) @@ -117,38 +96,11 @@ class AnimatedImageCache { } } -// MARK: - Private Helpers - -private extension AnimatedImageCache { - static func byteCost(for image: UIImage?) -> Int { - guard let image = image, let imageRef = image.cgImage else { - return 0 - } - return imageRef.bytesPerRow * imageRef.height // Cost in bytes - } - - static func byteCost(for data: Data?) -> Int { - guard let data = data else { - return 0 - } - return data.count // Cost in bytes - } - - static func totalCostLimit() -> Int { - let physicalMemory = ProcessInfo.processInfo.physicalMemory - let cacheRatio = physicalMemory <= (Constants.memory512MB) ? Constants.smallCacheRatio : Constants.largeCacheRatio - let cacheLimit = physicalMemory / UInt64(1 / cacheRatio) - return cacheLimit > UInt64(Int.max) ? Int.max : Int(cacheLimit) - } -} - // MARK: - Constants private extension AnimatedImageCache { struct Constants { + static let keyDataSuffix = "_data" static let keyStaticImageSuffix = "_static_image" - static let memory512MB: UInt64 = 1024 * 1024 * 512 // 512 Mb - static let smallCacheRatio: CGFloat = 0.1 - static let largeCacheRatio: CGFloat = 0.2 } } diff --git a/WordPress/Classes/ViewRelated/Media/MemoryCache.swift b/WordPress/Classes/ViewRelated/Media/MemoryCache.swift index 88a0f671d7cc..2082f3efdb69 100644 --- a/WordPress/Classes/ViewRelated/Media/MemoryCache.swift +++ b/WordPress/Classes/ViewRelated/Media/MemoryCache.swift @@ -10,9 +10,11 @@ final class MemoryCache { private let cache = SDMemoryCache() private init() { - self.cache.totalCostLimit = 128_000_000 // 128 MB + self.cache.totalCostLimit = 256_000_000 // 256 MB } + // MARK: - UIImage + func setImage(_ image: UIImage, forKey key: String) { cache.setObject(image, forKey: key as NSString, cost: image.sd_memoryCost) } @@ -25,8 +27,18 @@ final class MemoryCache { cache.removeObject(forKey: key as NSString) } - func removeAllImages() { + // MARK: - Data + + func setData(_ data: Data, forKey key: String) { + cache.setObject(data, forKey: key as NSString, cost: UInt(data.count)) + } + func geData(forKey key: String) -> Data? { + cache.object(forKey: key as NSString) as? Data + } + + func removeData(forKey key: String) { + cache.removeObject(forKey: key as NSString) } } @@ -40,6 +52,8 @@ extension MemoryCache { UIImageView.af_sharedImageDownloader = AlamofireImage.ImageDownloader( imageCache: AlamofireImageCacheAdapter(cache: .shared) ) + + // WordPress.AnimatedImageCache uses WordPress.MemoryCache directly } } From 3e0e4b20c46f043209097093af47532116c301c8 Mon Sep 17 00:00:00 2001 From: kean Date: Mon, 17 Jul 2023 17:48:01 -0400 Subject: [PATCH 6/9] Switch to using NSCache directly --- .../Classes/ViewRelated/Media/MemoryCache.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Media/MemoryCache.swift b/WordPress/Classes/ViewRelated/Media/MemoryCache.swift index 2082f3efdb69..760735624cf4 100644 --- a/WordPress/Classes/ViewRelated/Media/MemoryCache.swift +++ b/WordPress/Classes/ViewRelated/Media/MemoryCache.swift @@ -7,16 +7,22 @@ final class MemoryCache { /// A shared image cache used by the entire system. static let shared = MemoryCache() - private let cache = SDMemoryCache() + private let cache = NSCache() private init() { self.cache.totalCostLimit = 256_000_000 // 256 MB + + NotificationCenter.default.addObserver(self, selector: #selector(didReceiveMemoryWarning), name: UIApplication.didReceiveMemoryWarningNotification, object: nil) + } + + @objc private func didReceiveMemoryWarning() { + cache.removeAllObjects() } // MARK: - UIImage func setImage(_ image: UIImage, forKey key: String) { - cache.setObject(image, forKey: key as NSString, cost: image.sd_memoryCost) + cache.setObject(image, forKey: key as NSString, cost: Int(image.sd_memoryCost)) } func getImage(forKey key: String) -> UIImage? { @@ -30,7 +36,7 @@ final class MemoryCache { // MARK: - Data func setData(_ data: Data, forKey key: String) { - cache.setObject(data, forKey: key as NSString, cost: UInt(data.count)) + cache.setObject(data as NSData, forKey: key as NSString, cost: data.count) } func geData(forKey key: String) -> Data? { From 028eccd986df409c2a2641cd228dbbf8a4c5b4ae Mon Sep 17 00:00:00 2001 From: kean Date: Tue, 18 Jul 2023 15:08:40 -0400 Subject: [PATCH 7/9] Fix WordPressUI integration by removing non-existent headers --- WordPress/Classes/Utility/UIAlertControllerProxy.m | 1 - WordPress/Classes/Utility/WPError.m | 1 - WordPress/Classes/Utility/WPWebViewController.m | 1 - WordPress/Classes/ViewRelated/Blog/SharingAuthorizationHelper.m | 2 -- .../Classes/ViewRelated/Blog/SharingConnectionsViewController.m | 1 - .../Classes/ViewRelated/Blog/SharingDetailViewController.m | 1 - .../Classes/ViewRelated/Comments/EditCommentViewController.m | 1 - WordPress/Classes/ViewRelated/Me/Views/Header/MeHeaderView.m | 1 - WordPress/Classes/ViewRelated/Menus/MenusViewController.m | 1 - .../Classes/ViewRelated/Post/FeaturedImageViewController.m | 1 - WordPress/Classes/ViewRelated/Post/WPStyleGuide+Pages.m | 1 - .../ViewRelated/Reader/Comments/ReaderCommentsViewController.m | 1 - .../Classes/ViewRelated/Tools/SettingsTextViewController.m | 1 - 13 files changed, 14 deletions(-) diff --git a/WordPress/Classes/Utility/UIAlertControllerProxy.m b/WordPress/Classes/Utility/UIAlertControllerProxy.m index c6aea284b3f4..cc86e4ad2ca0 100644 --- a/WordPress/Classes/Utility/UIAlertControllerProxy.m +++ b/WordPress/Classes/Utility/UIAlertControllerProxy.m @@ -1,5 +1,4 @@ #import "UIAlertControllerProxy.h" -#import #import "WordPress-Swift.h" diff --git a/WordPress/Classes/Utility/WPError.m b/WordPress/Classes/Utility/WPError.m index 89eb893bc3c6..de6b110fd250 100644 --- a/WordPress/Classes/Utility/WPError.m +++ b/WordPress/Classes/Utility/WPError.m @@ -1,7 +1,6 @@ #import "WPError.h" #import "WPAccount.h" #import -#import #import #import "WordPress-Swift.h" diff --git a/WordPress/Classes/Utility/WPWebViewController.m b/WordPress/Classes/Utility/WPWebViewController.m index ad12775d30cd..146b234ac764 100644 --- a/WordPress/Classes/Utility/WPWebViewController.m +++ b/WordPress/Classes/Utility/WPWebViewController.m @@ -5,7 +5,6 @@ #import "Constants.h" #import "WPError.h" #import "WPStyleGuide+WebView.h" -#import #import #import "WordPress-Swift.h" diff --git a/WordPress/Classes/ViewRelated/Blog/SharingAuthorizationHelper.m b/WordPress/Classes/ViewRelated/Blog/SharingAuthorizationHelper.m index 5477f3ee88c8..30436b269ca2 100644 --- a/WordPress/Classes/ViewRelated/Blog/SharingAuthorizationHelper.m +++ b/WordPress/Classes/ViewRelated/Blog/SharingAuthorizationHelper.m @@ -4,8 +4,6 @@ #import "BlogService.h" #import "SVProgressHUD+Dismiss.h" -#import - #import "WordPress-Swift.h" diff --git a/WordPress/Classes/ViewRelated/Blog/SharingConnectionsViewController.m b/WordPress/Classes/ViewRelated/Blog/SharingConnectionsViewController.m index d8c0db3bbb53..8189dafd88b1 100644 --- a/WordPress/Classes/ViewRelated/Blog/SharingConnectionsViewController.m +++ b/WordPress/Classes/ViewRelated/Blog/SharingConnectionsViewController.m @@ -5,7 +5,6 @@ #import "SharingDetailViewController.h" #import "SharingAuthorizationHelper.h" #import -#import #import "WordPress-Swift.h" diff --git a/WordPress/Classes/ViewRelated/Blog/SharingDetailViewController.m b/WordPress/Classes/ViewRelated/Blog/SharingDetailViewController.m index eb556bb91696..538d340f5ee3 100644 --- a/WordPress/Classes/ViewRelated/Blog/SharingDetailViewController.m +++ b/WordPress/Classes/ViewRelated/Blog/SharingDetailViewController.m @@ -4,7 +4,6 @@ #import "SVProgressHUD+Dismiss.h" #import "SharingAuthorizationHelper.h" #import -#import #import "WordPress-Swift.h" diff --git a/WordPress/Classes/ViewRelated/Comments/EditCommentViewController.m b/WordPress/Classes/ViewRelated/Comments/EditCommentViewController.m index 812736cca2a6..4d7d986b54eb 100644 --- a/WordPress/Classes/ViewRelated/Comments/EditCommentViewController.m +++ b/WordPress/Classes/ViewRelated/Comments/EditCommentViewController.m @@ -2,7 +2,6 @@ #import "CommentService.h" #import "CoreDataStack.h" -#import #import "WordPress-Swift.h" diff --git a/WordPress/Classes/ViewRelated/Me/Views/Header/MeHeaderView.m b/WordPress/Classes/ViewRelated/Me/Views/Header/MeHeaderView.m index 2ed1d155d624..ac81fd525de3 100644 --- a/WordPress/Classes/ViewRelated/Me/Views/Header/MeHeaderView.m +++ b/WordPress/Classes/ViewRelated/Me/Views/Header/MeHeaderView.m @@ -1,6 +1,5 @@ #import "MeHeaderView.h" #import "Blog.h" -#import #import "WordPress-Swift.h" diff --git a/WordPress/Classes/ViewRelated/Menus/MenusViewController.m b/WordPress/Classes/ViewRelated/Menus/MenusViewController.m index 3075301e90c7..d90a3c7ad4d3 100644 --- a/WordPress/Classes/ViewRelated/Menus/MenusViewController.m +++ b/WordPress/Classes/ViewRelated/Menus/MenusViewController.m @@ -14,7 +14,6 @@ #import "WordPress-Swift.h" #import #import -#import diff --git a/WordPress/Classes/ViewRelated/Post/FeaturedImageViewController.m b/WordPress/Classes/ViewRelated/Post/FeaturedImageViewController.m index 4cd80d1dec7a..f77747e3bed3 100644 --- a/WordPress/Classes/ViewRelated/Post/FeaturedImageViewController.m +++ b/WordPress/Classes/ViewRelated/Post/FeaturedImageViewController.m @@ -2,7 +2,6 @@ #import "Media.h" #import "WordPress-Swift.h" -#import @interface FeaturedImageViewController () diff --git a/WordPress/Classes/ViewRelated/Post/WPStyleGuide+Pages.m b/WordPress/Classes/ViewRelated/Post/WPStyleGuide+Pages.m index 4ecf1c79270e..e2480941efab 100644 --- a/WordPress/Classes/ViewRelated/Post/WPStyleGuide+Pages.m +++ b/WordPress/Classes/ViewRelated/Post/WPStyleGuide+Pages.m @@ -1,7 +1,6 @@ #import "WPStyleGuide+Pages.h" #import #import -#import #import "WordPress-Swift.h" @implementation WPStyleGuide (Pages) diff --git a/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m b/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m index 1b44e7d036de..0682c39c58ad 100644 --- a/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m +++ b/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m @@ -10,7 +10,6 @@ #import "SuggestionsTableView.h" #import "WordPress-Swift.h" #import "WPAppAnalytics.h" -#import @class Comment; diff --git a/WordPress/Classes/ViewRelated/Tools/SettingsTextViewController.m b/WordPress/Classes/ViewRelated/Tools/SettingsTextViewController.m index b0193746184f..bc38de65b144 100644 --- a/WordPress/Classes/ViewRelated/Tools/SettingsTextViewController.m +++ b/WordPress/Classes/ViewRelated/Tools/SettingsTextViewController.m @@ -1,7 +1,6 @@ #import "SettingsTextViewController.h" #import #import -#import #import "WordPress-Swift.h" From 027537ff2d2de35a861da60636e511537d652eeb Mon Sep 17 00:00:00 2001 From: kean Date: Fri, 21 Jul 2023 12:50:25 -0400 Subject: [PATCH 8/9] Use shorthands for unwrapping --- .../ViewRelated/Media/AnimatedImageCache.swift | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Media/AnimatedImageCache.swift b/WordPress/Classes/ViewRelated/Media/AnimatedImageCache.swift index 88dd699b5a75..9285a5478292 100644 --- a/WordPress/Classes/ViewRelated/Media/AnimatedImageCache.swift +++ b/WordPress/Classes/ViewRelated/Media/AnimatedImageCache.swift @@ -22,33 +22,25 @@ final class AnimatedImageCache { // MARK: Instance methods func cacheData(data: Data, url: URL?) { - guard let url = url else { - return - } + guard let url else { return } let key = url.absoluteString + Constants.keyDataSuffix MemoryCache.shared.setData(data, forKey: key) } func cachedData(url: URL?) -> Data? { - guard let url = url else { - return nil - } + guard let url else { return nil } let key = url.absoluteString + Constants.keyDataSuffix return MemoryCache.shared.geData(forKey: key) } func cacheStaticImage(url: URL?, image: UIImage?) { - guard let url = url, let image = image else { - return - } + guard let url, let image else { return } let key = url.absoluteString + Constants.keyStaticImageSuffix MemoryCache.shared.setImage(image, forKey: key) } func cachedStaticImage(url: URL?) -> UIImage? { - guard let url = url else { - return nil - } + guard let url else { return nil } let key = url.absoluteString + Constants.keyStaticImageSuffix return MemoryCache.shared.getImage(forKey: key) } From 7cffec977f66eef6facf70fb847dca2f7dc8236d Mon Sep 17 00:00:00 2001 From: kean Date: Wed, 26 Jul 2023 10:41:50 -0400 Subject: [PATCH 9/9] Install WordPressUI 1.14 --- Podfile | 2 +- Podfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Podfile b/Podfile index 54dd92e2b6db..ff3bd3f6942b 100644 --- a/Podfile +++ b/Podfile @@ -42,7 +42,7 @@ def aztec end def wordpress_ui - pod 'WordPressUI', '~> 1.12.5' + pod 'WordPressUI', '~> 1.14' # pod 'WordPressUI', git: 'https://github.com/wordpress-mobile/WordPressUI-iOS', tag: '' # pod 'WordPressUI', git: 'https://github.com/wordpress-mobile/WordPressUI-iOS', branch: '' # pod 'WordPressUI', git: 'https://github.com/wordpress-mobile/WordPressUI-iOS', commit: '' diff --git a/Podfile.lock b/Podfile.lock index 5a666d73ba45..7d04188561d3 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -519,7 +519,7 @@ PODS: - WordPressShared (~> 2.0-beta) - wpxmlrpc (~> 0.10) - WordPressShared (2.2.0) - - WordPressUI (1.12.5) + - WordPressUI (1.14.0) - WPMediaPicker (1.8.8) - wpxmlrpc (0.10.0) - Yoga (1.14.0) @@ -614,7 +614,7 @@ DEPENDENCIES: - WordPressAuthenticator (~> 6.3-beta) - WordPressKit (~> 8.5) - WordPressShared (~> 2.2) - - WordPressUI (~> 1.12.5) + - WordPressUI (~> 1.14) - WPMediaPicker (~> 1.8.8) - Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.100.1/third-party-podspecs/Yoga.podspec.json`) - ZendeskSupportSDK (= 5.3.0) @@ -882,7 +882,7 @@ SPEC CHECKSUMS: WordPressAuthenticator: 9bfb316cb165e1997b73aa94d92fadc991cc9725 WordPressKit: f6943a6e927e9f57bc8793938af1e3a4c3adb614 WordPressShared: 87f3ee89b0a3e83106106f13a8b71605fb8eb6d2 - WordPressUI: c5be816f6c7b3392224ac21de9e521e89fa108ac + WordPressUI: 09b5477f8678446f27e651cccb6b6401bd19d9a3 WPMediaPicker: 0d40b8d66b6dfdaa2d6a41e3be51249ff5898775 wpxmlrpc: 68db063041e85d186db21f674adf08d9c70627fd Yoga: 5e12f4deb20582f86f6323e1cdff25f07afc87f6 @@ -895,6 +895,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: ae5b4b813d216d3bf0a148773267fff14bd51d37 -PODFILE CHECKSUM: bc29b8c7c29da0feaa4a5ed3dff2a805e1a06a93 +PODFILE CHECKSUM: e815177754512909cfd69c325e5898954f2e6525 COCOAPODS: 1.12.1