diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index f4cea74d9..cc072f4f9 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -1259,7 +1259,8 @@ private extension MainWindowController { } func startArticleExtractorForCurrentLink() { - if let link = currentLink, let extractor = ArticleExtractor(link, secretsProvider: Secrets()) { + let secrets = Secrets() + if let link = currentLink, let extractor = ArticleExtractor(link, clientID: secrets.mercuryClientId, clientSecret: secrets.mercuryClientSecret) { extractor.delegate = self extractor.process() articleExtractor = extractor diff --git a/Shared/Article Extractor/ArticleExtractor.swift b/Shared/Article Extractor/ArticleExtractor.swift index b4ebb57f2..eb025e5ea 100644 --- a/Shared/Article Extractor/ArticleExtractor.swift +++ b/Shared/Article Extractor/ArticleExtractor.swift @@ -7,8 +7,7 @@ // import Foundation -import Account -import Secrets +import FoundationExtras public enum ArticleExtractorState { case ready @@ -24,23 +23,23 @@ protocol ArticleExtractorDelegate { @MainActor func articleExtractionDidComplete(extractedArticle: ExtractedArticle) } -final class ArticleExtractor { - +@MainActor final class ArticleExtractor { + private var dataTask: URLSessionDataTask? = nil var state: ArticleExtractorState! - var article: ExtractedArticle? + var article: ExtractedArticle? var delegate: ArticleExtractorDelegate? - var articleLink: String? - - private var url: URL! - - public init?(_ articleLink: String, secretsProvider: SecretsProvider) { + let articleLink: String? + + private let url: URL! + + public init?(_ articleLink: String, clientID: String, clientSecret: String) { self.articleLink = articleLink let clientURL = "https://extract.feedbin.com/parser" - let username = secretsProvider.mercuryClientId - let signature = articleLink.hmacUsingSHA1(key: secretsProvider.mercuryClientSecret) + let username = clientID + let signature = articleLink.hmacUsingSHA1(key: clientSecret) if let base64URL = articleLink.data(using: .utf8)?.base64EncodedString() { let fullURL = "\(clientURL)/\(username)/\(signature)?base64_url=\(base64URL)" @@ -54,59 +53,70 @@ final class ArticleExtractor { } public func process() { - - state = .processing - dataTask = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in - - guard let self = self else { return } - - if let error = error { - self.state = .failedToParse - DispatchQueue.main.async { - self.delegate?.articleExtractionDidFail(with: error) - } - return - } - - guard let data = data else { - self.state = .failedToParse - DispatchQueue.main.async { - self.delegate?.articleExtractionDidFail(with: URLError(.cannotDecodeContentData)) - } - return - } - - do { - let decoder = JSONDecoder() - decoder.dateDecodingStrategy = .iso8601 - self.article = try decoder.decode(ExtractedArticle.self, from: data) - - DispatchQueue.main.async { - if self.article?.content == nil { - self.state = .failedToParse - self.delegate?.articleExtractionDidFail(with: URLError(.cannotDecodeContentData)) + state = .processing + + dataTask = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in + + Task { @MainActor [weak self] in + guard let self else { + return + } + + if let error { + self.noteDidFail(error: error) + return + } + + guard let data else { + self.noteDidFail(error: URLError(.cannotDecodeContentData)) + return + } + + do { + let article = try decodeArticle(data: data) + self.article = article + + if article.content == nil { + self.noteDidFail(error: URLError(.cannotDecodeContentData)) } else { - self.state = .complete - self.delegate?.articleExtractionDidComplete(extractedArticle: self.article!) + self.noteDidComplete(article: article) } - } - } catch { - self.state = .failedToParse - DispatchQueue.main.async { - self.delegate?.articleExtractionDidFail(with: error) - } - } - - } - + } catch { + self.noteDidFail(error: error) + } + } + } + dataTask!.resume() - } public func cancel() { state = .cancelled dataTask?.cancel() } - +} + +private extension ArticleExtractor { + + func decodeArticle(data: Data) throws -> ExtractedArticle { + + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + + let article = try decoder.decode(ExtractedArticle.self, from: data) + return article + } + + func noteDidFail(error: Error) { + + state = .failedToParse + delegate?.articleExtractionDidFail(with: error) + } + + func noteDidComplete(article: ExtractedArticle) { + + state = .complete + delegate?.articleExtractionDidComplete(extractedArticle: article) + } } diff --git a/iOS/Article/WebViewController.swift b/iOS/Article/WebViewController.swift index 668b6548d..5ca9bdc52 100644 --- a/iOS/Article/WebViewController.swift +++ b/iOS/Article/WebViewController.swift @@ -657,7 +657,8 @@ private extension WebViewController { func startArticleExtractor() { guard articleExtractor == nil else { return } - if let link = article?.preferredLink, let extractor = ArticleExtractor(link, secretsProvider: Secrets()) { + let secrets = Secrets() + if let link = article?.preferredLink, let extractor = ArticleExtractor(link, clientID: secrets.mercuryClientId, clientSecret: secrets.mercuryClientSecret) { extractor.delegate = self extractor.process() articleExtractor = extractor