Cache favicon to homepage mappings
This commit is contained in:
parent
5bcb5a982f
commit
8ba15c6234
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue