Cache favicon to homepage mappings

This commit is contained in:
Maurice Parker 2019-10-31 14:04:34 -05:00
parent 5bcb5a982f
commit 8ba15c6234
2 changed files with 99 additions and 5 deletions

View File

@ -18,11 +18,28 @@ extension Notification.Name {
final class FaviconDownloader { final class FaviconDownloader {
private static let saveQueue = CoalescingQueue(name: "Cache Save Queue", interval: 1.0)
private let folder: String private let folder: String
private let diskCache: BinaryDiskCache private let diskCache: BinaryDiskCache
private var singleFaviconDownloaderCache = [String: SingleFaviconDownloader]() // faviconURL: SingleFaviconDownloader private var singleFaviconDownloaderCache = [String: SingleFaviconDownloader]() // faviconURL: SingleFaviconDownloader
private var homePageToFaviconURLCache = [String: String]() //homePageURL: faviconURL private var homePageToFaviconURLCache = [String: String]() //homePageURL: faviconURL
private var homePageURLsWithNoFaviconURL = Set<String>() private var homePageToFaviconURLCachePath: String
private var homePageToFaviconURLCacheDirty = false {
didSet {
queueSaveHomePageToFaviconURLCacheIfNeeded()
}
}
private var homePageURLsWithNoFaviconURLCache = Set<String>()
private var homePageURLsWithNoFaviconURLCachePath: String
private var homePageURLsWithNoFaviconURLCacheDirty = false {
didSet {
queueSaveHomePageURLsWithNoFaviconURLCacheIfNeeded()
}
}
private let queue: DispatchQueue private let queue: DispatchQueue
private var cache = [Feed: RSImage]() // faviconURL: RSImage private var cache = [Feed: RSImage]() // faviconURL: RSImage
@ -36,6 +53,11 @@ final class FaviconDownloader {
self.diskCache = BinaryDiskCache(folder: folder) self.diskCache = BinaryDiskCache(folder: folder)
self.queue = DispatchQueue(label: "FaviconDownloader serial queue - \(folder)") self.queue = DispatchQueue(label: "FaviconDownloader serial queue - \(folder)")
self.homePageToFaviconURLCachePath = (folder as NSString).appendingPathComponent("HomePageToFaviconURLCache.plist")
self.homePageURLsWithNoFaviconURLCachePath = (folder as NSString).appendingPathComponent("HomePageURLsWithNoFaviconURLCache.plist")
loadHomePageToFaviconURLCache()
loadHomePageURLsWithNoFaviconURLCache()
NotificationCenter.default.addObserver(self, selector: #selector(didLoadFavicon(_:)), name: .DidLoadFavicon, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didLoadFavicon(_:)), name: .DidLoadFavicon, object: nil)
} }
@ -92,7 +114,7 @@ final class FaviconDownloader {
func favicon(withHomePageURL homePageURL: String) -> RSImage? { func favicon(withHomePageURL homePageURL: String) -> RSImage? {
let url = homePageURL.rs_normalizedURL() let url = homePageURL.rs_normalizedURL()
if homePageURLsWithNoFaviconURL.contains(url) { if homePageURLsWithNoFaviconURLCache.contains(url) {
return nil return nil
} }
@ -103,10 +125,12 @@ final class FaviconDownloader {
findFaviconURL(with: url) { (faviconURL) in findFaviconURL(with: url) { (faviconURL) in
if let faviconURL = faviconURL { if let faviconURL = faviconURL {
self.homePageToFaviconURLCache[url] = faviconURL self.homePageToFaviconURLCache[url] = faviconURL
self.homePageToFaviconURLCacheDirty = true
let _ = self.favicon(with: faviconURL) let _ = self.favicon(with: faviconURL)
} }
else { else {
self.homePageURLsWithNoFaviconURL.insert(url) self.homePageURLsWithNoFaviconURLCache.insert(url)
self.homePageURLsWithNoFaviconURLCacheDirty = true
} }
} }
@ -126,6 +150,18 @@ final class FaviconDownloader {
postFaviconDidBecomeAvailableNotification(singleFaviconDownloader.faviconURL) postFaviconDidBecomeAvailableNotification(singleFaviconDownloader.faviconURL)
} }
@objc func saveHomePageToFaviconURLCacheIfNeeded() {
if homePageToFaviconURLCacheDirty {
saveHomePageToFaviconURLCache()
}
}
@objc func saveHomePageURLsWithNoFaviconURLCacheIfNeeded() {
if homePageURLsWithNoFaviconURLCacheDirty {
saveHomePageURLsWithNoFaviconURLCache()
}
}
} }
private extension FaviconDownloader { private extension FaviconDownloader {
@ -175,4 +211,60 @@ private extension FaviconDownloader {
NotificationCenter.default.post(name: .FaviconDidBecomeAvailable, object: self, userInfo: userInfo) NotificationCenter.default.post(name: .FaviconDidBecomeAvailable, object: self, userInfo: userInfo)
} }
} }
func loadHomePageToFaviconURLCache() {
let url = URL(fileURLWithPath: homePageToFaviconURLCachePath)
guard let data = try? Data(contentsOf: url) else {
return
}
let decoder = PropertyListDecoder()
homePageToFaviconURLCache = (try? decoder.decode([String: String].self, from: data)) ?? [String: String]()
}
func loadHomePageURLsWithNoFaviconURLCache() {
let url = URL(fileURLWithPath: homePageURLsWithNoFaviconURLCachePath)
guard let data = try? Data(contentsOf: url) else {
return
}
let decoder = PropertyListDecoder()
let decoded = (try? decoder.decode([String].self, from: data)) ?? [String]()
homePageURLsWithNoFaviconURLCache = Set(decoded)
}
func queueSaveHomePageToFaviconURLCacheIfNeeded() {
FaviconDownloader.saveQueue.add(self, #selector(saveHomePageToFaviconURLCacheIfNeeded))
}
func queueSaveHomePageURLsWithNoFaviconURLCacheIfNeeded() {
FaviconDownloader.saveQueue.add(self, #selector(saveHomePageURLsWithNoFaviconURLCacheIfNeeded))
}
func saveHomePageToFaviconURLCache() {
homePageToFaviconURLCacheDirty = false
let encoder = PropertyListEncoder()
encoder.outputFormat = .binary
let url = URL(fileURLWithPath: homePageToFaviconURLCachePath)
do {
let data = try encoder.encode(homePageToFaviconURLCache)
try data.write(to: url)
} catch {
assertionFailure(error.localizedDescription)
}
}
func saveHomePageURLsWithNoFaviconURLCache() {
homePageURLsWithNoFaviconURLCacheDirty = false
let encoder = PropertyListEncoder()
encoder.outputFormat = .binary
let url = URL(fileURLWithPath: homePageURLsWithNoFaviconURLCachePath)
do {
let data = try encoder.encode(Array(homePageURLsWithNoFaviconURLCache))
try data.write(to: url)
} catch {
assertionFailure(error.localizedDescription)
}
}
} }

View File

@ -201,12 +201,14 @@ private extension AppDelegate {
let imagesFolderURL = tempDir.appendingPathComponent("Images") let imagesFolderURL = tempDir.appendingPathComponent("Images")
let homePageToIconURL = tempDir.appendingPathComponent("HomePageToIconURLCache.plist") let homePageToIconURL = tempDir.appendingPathComponent("HomePageToIconURLCache.plist")
let homePagesWithNoIconURL = tempDir.appendingPathComponent("HomePagesWithNoIconURLCache.plist") let homePagesWithNoIconURL = tempDir.appendingPathComponent("HomePagesWithNoIconURLCache.plist")
let homePageToFaviconURL = tempDir.appendingPathComponent("HomePageToFaviconURLCache.plist")
let homePageURLsWithNoFaviconURL = tempDir.appendingPathComponent("HomePageURLsWithNoFaviconURLCache.plist")
// If the image disk cache hasn't been flushed for 3 days and the network is available, delete it // 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 flushDate = AppDefaults.lastImageCacheFlushDate, flushDate.addingTimeInterval(3600*24*3) < Date() {
if let reachability = try? Reachability(hostname: "apple.com") { if let reachability = try? Reachability(hostname: "apple.com") {
if reachability.connection != .unavailable { if reachability.connection != .unavailable {
for tempItem in [faviconsFolderURL, imagesFolderURL, homePageToIconURL, homePagesWithNoIconURL] { for tempItem in [faviconsFolderURL, imagesFolderURL, homePageToIconURL, homePagesWithNoIconURL, homePageToFaviconURL, homePageURLsWithNoFaviconURL] {
do { do {
os_log(.info, log: self.log, "Removing cache file: %@", tempItem.absoluteString) os_log(.info, log: self.log, "Removing cache file: %@", tempItem.absoluteString)
try FileManager.default.removeItem(at: tempItem) try FileManager.default.removeItem(at: tempItem)