Make ArticleExtractor MainActor; make it forget about SecretsProvider and the Secrets module.

This commit is contained in:
Brent Simmons 2024-04-01 22:40:27 -07:00
parent 1c9452a1c5
commit 6e8eecb6a9
3 changed files with 71 additions and 59 deletions

View File

@ -1259,7 +1259,8 @@ private extension MainWindowController {
} }
func startArticleExtractorForCurrentLink() { 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.delegate = self
extractor.process() extractor.process()
articleExtractor = extractor articleExtractor = extractor

View File

@ -7,8 +7,7 @@
// //
import Foundation import Foundation
import Account import FoundationExtras
import Secrets
public enum ArticleExtractorState { public enum ArticleExtractorState {
case ready case ready
@ -24,23 +23,23 @@ protocol ArticleExtractorDelegate {
@MainActor func articleExtractionDidComplete(extractedArticle: ExtractedArticle) @MainActor func articleExtractionDidComplete(extractedArticle: ExtractedArticle)
} }
final class ArticleExtractor { @MainActor final class ArticleExtractor {
private var dataTask: URLSessionDataTask? = nil private var dataTask: URLSessionDataTask? = nil
var state: ArticleExtractorState! var state: ArticleExtractorState!
var article: ExtractedArticle? var article: ExtractedArticle?
var delegate: ArticleExtractorDelegate? var delegate: ArticleExtractorDelegate?
var articleLink: String? let articleLink: String?
private var url: URL! private let url: URL!
public init?(_ articleLink: String, secretsProvider: SecretsProvider) { public init?(_ articleLink: String, clientID: String, clientSecret: String) {
self.articleLink = articleLink self.articleLink = articleLink
let clientURL = "https://extract.feedbin.com/parser" let clientURL = "https://extract.feedbin.com/parser"
let username = secretsProvider.mercuryClientId let username = clientID
let signature = articleLink.hmacUsingSHA1(key: secretsProvider.mercuryClientSecret) let signature = articleLink.hmacUsingSHA1(key: clientSecret)
if let base64URL = articleLink.data(using: .utf8)?.base64EncodedString() { if let base64URL = articleLink.data(using: .utf8)?.base64EncodedString() {
let fullURL = "\(clientURL)/\(username)/\(signature)?base64_url=\(base64URL)" let fullURL = "\(clientURL)/\(username)/\(signature)?base64_url=\(base64URL)"
@ -54,59 +53,70 @@ final class ArticleExtractor {
} }
public func process() { public func process() {
state = .processing
dataTask = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in state = .processing
guard let self = self else { return } dataTask = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
if let error = error { Task { @MainActor [weak self] in
self.state = .failedToParse guard let self else {
DispatchQueue.main.async { return
self.delegate?.articleExtractionDidFail(with: error) }
}
return if let error {
} self.noteDidFail(error: error)
return
guard let data = data else { }
self.state = .failedToParse
DispatchQueue.main.async { guard let data else {
self.delegate?.articleExtractionDidFail(with: URLError(.cannotDecodeContentData)) self.noteDidFail(error: URLError(.cannotDecodeContentData))
} return
return }
}
do {
do { let article = try decodeArticle(data: data)
let decoder = JSONDecoder() self.article = article
decoder.dateDecodingStrategy = .iso8601
self.article = try decoder.decode(ExtractedArticle.self, from: data) if article.content == nil {
self.noteDidFail(error: URLError(.cannotDecodeContentData))
DispatchQueue.main.async {
if self.article?.content == nil {
self.state = .failedToParse
self.delegate?.articleExtractionDidFail(with: URLError(.cannotDecodeContentData))
} else { } else {
self.state = .complete self.noteDidComplete(article: article)
self.delegate?.articleExtractionDidComplete(extractedArticle: self.article!)
} }
} } catch {
} catch { self.noteDidFail(error: error)
self.state = .failedToParse }
DispatchQueue.main.async { }
self.delegate?.articleExtractionDidFail(with: error) }
}
}
}
dataTask!.resume() dataTask!.resume()
} }
public func cancel() { public func cancel() {
state = .cancelled state = .cancelled
dataTask?.cancel() 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)
}
} }

View File

@ -657,7 +657,8 @@ private extension WebViewController {
func startArticleExtractor() { func startArticleExtractor() {
guard articleExtractor == nil else { return } 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.delegate = self
extractor.process() extractor.process()
articleExtractor = extractor articleExtractor = extractor