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 diff --git a/WordPress/Classes/System/WordPressAppDelegate.swift b/WordPress/Classes/System/WordPressAppDelegate.swift index 21ff24a135f7..d7cb5e59cbd0 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() + MemoryCache.shared.register() // Start CrashLogging as soon as possible (in case a crash happens during startup) try? loggingStack.start() 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/Media/AnimatedImageCache.swift b/WordPress/Classes/ViewRelated/Media/AnimatedImageCache.swift index ab086dddf8c1..9285a5478292 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,50 +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 else { return } + let key = url.absoluteString + Constants.keyDataSuffix + MemoryCache.shared.setData(data, forKey: key) } func cachedData(url: URL?) -> Data? { - guard let key = url else { - return nil - } - return AnimatedImageCache.cache.object(forKey: key as AnyObject) as? Data + 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 - AnimatedImageCache.cache.setObject(image as AnyObject, forKey: key as AnyObject) + 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 AnimatedImageCache.cache.object(forKey: key as AnyObject) as? UIImage + return MemoryCache.shared.getImage(forKey: key) } func animatedImage(_ urlRequest: URLRequest, @@ -81,7 +57,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 +77,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 +88,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 new file mode 100644 index 000000000000..760735624cf4 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Media/MemoryCache.swift @@ -0,0 +1,118 @@ +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 = 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: Int(image.sd_memoryCost)) + } + + 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) + } + + // MARK: - Data + + func setData(_ data: Data, forKey key: String) { + cache.setObject(data as NSData, forKey: key as NSString, cost: 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) + } +} + +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) + ) + + // WordPress.AnimatedImageCache uses WordPress.MemoryCache directly + } +} + +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) + } +} + +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 + } +} 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" diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 64ab744a04f5..48e2d2a58776 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 /* 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 */; }; @@ -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 /* 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 = ""; }; @@ -12503,6 +12506,7 @@ D8A3A5AD206A059100992576 /* StockPhotos */, FF8A04DF1D9BFE7400523BC4 /* CachedAnimatedImageView.swift */, 7435CE7220A4B9170075A1B9 /* AnimatedImageCache.swift */, + 0C7073942A65CB2E00F325CE /* MemoryCache.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 /* MemoryCache.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 /* MemoryCache.swift in Sources */, DCFC097429D3549C00277ECB /* DashboardDomainsCardCell.swift in Sources */, F44293DD28E45DBA00D340AF /* AppIconListViewModel.swift in Sources */, 8B45C12727B2B08900EA3257 /* DashboardStatsCardCell.swift in Sources */,