diff --git a/Shared/Images/FeedIconDownloader.swift b/Shared/Images/FeedIconDownloader.swift index 2d924e584..e1edec0e3 100644 --- a/Shared/Images/FeedIconDownloader.swift +++ b/Shared/Images/FeedIconDownloader.swift @@ -32,7 +32,14 @@ public final class FeedIconDownloader { } } - private var homePagesWithNoIconURL = Set() + private var homePagesWithNoIconURLCache = Set() + private var homePagesWithNoIconURLCachePath: String + private var homePagesWithNoIconURLCacheDirty = false { + didSet { + queueHomePagesWithNoIconURLCacheIfNeeded() + } + } + private var urlsInProgress = Set() private var cache = [Feed: RSImage]() private var waitingForFeedURLs = [String: Feed]() @@ -40,7 +47,9 @@ public final class FeedIconDownloader { init(imageDownloader: ImageDownloader, folder: String) { self.imageDownloader = imageDownloader self.homePageToIconURLCachePath = (folder as NSString).appendingPathComponent("HomePageToIconURLCache.plist") + self.homePagesWithNoIconURLCachePath = (folder as NSString).appendingPathComponent("HomePagesWithNoIconURLCache.plist") loadHomePageToIconURLCache() + loadHomePagesWithNoIconURLCache() NotificationCenter.default.addObserver(self, selector: #selector(imageDidBecomeAvailable(_:)), name: .ImageDidBecomeAvailable, object: imageDownloader) } @@ -99,13 +108,19 @@ public final class FeedIconDownloader { } } + @objc func saveHomePagesWithNoIconURLCacheIfNeeded() { + if homePagesWithNoIconURLCacheDirty { + saveHomePagesWithNoIconURLCache() + } + } + } private extension FeedIconDownloader { func icon(forHomePageURL homePageURL: String, feed: Feed, _ imageResultBlock: @escaping (RSImage?) -> Void) { - if homePagesWithNoIconURL.contains(homePageURL) { + if homePagesWithNoIconURLCache.contains(homePageURL) { imageResultBlock(nil) return } @@ -141,7 +156,8 @@ private extension FeedIconDownloader { } func cacheIconURL(for homePageURL: String, _ iconURL: String) { - homePagesWithNoIconURL.remove(homePageURL) + homePagesWithNoIconURLCache.remove(homePageURL) + homePagesWithNoIconURLCacheDirty = true homePageToIconURLCache[homePageURL] = iconURL homePageToIconURLCacheDirty = true } @@ -172,7 +188,8 @@ private extension FeedIconDownloader { return } - homePagesWithNoIconURL.insert(homePageURL) + homePagesWithNoIconURLCache.insert(homePageURL) + homePagesWithNoIconURLCacheDirty = true } func loadHomePageToIconURLCache() { @@ -184,10 +201,24 @@ private extension FeedIconDownloader { homePageToIconURLCache = (try? decoder.decode([String: String].self, from: data)) ?? [String: String]() } + func loadHomePagesWithNoIconURLCache() { + let url = URL(fileURLWithPath: homePagesWithNoIconURLCachePath) + guard let data = try? Data(contentsOf: url) else { + return + } + let decoder = PropertyListDecoder() + let decoded = (try? decoder.decode([String].self, from: data)) ?? [String]() + homePagesWithNoIconURLCache = Set(decoded) + } + func queueSaveHomePageToIconURLCacheIfNeeded() { FeedIconDownloader.saveQueue.add(self, #selector(saveHomePageToIconURLCacheIfNeeded)) } + func queueHomePagesWithNoIconURLCacheIfNeeded() { + FeedIconDownloader.saveQueue.add(self, #selector(saveHomePagesWithNoIconURLCacheIfNeeded)) + } + func saveHomePageToIconURLCache() { homePageToIconURLCacheDirty = false @@ -202,4 +233,18 @@ private extension FeedIconDownloader { } } + func saveHomePagesWithNoIconURLCache() { + homePagesWithNoIconURLCacheDirty = false + + let encoder = PropertyListEncoder() + encoder.outputFormat = .binary + let url = URL(fileURLWithPath: homePagesWithNoIconURLCachePath) + do { + let data = try encoder.encode(Array(homePagesWithNoIconURLCache)) + try data.write(to: url) + } catch { + assertionFailure(error.localizedDescription) + } + } + } diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index f0fe5936f..17a1ef1bd 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -200,12 +200,13 @@ private extension AppDelegate { let faviconsFolderURL = tempDir.appendingPathComponent("Favicons") let imagesFolderURL = tempDir.appendingPathComponent("Images") let homePageToIconURL = tempDir.appendingPathComponent("HomePageToIconURLCache.plist") - + let homePagesWithNoIconURL = tempDir.appendingPathComponent("HomePagesWithNoIconURLCache.plist") + // If the image disk cache hasn't been flushed for 3 days and the network is available, delete it if let flushDate = AppDefaults.lastImageCacheFlushDate, flushDate.addingTimeInterval(3600*24*3) < Date() { if let reachability = try? Reachability(hostname: "apple.com") { if reachability.connection != .unavailable { - for tempItem in [faviconsFolderURL, imagesFolderURL, homePageToIconURL] { + for tempItem in [faviconsFolderURL, imagesFolderURL, homePageToIconURL, homePagesWithNoIconURL] { do { os_log(.info, log: self.log, "Removing cache file: %@", tempItem.absoluteString) try FileManager.default.removeItem(at: tempItem)