Remove FeedProviderManager and Feed Provider and remove references to them.

This commit is contained in:
Brent Simmons 2023-06-25 16:11:55 -07:00
parent de1dca3912
commit 3aeda820e4
8 changed files with 12 additions and 315 deletions

View File

@ -176,20 +176,14 @@ final class CloudKitAccountDelegate: AccountDelegate {
}
func createWebFeed(for account: Account, url urlString: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
guard let url = URL(string: urlString), let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
guard let url = URL(string: urlString) else {
completion(.failure(LocalAccountDelegateError.invalidParameter))
return
}
let editedName = name == nil || name!.isEmpty ? nil : name
// Username should be part of the URL on new feed adds
if let feedProvider = FeedProviderManager.shared.best(for: urlComponents) {
createProviderWebFeed(for: account, urlComponents: urlComponents, editedName: editedName, container: container, feedProvider: feedProvider, completion: completion)
} else {
createRSSWebFeed(for: account, url: url, editedName: editedName, container: container, validateFeed: validateFeed, completion: completion)
}
createRSSWebFeed(for: account, url: url, editedName: editedName, container: container, validateFeed: validateFeed, completion: completion)
}
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
@ -570,119 +564,15 @@ private extension CloudKitAccountDelegate {
func combinedRefresh(_ account: Account, _ webFeeds: Set<WebFeed>, completion: @escaping (Result<Void, Error>) -> Void) {
var refresherWebFeeds = Set<WebFeed>()
let group = DispatchGroup()
var feedProviderError: Error? = nil
for webFeed in webFeeds {
if let components = URLComponents(string: webFeed.url), let feedProvider = FeedProviderManager.shared.best(for: components) {
group.enter()
feedProvider.refresh(webFeed) { result in
switch result {
case .success(let parsedItems):
account.update(webFeed.webFeedID, with: parsedItems) { result in
switch result {
case .success(let articleChanges):
self.storeArticleChanges(new: articleChanges.newArticles, updated: articleChanges.updatedArticles, deleted: articleChanges.deletedArticles) {
self.refreshProgress.completeTask()
group.leave()
}
case .failure(let error):
os_log(.error, log: self.log, "CloudKit Feed refresh update error: %@.", error.localizedDescription)
self.refreshProgress.completeTask()
group.leave()
}
}
case .failure(let error):
os_log(.error, log: self.log, "CloudKit Feed refresh error: %@.", error.localizedDescription)
feedProviderError = error
self.refreshProgress.completeTask()
group.leave()
}
}
} else {
refresherWebFeeds.insert(webFeed)
}
}
group.enter()
refresher.refreshFeeds(refresherWebFeeds) {
refresher.refreshFeeds(webFeeds) {
group.leave()
}
group.notify(queue: DispatchQueue.main) {
if let error = feedProviderError {
completion(.failure(error))
} else {
completion(.success(()))
}
}
}
func createProviderWebFeed(for account: Account, urlComponents: URLComponents, editedName: String?, container: Container, feedProvider: FeedProvider, completion: @escaping (Result<WebFeed, Error>) -> Void) {
refreshProgress.addToNumberOfTasksAndRemaining(5)
feedProvider.metaData(urlComponents) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let metaData):
guard let urlString = urlComponents.url?.absoluteString else {
self.refreshProgress.completeTasks(4)
completion(.failure(AccountError.createErrorNotFound))
return
}
self.accountZone.createWebFeed(url: urlString, name: metaData.name, editedName: editedName, homePageURL: metaData.homePageURL, container: container) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let externalID):
let feed = account.createWebFeed(with: metaData.name, url: urlString, webFeedID: urlString, homePageURL: metaData.homePageURL)
feed.editedName = editedName
feed.externalID = externalID
container.addWebFeed(feed)
feedProvider.refresh(feed) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let parsedItems):
account.update(urlString, with: parsedItems) { result in
switch result {
case .success:
self.sendNewArticlesToTheCloud(account, feed)
self.refreshProgress.clear()
completion(.success(feed))
case .failure(let error):
self.refreshProgress.completeTasks(2)
completion(.failure(error))
}
}
case .failure:
self.refreshProgress.completeTasks(3)
completion(.failure(AccountError.createErrorNotFound))
}
}
case .failure(let error):
self.refreshProgress.completeTasks(4)
completion(.failure(error))
}
}
case .failure:
self.refreshProgress.completeTasks(4)
completion(.failure(AccountError.createErrorNotFound))
}
completion(.success(()))
}
}

View File

@ -1,38 +0,0 @@
//
// FeedProvider.swift
// FeedProvider
//
// Created by Maurice Parker on 4/6/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import RSCore
import RSParser
public enum FeedProviderAbility {
case owner
case available
case none
}
public struct FeedProviderFeedMetaData {
let name: String
let homePageURL: String?
}
public protocol FeedProvider {
/// Informs the caller of the ability for this feed provider to service the given URL
func ability(_ urlComponents: URLComponents) -> FeedProviderAbility
/// Provide the iconURL of the given URL
func iconURL(_ urlComponents: URLComponents, completion: @escaping (Result<String, Error>) -> Void)
/// Construct the associated metadata for the new feed
func metaData(_ urlComponents: URLComponents, completion: @escaping (Result<FeedProviderFeedMetaData, Error>) -> Void)
/// Refresh all the article entries (ParsedItems)
func refresh(_ webFeed: WebFeed, completion: @escaping (Result<Set<ParsedItem>, Error>) -> Void)
}

View File

@ -1,42 +0,0 @@
//
// FeedProviderManager.swift
// Account
//
// Created by Maurice Parker on 4/16/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
public protocol FeedProviderManagerDelegate: AnyObject {
var activeFeedProviders: [FeedProvider] { get }
}
public final class FeedProviderManager {
public static let shared = FeedProviderManager()
public weak var delegate: FeedProviderManagerDelegate?
public func best(for offered: URLComponents) -> FeedProvider? {
if let owner = feedProviderMatching(offered, ability: .owner) {
return owner
}
return feedProviderMatching(offered, ability: .available)
}
}
private extension FeedProviderManager {
func feedProviderMatching(_ offered: URLComponents, ability: FeedProviderAbility) -> FeedProvider? {
if let delegate = delegate {
for feedProvider in delegate.activeFeedProviders {
if feedProvider.ability(offered) == ability {
return feedProvider
}
}
}
return nil
}
}

View File

@ -50,48 +50,20 @@ final class LocalAccountDelegate: AccountDelegate {
return
}
var refresherWebFeeds = Set<WebFeed>()
let webFeeds = account.flattenedWebFeeds()
refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
let group = DispatchGroup()
var feedProviderError: Error? = nil
for webFeed in webFeeds {
if let components = URLComponents(string: webFeed.url), let feedProvider = FeedProviderManager.shared.best(for: components) {
group.enter()
feedProvider.refresh(webFeed) { result in
switch result {
case .success(let parsedItems):
account.update(webFeed.webFeedID, with: parsedItems) { _ in
self.refreshProgress.completeTask()
group.leave()
}
case .failure(let error):
os_log(.error, log: self.log, "Feed Provider refresh error: %@.", error.localizedDescription)
feedProviderError = error
self.refreshProgress.completeTask()
group.leave()
}
}
} else {
refresherWebFeeds.insert(webFeed)
}
}
group.enter()
refresher?.refreshFeeds(refresherWebFeeds) {
refresher?.refreshFeeds(webFeeds) {
group.leave()
}
group.notify(queue: DispatchQueue.main) {
self.refreshProgress.clear()
account.metadata.lastArticleFetchEndTime = Date()
if let error = feedProviderError {
completion(.failure(error))
} else {
completion(.success(()))
}
completion(.success(()))
}
}
@ -156,12 +128,7 @@ final class LocalAccountDelegate: AccountDelegate {
return
}
// Username should be part of the URL on new feed adds
if let feedProvider = FeedProviderManager.shared.best(for: urlComponents) {
createProviderWebFeed(for: account, urlComponents: urlComponents, editedName: name, container: container, feedProvider: feedProvider, completion: completion)
} else {
createRSSWebFeed(for: account, url: url, editedName: name, container: container, completion: completion)
}
createRSSWebFeed(for: account, url: url, editedName: name, container: container, completion: completion)
}
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
@ -264,44 +231,6 @@ extension LocalAccountDelegate: LocalAccountRefresherDelegate {
private extension LocalAccountDelegate {
func createProviderWebFeed(for account: Account, urlComponents: URLComponents, editedName: String?, container: Container, feedProvider: FeedProvider, completion: @escaping (Result<WebFeed, Error>) -> Void) {
refreshProgress.addToNumberOfTasksAndRemaining(2)
feedProvider.metaData(urlComponents) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let metaData):
guard let urlString = urlComponents.url?.absoluteString else {
completion(.failure(AccountError.createErrorNotFound))
return
}
let feed = account.createWebFeed(with: metaData.name, url: urlString, webFeedID: urlString, homePageURL: metaData.homePageURL)
feed.editedName = editedName
container.addWebFeed(feed)
feedProvider.refresh(feed) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let parsedItems):
account.update(urlString, with: parsedItems) { _ in
completion(.success(feed))
}
case .failure(let error):
self.refreshProgress.clear()
completion(.failure(error))
}
}
case .failure:
self.refreshProgress.clear()
completion(.failure(AccountError.createErrorNotFound))
}
}
}
func createRSSWebFeed(for account: Account, url: URL, editedName: String?, container: Container, completion: @escaping (Result<WebFeed, Error>) -> Void) {
// We need to use a batch update here because we need to assign add the feed to the

View File

@ -234,10 +234,7 @@ public final class WebFeed: Feed, Renamable, Hashable {
return false
}
if FeedProviderManager.shared.best(for: components) == nil {
return false
}
return true
return false
}
}

View File

@ -123,7 +123,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
SecretsManager.provider = Secrets()
AccountManager.shared = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
ArticleThemesManager.shared = ArticleThemesManager(folderPath: Platform.dataSubfolder(forApplication: nil, folderName: "Themes")!)
FeedProviderManager.shared.delegate = ExtensionPointManager.shared
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(inspectableObjectsDidChange(_:)), name: .InspectableObjectsDidChange, object: nil)

View File

@ -27,7 +27,7 @@ public enum ExtensionPointManagerError: LocalizedError {
}
final class ExtensionPointManager: FeedProviderManagerDelegate {
final class ExtensionPointManager {
static let shared = ExtensionPointManager()
@ -65,10 +65,6 @@ final class ExtensionPointManager: FeedProviderManagerDelegate {
return commands
}
var activeFeedProviders: [FeedProvider] {
return activeExtensionPoints.values.compactMap({ return $0 as? FeedProvider })
}
init() {
possibleExtensionPointTypes = []
loadExtensionPoints()
@ -126,14 +122,4 @@ private extension ExtensionPointManager {
#endif
}
}
func feedProviderMatching(_ offered: URLComponents, ability: FeedProviderAbility) -> FeedProvider? {
for extensionPoint in activeExtensionPoints.values {
if let feedProvider = extensionPoint as? FeedProvider, feedProvider.ability(offered) == ability {
return feedProvider
}
}
return nil
}
}

View File

@ -118,31 +118,7 @@ public final class WebFeedIconDownloader {
return nil
}
if let components = URLComponents(string: feed.url), let feedProvider = FeedProviderManager.shared.best(for: components) {
guard !urlsInProgress.contains(feed.url) else {
return nil
}
urlsInProgress.insert(feed.url)
feedProvider.iconURL(components) { result in
self.urlsInProgress.remove(feed.url)
switch result {
case .success(let feedProviderURL):
self.feedURLToIconURLCache[feed.url] = feedProviderURL
self.feedURLToIconURLCacheDirty = true
self.icon(forURL: feedProviderURL, feed: feed) { (image) in
if let image = image {
self.postFeedIconDidBecomeAvailableNotification(feed)
self.cache[feed] = IconImage(image)
}
}
case .failure:
checkFeedIconURL()
}
}
} else {
checkFeedIconURL()
}
checkFeedIconURL()
return nil
}