151 lines
2.9 KiB
Swift
151 lines
2.9 KiB
Swift
|
//
|
|||
|
// SingleFaviconDownloader.swift
|
|||
|
// Evergreen
|
|||
|
//
|
|||
|
// Created by Brent Simmons on 11/23/17.
|
|||
|
// Copyright © 2017 Ranchero Software. All rights reserved.
|
|||
|
//
|
|||
|
|
|||
|
import AppKit
|
|||
|
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
|
|||
|
var image: NSImage?
|
|||
|
|
|||
|
private var lastDownloadAttemptDate: Date
|
|||
|
private var diskStatus = DiskStatus.unknown
|
|||
|
private var diskCache: BinaryDiskCache
|
|||
|
private let queue: DispatchQueue
|
|||
|
|
|||
|
private var diskKey: String {
|
|||
|
return (faviconURL as NSString).rs_md5Hash()
|
|||
|
}
|
|||
|
|
|||
|
init(faviconURL: String, diskCache: BinaryDiskCache, queue: DispatchQueue) {
|
|||
|
|
|||
|
self.faviconURL = faviconURL
|
|||
|
self.diskCache = diskCache
|
|||
|
self.queue = queue
|
|||
|
self.lastDownloadAttemptDate = Date()
|
|||
|
|
|||
|
findFavicon()
|
|||
|
}
|
|||
|
|
|||
|
func downloadFaviconIfNeeded() {
|
|||
|
|
|||
|
// If we don’t have an image, and lastDownloadAttemptDate is a while ago, try again.
|
|||
|
|
|||
|
if let _ = image {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
let retryInterval: TimeInterval = 30 * 60 // 30 minutes
|
|||
|
if Date().timeIntervalSince(lastDownloadAttemptDate) < retryInterval {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
lastDownloadAttemptDate = Date()
|
|||
|
findFavicon()
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private extension SingleFaviconDownloader {
|
|||
|
|
|||
|
func findFavicon() {
|
|||
|
|
|||
|
readFromDisk { (image) in
|
|||
|
|
|||
|
if let image = image {
|
|||
|
self.diskStatus = .onDisk
|
|||
|
self.image = image
|
|||
|
self.postDidLoadFaviconNotification()
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
self.diskStatus = .notOnDisk
|
|||
|
|
|||
|
self.downloadFavicon { (image) in
|
|||
|
|
|||
|
if let image = image {
|
|||
|
self.image = image
|
|||
|
self.postDidLoadFaviconNotification()
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func readFromDisk(_ callback: @escaping (NSImage?) -> Void) {
|
|||
|
|
|||
|
guard diskStatus != .notOnDisk else {
|
|||
|
callback(nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
queue.async {
|
|||
|
|
|||
|
if let data = self.diskCache[self.diskKey], !data.isEmpty {
|
|||
|
NSImage.rs_image(with: data, imageResultBlock: callback)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
DispatchQueue.main.async {
|
|||
|
callback(nil)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func saveToDisk(_ data: Data) {
|
|||
|
|
|||
|
queue.async {
|
|||
|
|
|||
|
do {
|
|||
|
try self.diskCache.setData(data, forKey: self.diskKey)
|
|||
|
DispatchQueue.main.async {
|
|||
|
self.diskStatus = .onDisk
|
|||
|
}
|
|||
|
}
|
|||
|
catch {}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func downloadFavicon(_ callback: @escaping (NSImage?) -> Void) {
|
|||
|
|
|||
|
guard let url = URL(string: faviconURL) else {
|
|||
|
callback(nil)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
downloadUsingCache(url) { (data, response, error) in
|
|||
|
|
|||
|
if let data = data, !data.isEmpty, let response = response, response.statusIsOK, error == nil {
|
|||
|
self.saveToDisk(data)
|
|||
|
NSImage.rs_image(with: data, imageResultBlock: callback)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
callback(nil)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func postDidLoadFaviconNotification() {
|
|||
|
|
|||
|
assert(Thread.isMainThread)
|
|||
|
NotificationCenter.default.post(name: .DidLoadFavicon, object: self)
|
|||
|
}
|
|||
|
}
|