2017-11-24 22:12:18 +01:00
|
|
|
|
//
|
|
|
|
|
// SingleFaviconDownloader.swift
|
2018-08-29 07:18:24 +02:00
|
|
|
|
// NetNewsWire
|
2017-11-24 22:12:18 +01:00
|
|
|
|
//
|
|
|
|
|
// Created by Brent Simmons on 11/23/17.
|
|
|
|
|
// Copyright © 2017 Ranchero Software. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
2019-04-15 22:03:05 +02:00
|
|
|
|
import Foundation
|
2017-11-24 22:12:18 +01:00
|
|
|
|
import RSCore
|
|
|
|
|
import RSWeb
|
|
|
|
|
|
|
|
|
|
// The image may be on disk already. If not, download it.
|
|
|
|
|
// Post .DidLoadFavicon notification once it’s in memory.
|
|
|
|
|
|
|
|
|
|
extension Notification.Name {
|
|
|
|
|
|
|
|
|
|
static let DidLoadFavicon = Notification.Name("DidLoadFaviconNotification")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final class SingleFaviconDownloader {
|
|
|
|
|
|
|
|
|
|
enum DiskStatus {
|
|
|
|
|
case unknown, notOnDisk, onDisk
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let faviconURL: String
|
2019-11-06 01:05:57 +01:00
|
|
|
|
var iconImage: IconImage?
|
2019-11-26 02:54:09 +01:00
|
|
|
|
let homePageURL: String?
|
2017-11-24 22:12:18 +01:00
|
|
|
|
|
|
|
|
|
private var lastDownloadAttemptDate: Date
|
|
|
|
|
private var diskStatus = DiskStatus.unknown
|
|
|
|
|
private var diskCache: BinaryDiskCache
|
|
|
|
|
private let queue: DispatchQueue
|
|
|
|
|
|
|
|
|
|
private var diskKey: String {
|
2020-01-18 08:00:56 +01:00
|
|
|
|
return faviconURL.md5String
|
2017-11-24 22:12:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-26 02:54:09 +01:00
|
|
|
|
init(faviconURL: String, homePageURL: String?, diskCache: BinaryDiskCache, queue: DispatchQueue) {
|
2017-11-24 22:12:18 +01:00
|
|
|
|
|
|
|
|
|
self.faviconURL = faviconURL
|
2019-11-26 02:54:09 +01:00
|
|
|
|
self.homePageURL = homePageURL
|
2017-11-24 22:12:18 +01:00
|
|
|
|
self.diskCache = diskCache
|
|
|
|
|
self.queue = queue
|
|
|
|
|
self.lastDownloadAttemptDate = Date()
|
|
|
|
|
|
|
|
|
|
findFavicon()
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-24 18:21:08 +01:00
|
|
|
|
func downloadFaviconIfNeeded() -> Bool {
|
2017-11-24 22:12:18 +01:00
|
|
|
|
|
|
|
|
|
// If we don’t have an image, and lastDownloadAttemptDate is a while ago, try again.
|
|
|
|
|
|
2019-11-06 01:05:57 +01:00
|
|
|
|
if let _ = iconImage {
|
2020-03-24 18:21:08 +01:00
|
|
|
|
return false
|
2017-11-24 22:12:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let retryInterval: TimeInterval = 30 * 60 // 30 minutes
|
|
|
|
|
if Date().timeIntervalSince(lastDownloadAttemptDate) < retryInterval {
|
2020-03-24 18:21:08 +01:00
|
|
|
|
return false
|
2017-11-24 22:12:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lastDownloadAttemptDate = Date()
|
|
|
|
|
findFavicon()
|
2020-03-24 18:21:08 +01:00
|
|
|
|
|
|
|
|
|
return true
|
2017-11-24 22:12:18 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private extension SingleFaviconDownloader {
|
|
|
|
|
|
|
|
|
|
func findFavicon() {
|
|
|
|
|
|
|
|
|
|
readFromDisk { (image) in
|
|
|
|
|
|
|
|
|
|
if let image = image {
|
|
|
|
|
self.diskStatus = .onDisk
|
2019-11-06 01:05:57 +01:00
|
|
|
|
self.iconImage = IconImage(image)
|
2017-11-24 22:12:18 +01:00
|
|
|
|
self.postDidLoadFaviconNotification()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.diskStatus = .notOnDisk
|
|
|
|
|
|
|
|
|
|
self.downloadFavicon { (image) in
|
|
|
|
|
|
|
|
|
|
if let image = image {
|
2019-11-06 01:05:57 +01:00
|
|
|
|
self.iconImage = IconImage(image)
|
2017-11-24 22:12:18 +01:00
|
|
|
|
}
|
2019-11-27 22:40:35 +01:00
|
|
|
|
|
|
|
|
|
self.postDidLoadFaviconNotification()
|
2019-11-27 21:08:52 +01:00
|
|
|
|
|
2017-11-24 22:12:18 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-15 02:01:34 +01:00
|
|
|
|
func readFromDisk(_ completion: @escaping (RSImage?) -> Void) {
|
2017-11-24 22:12:18 +01:00
|
|
|
|
|
|
|
|
|
guard diskStatus != .notOnDisk else {
|
2019-12-15 02:01:34 +01:00
|
|
|
|
completion(nil)
|
2017-11-24 22:12:18 +01:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
queue.async {
|
|
|
|
|
|
|
|
|
|
if let data = self.diskCache[self.diskKey], !data.isEmpty {
|
2020-01-10 01:22:06 +01:00
|
|
|
|
RSImage.image(with: data, imageResultBlock: completion)
|
2017-11-24 22:12:18 +01:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DispatchQueue.main.async {
|
2019-12-15 02:01:34 +01:00
|
|
|
|
completion(nil)
|
2017-11-24 22:12:18 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func saveToDisk(_ data: Data) {
|
|
|
|
|
|
|
|
|
|
queue.async {
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
try self.diskCache.setData(data, forKey: self.diskKey)
|
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
self.diskStatus = .onDisk
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-15 02:01:34 +01:00
|
|
|
|
func downloadFavicon(_ completion: @escaping (RSImage?) -> Void) {
|
2017-11-24 22:12:18 +01:00
|
|
|
|
|
|
|
|
|
guard let url = URL(string: faviconURL) else {
|
2019-12-15 02:01:34 +01:00
|
|
|
|
completion(nil)
|
2017-11-24 22:12:18 +01:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
downloadUsingCache(url) { (data, response, error) in
|
|
|
|
|
|
|
|
|
|
if let data = data, !data.isEmpty, let response = response, response.statusIsOK, error == nil {
|
|
|
|
|
self.saveToDisk(data)
|
2020-01-10 01:22:06 +01:00
|
|
|
|
RSImage.image(with: data, imageResultBlock: completion)
|
2017-11-24 22:12:18 +01:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-26 01:15:36 +01:00
|
|
|
|
if let error = error {
|
|
|
|
|
appDelegate.logMessage("Error downloading favicon at \(url): \(error)", type: .warning)
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-15 02:01:34 +01:00
|
|
|
|
completion(nil)
|
2017-11-24 22:12:18 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func postDidLoadFaviconNotification() {
|
|
|
|
|
|
|
|
|
|
assert(Thread.isMainThread)
|
|
|
|
|
NotificationCenter.default.post(name: .DidLoadFavicon, object: self)
|
|
|
|
|
}
|
2019-04-12 00:53:03 +02:00
|
|
|
|
|
2017-11-24 22:12:18 +01:00
|
|
|
|
}
|