Cache the feed provider results to make it as fast as the other icon look up types.
This commit is contained in:
parent
c44759fdb2
commit
f8a664d035
|
@ -24,6 +24,14 @@ public final class WebFeedIconDownloader {
|
||||||
|
|
||||||
private let imageDownloader: ImageDownloader
|
private let imageDownloader: ImageDownloader
|
||||||
|
|
||||||
|
private var feedURLToIconURLCache = [String: String]()
|
||||||
|
private var feedURLToIconURLCachePath: String
|
||||||
|
private var feedURLToIconURLCacheDirty = false {
|
||||||
|
didSet {
|
||||||
|
queueSaveFeedURLToIconURLCacheIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var homePageToIconURLCache = [String: String]()
|
private var homePageToIconURLCache = [String: String]()
|
||||||
private var homePageToIconURLCachePath: String
|
private var homePageToIconURLCachePath: String
|
||||||
private var homePageToIconURLCacheDirty = false {
|
private var homePageToIconURLCacheDirty = false {
|
||||||
|
@ -50,8 +58,10 @@ public final class WebFeedIconDownloader {
|
||||||
|
|
||||||
init(imageDownloader: ImageDownloader, folder: String) {
|
init(imageDownloader: ImageDownloader, folder: String) {
|
||||||
self.imageDownloader = imageDownloader
|
self.imageDownloader = imageDownloader
|
||||||
|
self.feedURLToIconURLCachePath = (folder as NSString).appendingPathComponent("FeedURLToIconURLCache.plist")
|
||||||
self.homePageToIconURLCachePath = (folder as NSString).appendingPathComponent("HomePageToIconURLCache.plist")
|
self.homePageToIconURLCachePath = (folder as NSString).appendingPathComponent("HomePageToIconURLCache.plist")
|
||||||
self.homePagesWithNoIconURLCachePath = (folder as NSString).appendingPathComponent("HomePagesWithNoIconURLCache.plist")
|
self.homePagesWithNoIconURLCachePath = (folder as NSString).appendingPathComponent("HomePagesWithNoIconURLCache.plist")
|
||||||
|
loadFeedURLToIconURLCache()
|
||||||
loadHomePageToIconURLCache()
|
loadHomePageToIconURLCache()
|
||||||
loadHomePagesWithNoIconURLCache()
|
loadHomePagesWithNoIconURLCache()
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(imageDidBecomeAvailable(_:)), name: .ImageDidBecomeAvailable, object: imageDownloader)
|
NotificationCenter.default.addObserver(self, selector: #selector(imageDidBecomeAvailable(_:)), name: .ImageDidBecomeAvailable, object: imageDownloader)
|
||||||
|
@ -98,19 +108,32 @@ public final class WebFeedIconDownloader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let components = URLComponents(string: feed.url), let feedProvider = ExtensionPointManager.shared.bestFeedProvider(for: components, with: nil) {
|
if let feedProviderURL = feedURLToIconURLCache[feed.url] {
|
||||||
feedProvider.iconURL(components) { result in
|
self.icon(forURL: feedProviderURL, feed: feed) { (image) in
|
||||||
if case .success(let url) = result {
|
|
||||||
self.icon(forURL: url, feed: feed) { (image) in
|
|
||||||
if let image = image {
|
if let image = image {
|
||||||
self.postFeedIconDidBecomeAvailableNotification(feed)
|
self.postFeedIconDidBecomeAvailableNotification(feed)
|
||||||
self.cache[feed] = IconImage(image)
|
self.cache[feed] = IconImage(image)
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let components = URLComponents(string: feed.url), let feedProvider = ExtensionPointManager.shared.bestFeedProvider(for: components, with: nil) {
|
||||||
|
feedProvider.iconURL(components) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let feedProviderURL):
|
||||||
|
self.feedURLToIconURLCache[feed.url] = feedProviderURL
|
||||||
|
self.feedURLToIconURLCacheDirty = true
|
||||||
|
self.icon(forURL: feedProviderURL, feed: feed) { (image) in
|
||||||
|
if let image = image {
|
||||||
|
self.postFeedIconDidBecomeAvailableNotification(feed)
|
||||||
|
self.cache[feed] = IconImage(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .failure:
|
||||||
checkFeedIconURL()
|
checkFeedIconURL()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
checkFeedIconURL()
|
checkFeedIconURL()
|
||||||
}
|
}
|
||||||
|
@ -126,6 +149,12 @@ public final class WebFeedIconDownloader {
|
||||||
_ = icon(for: feed)
|
_ = icon(for: feed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func saveFeedURLToIconURLCacheIfNeeded() {
|
||||||
|
if feedURLToIconURLCacheDirty {
|
||||||
|
saveFeedURLToIconURLCache()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc func saveHomePageToIconURLCacheIfNeeded() {
|
@objc func saveHomePageToIconURLCacheIfNeeded() {
|
||||||
if homePageToIconURLCacheDirty {
|
if homePageToIconURLCacheDirty {
|
||||||
saveHomePageToIconURLCache()
|
saveHomePageToIconURLCache()
|
||||||
|
@ -216,6 +245,15 @@ private extension WebFeedIconDownloader {
|
||||||
homePagesWithNoIconURLCacheDirty = true
|
homePagesWithNoIconURLCacheDirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadFeedURLToIconURLCache() {
|
||||||
|
let url = URL(fileURLWithPath: feedURLToIconURLCachePath)
|
||||||
|
guard let data = try? Data(contentsOf: url) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let decoder = PropertyListDecoder()
|
||||||
|
feedURLToIconURLCache = (try? decoder.decode([String: String].self, from: data)) ?? [String: String]()
|
||||||
|
}
|
||||||
|
|
||||||
func loadHomePageToIconURLCache() {
|
func loadHomePageToIconURLCache() {
|
||||||
let url = URL(fileURLWithPath: homePageToIconURLCachePath)
|
let url = URL(fileURLWithPath: homePageToIconURLCachePath)
|
||||||
guard let data = try? Data(contentsOf: url) else {
|
guard let data = try? Data(contentsOf: url) else {
|
||||||
|
@ -235,6 +273,10 @@ private extension WebFeedIconDownloader {
|
||||||
homePagesWithNoIconURLCache = Set(decoded)
|
homePagesWithNoIconURLCache = Set(decoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func queueSaveFeedURLToIconURLCacheIfNeeded() {
|
||||||
|
WebFeedIconDownloader.saveQueue.add(self, #selector(saveFeedURLToIconURLCacheIfNeeded))
|
||||||
|
}
|
||||||
|
|
||||||
func queueSaveHomePageToIconURLCacheIfNeeded() {
|
func queueSaveHomePageToIconURLCacheIfNeeded() {
|
||||||
WebFeedIconDownloader.saveQueue.add(self, #selector(saveHomePageToIconURLCacheIfNeeded))
|
WebFeedIconDownloader.saveQueue.add(self, #selector(saveHomePageToIconURLCacheIfNeeded))
|
||||||
}
|
}
|
||||||
|
@ -243,6 +285,20 @@ private extension WebFeedIconDownloader {
|
||||||
WebFeedIconDownloader.saveQueue.add(self, #selector(saveHomePagesWithNoIconURLCacheIfNeeded))
|
WebFeedIconDownloader.saveQueue.add(self, #selector(saveHomePagesWithNoIconURLCacheIfNeeded))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func saveFeedURLToIconURLCache() {
|
||||||
|
feedURLToIconURLCacheDirty = false
|
||||||
|
|
||||||
|
let encoder = PropertyListEncoder()
|
||||||
|
encoder.outputFormat = .binary
|
||||||
|
let url = URL(fileURLWithPath: feedURLToIconURLCachePath)
|
||||||
|
do {
|
||||||
|
let data = try encoder.encode(feedURLToIconURLCache)
|
||||||
|
try data.write(to: url)
|
||||||
|
} catch {
|
||||||
|
assertionFailure(error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func saveHomePageToIconURLCache() {
|
func saveHomePageToIconURLCache() {
|
||||||
homePageToIconURLCacheDirty = false
|
homePageToIconURLCacheDirty = false
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue