mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2025-01-25 00:15:28 +01:00
149 lines
3.0 KiB
Swift
149 lines
3.0 KiB
Swift
//
|
||
// SingleFaviconDownloader.swift
|
||
// NetNewsWire
|
||
//
|
||
// Created by Brent Simmons on 11/23/17.
|
||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||
//
|
||
|
||
import Foundation
|
||
import os.log
|
||
import Web
|
||
import FoundationExtras
|
||
import Core
|
||
|
||
// 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")
|
||
}
|
||
|
||
@MainActor final class SingleFaviconDownloader {
|
||
|
||
enum DiskStatus {
|
||
case unknown, notOnDisk, onDisk
|
||
}
|
||
|
||
let faviconURL: String
|
||
var iconImage: IconImage?
|
||
let homePageURL: String?
|
||
|
||
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "SingleFaviconDownloader")
|
||
|
||
private var lastDownloadAttemptDate: Date
|
||
private var diskStatus = DiskStatus.unknown
|
||
private var diskCache: BinaryDiskCache
|
||
|
||
private var diskKey: String {
|
||
return faviconURL.md5String
|
||
}
|
||
|
||
init(faviconURL: String, homePageURL: String?, diskCache: BinaryDiskCache) {
|
||
|
||
self.faviconURL = faviconURL
|
||
self.homePageURL = homePageURL
|
||
self.diskCache = diskCache
|
||
self.lastDownloadAttemptDate = Date()
|
||
|
||
Task {
|
||
await findFavicon()
|
||
}
|
||
}
|
||
|
||
func downloadFaviconIfNeeded() -> Bool {
|
||
|
||
// If we don’t have an image, and lastDownloadAttemptDate is a while ago, try again.
|
||
|
||
if let _ = iconImage {
|
||
return false
|
||
}
|
||
|
||
let retryInterval: TimeInterval = 30 * 60 // 30 minutes
|
||
if Date().timeIntervalSince(lastDownloadAttemptDate) < retryInterval {
|
||
return false
|
||
}
|
||
|
||
lastDownloadAttemptDate = Date()
|
||
|
||
Task {
|
||
await findFavicon()
|
||
}
|
||
|
||
return true
|
||
}
|
||
}
|
||
|
||
private extension SingleFaviconDownloader {
|
||
|
||
func findFavicon() async {
|
||
|
||
if let image = await readFromDisk() {
|
||
diskStatus = .onDisk
|
||
iconImage = IconImage(image)
|
||
postDidLoadFaviconNotification()
|
||
return
|
||
}
|
||
|
||
diskStatus = .notOnDisk
|
||
|
||
if let image = await downloadFavicon() {
|
||
iconImage = IconImage(image)
|
||
postDidLoadFaviconNotification()
|
||
}
|
||
}
|
||
|
||
func readFromDisk() async -> RSImage? {
|
||
|
||
guard diskStatus != .notOnDisk else {
|
||
return nil
|
||
}
|
||
guard let data = await diskCache[self.diskKey], !data.isEmpty else {
|
||
return nil
|
||
}
|
||
|
||
return await RSImage.image(with: data)
|
||
}
|
||
|
||
func saveToDisk(_ data: Data) {
|
||
|
||
Task.detached {
|
||
try? await self.diskCache.setData(data, forKey: self.diskKey)
|
||
|
||
Task { @MainActor in
|
||
self.diskStatus = .onDisk
|
||
}
|
||
}
|
||
}
|
||
|
||
func downloadFavicon() async -> RSImage? {
|
||
|
||
guard let url = URL(string: faviconURL) else {
|
||
return nil
|
||
}
|
||
|
||
do {
|
||
let downloadData = try await downloadUsingCache(url)
|
||
|
||
let data = downloadData.data
|
||
let response = downloadData.response
|
||
|
||
if let data, !data.isEmpty, let response, response.statusIsOK {
|
||
saveToDisk(data)
|
||
return await RSImage.image(with: data)
|
||
}
|
||
|
||
} catch {
|
||
os_log(.info, log: self.log, "Error downloading image at %@: %@.", url.absoluteString, error.localizedDescription)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func postDidLoadFaviconNotification() {
|
||
|
||
assert(Thread.isMainThread)
|
||
NotificationCenter.default.post(name: .DidLoadFavicon, object: self)
|
||
}
|
||
}
|