diff --git a/Shared/Favicons/FaviconURLFinder.swift b/Shared/Favicons/FaviconURLFinder.swift index 7d7001b04..213db70fe 100644 --- a/Shared/Favicons/FaviconURLFinder.swift +++ b/Shared/Favicons/FaviconURLFinder.swift @@ -30,14 +30,13 @@ struct FaviconURLFinder { return } - // If the favicon has an explicit type, check that for an ignored type; otherwise, check the file extension. - HTMLMetadataDownloader.downloadMetadata(for: homePageURL) { htmlMetadata in - let faviconURLs = htmlMetadata?.favicons.compactMap { favicon -> String? in + Task { @MainActor in - guard shouldAllowFavicon(favicon) else { - return nil - } - return favicon.urlString + // If the favicon has an explicit type, check that for an ignored type; otherwise, check the file extension. + let htmlMetadata = try? await HTMLMetadataDownloader.downloadMetadata(for: homePageURL) + + let faviconURLs = htmlMetadata?.favicons.compactMap { favicon -> String? in + shouldAllowFavicon(favicon) ? favicon.urlString : nil } completion(faviconURLs) diff --git a/Shared/HTMLMetadata/HTMLMetadataDownloader.swift b/Shared/HTMLMetadata/HTMLMetadataDownloader.swift index 051ba744f..ae21b1bd6 100644 --- a/Shared/HTMLMetadata/HTMLMetadataDownloader.swift +++ b/Shared/HTMLMetadata/HTMLMetadataDownloader.swift @@ -10,34 +10,35 @@ import Foundation import Web import Parser +extension RSHTMLMetadata: @unchecked Sendable {} + struct HTMLMetadataDownloader { - static let serialDispatchQueue = DispatchQueue(label: "HTMLMetadataDownloader") + @MainActor static func downloadMetadata(for url: String) async -> RSHTMLMetadata? { - @MainActor static func downloadMetadata(for url: String, _ completion: @escaping @Sendable (RSHTMLMetadata?) -> Void) { guard let actualURL = URL(unicodeString: url) else { - completion(nil) - return + return nil } - downloadUsingCache(actualURL) { (data, response, error) in - if let data = data, !data.isEmpty, let response = response, response.statusIsOK, error == nil { - let urlToUse = response.url ?? actualURL - let parserData = ParserData(url: urlToUse.absoluteString, data: data) - parseMetadata(with: parserData, completion) - return - } + let downloadData = try? await downloadUsingCache(actualURL) + let data = downloadData?.data + let response = downloadData?.response - completion(nil) + if let data, !data.isEmpty, let response, response.statusIsOK { + let urlToUse = response.url ?? actualURL + let parserData = ParserData(url: urlToUse.absoluteString, data: data) + return await parseMetadata(with: parserData) } + + return nil } - private static func parseMetadata(with parserData: ParserData, _ completion: @escaping @Sendable (RSHTMLMetadata?) -> Void) { - serialDispatchQueue.async { - let htmlMetadata = RSHTMLMetadataParser.htmlMetadata(with: parserData) - DispatchQueue.main.async { - completion(htmlMetadata) - } + @MainActor private static func parseMetadata(with parserData: ParserData) async -> RSHTMLMetadata? { + + let task = Task.detached { () -> RSHTMLMetadata? in + RSHTMLMetadataParser.htmlMetadata(with: parserData) } + + return await task.value } } diff --git a/Shared/Images/FeedIconDownloader.swift b/Shared/Images/FeedIconDownloader.swift index 63ffd6ad3..e8212198e 100644 --- a/Shared/Images/FeedIconDownloader.swift +++ b/Shared/Images/FeedIconDownloader.swift @@ -214,15 +214,15 @@ private extension FeedIconDownloader { } urlsInProgress.insert(homePageURL) - HTMLMetadataDownloader.downloadMetadata(for: homePageURL) { (metadata) in + Task { @MainActor in - MainActor.assumeIsolated { - self.urlsInProgress.remove(homePageURL) - guard let metadata = metadata else { - return - } - self.pullIconURL(from: metadata, homePageURL: homePageURL, feed: feed) + let metadata = try? await HTMLMetadataDownloader.downloadMetadata(for: homePageURL) + + self.urlsInProgress.remove(homePageURL) + guard let metadata else { + return } + self.pullIconURL(from: metadata, homePageURL: homePageURL, feed: feed) } }