From 4b54f19a38c64ff23fcfa67ff586ed5ef92f0a9e Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 25 Apr 2020 15:09:03 -0500 Subject: [PATCH] Refactor CloudKit specific feed refreshing out into a standalone class --- .../Account/Account.xcodeproj/project.pbxproj | 4 + .../CloudKit/CloudKitAccountDelegate.swift | 74 ++------------- .../CloudKitArticlesZoneDelegate.swift | 10 +- .../CloudKit/CloudKitFeedRefresher.swift | 94 +++++++++++++++++++ 4 files changed, 114 insertions(+), 68 deletions(-) create mode 100644 Frameworks/Account/CloudKit/CloudKitFeedRefresher.swift diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index 6573d99b1..d1f524d3f 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ 510BD113232C3E9D002692E4 /* WebFeedMetadataFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510BD112232C3E9D002692E4 /* WebFeedMetadataFile.swift */; }; 510E3317244E0CED00E7A6AF /* TwitterMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510E3316244E0CED00E7A6AF /* TwitterMedia.swift */; }; 511B9804237CD4270028BCAA /* FeedIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9803237CD4270028BCAA /* FeedIdentifier.swift */; }; + 5124A1612454C91B00C1245B /* CloudKitFeedRefresher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5124A1602454C91B00C1245B /* CloudKitFeedRefresher.swift */; }; 512DD4CB2431000600C17B1F /* CKRecord+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512DD4CA2431000600C17B1F /* CKRecord+Extensions.swift */; }; 512DD4CD2431098700C17B1F /* CloudKitAccountZoneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 512DD4CC2431098700C17B1F /* CloudKitAccountZoneDelegate.swift */; }; 5132AAC42448BAD90077840A /* FeedProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5132AAC12448BAD90077840A /* FeedProvider.swift */; }; @@ -285,6 +286,7 @@ 511076A3243BD33100D97C8C /* .framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = .framework; sourceTree = BUILT_PRODUCTS_DIR; }; 511076F4243BD96D00D97C8C /* FeedProvider.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FeedProvider.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 511B9803237CD4270028BCAA /* FeedIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedIdentifier.swift; sourceTree = ""; }; + 5124A1602454C91B00C1245B /* CloudKitFeedRefresher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitFeedRefresher.swift; sourceTree = ""; }; 512DD4CA2431000600C17B1F /* CKRecord+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CKRecord+Extensions.swift"; sourceTree = ""; }; 512DD4CC2431098700C17B1F /* CloudKitAccountZoneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitAccountZoneDelegate.swift; sourceTree = ""; }; 5132AAC12448BAD90077840A /* FeedProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedProvider.swift; sourceTree = ""; }; @@ -556,6 +558,7 @@ 519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */, 519E84AB2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift */, 5150FFFD243823B800C1A442 /* CloudKitError.swift */, + 5124A1602454C91B00C1245B /* CloudKitFeedRefresher.swift */, 51E4DB2D242633ED0091EB5B /* CloudKitZone.swift */, 51C034DE242D65D20014DC71 /* CloudKitZoneResult.swift */, ); @@ -1172,6 +1175,7 @@ 519E84A62433D49000D238B0 /* OPMLNormalizer.swift in Sources */, 9EEEF71F23545CB4009E9D80 /* FeedlySendArticleStatusesOperation.swift in Sources */, 9EBD49C223C67784005AD5CD /* FeedlyEntryIdentifierProviding.swift in Sources */, + 5124A1612454C91B00C1245B /* CloudKitFeedRefresher.swift in Sources */, 846E77541F6F00E300A165E2 /* AccountManager.swift in Sources */, 51E490362288C37100C791F0 /* FeedbinDate.swift in Sources */, 9EEAE06E235D002D00E3FEE4 /* FeedlyGetCollectionsService.swift in Sources */, diff --git a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift index 3b3358447..4669a4fcc 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift @@ -37,14 +37,18 @@ final class CloudKitAccountDelegate: AccountDelegate { private let accountZone: CloudKitAccountZone private let articlesZone: CloudKitArticlesZone - weak var account: Account? - private lazy var refresher: LocalAccountRefresher = { let refresher = LocalAccountRefresher() refresher.delegate = self return refresher }() + private lazy var cloudKitFeedRefresher: CloudKitFeedRefresher = { + return CloudKitFeedRefresher(refreshProgress: refreshProgress, refresher: refresher, articlesZone: articlesZone) + }() + + weak var account: Account? + let behaviors: AccountBehaviors = [] let isOPMLImportInProgress = false @@ -542,7 +546,7 @@ private extension CloudKitAccountDelegate { self.refreshProgress.completeTask() - self.refreshWebFeeds(account, webFeeds) { + self.cloudKitFeedRefresher.refresh(account, webFeeds) { self.refreshProgress.clear() account.metadata.lastArticleFetchEndTime = Date() } @@ -562,70 +566,6 @@ private extension CloudKitAccountDelegate { fail(error) } } - } - - func refreshWebFeeds(_ account: Account, _ webFeeds: Set, completion: @escaping () -> Void) { - - var newArticles = Set
() - var deletedArticles = Set
() - - var refresherWebFeeds = Set() - let group = DispatchGroup() - - refreshProgress.addToNumberOfTasksAndRemaining(2) - - 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): - - newArticles.formUnion(articleChanges.newArticles ?? Set
()) - deletedArticles.formUnion(articleChanges.deletedArticles ?? Set
()) - - self.refreshProgress.completeTask() - group.leave() - - case .failure(let error): - os_log(.error, log: self.log, "Feed Provider refresh update error: %@.", error.localizedDescription) - self.refreshProgress.completeTask() - group.leave() - } - - } - - case .failure(let error): - os_log(.error, log: self.log, "Feed Provider refresh error: %@.", error.localizedDescription) - self.refreshProgress.completeTask() - group.leave() - } - } - } else { - refresherWebFeeds.insert(webFeed) - } - } - - group.enter() - refresher.refreshFeeds(refresherWebFeeds) { - group.leave() - } - - group.notify(queue: DispatchQueue.main) { - - self.articlesZone.deleteArticles(deletedArticles) { _ in - self.refreshProgress.completeTask() - self.articlesZone.sendNewArticles(newArticles) { _ in - self.refreshProgress.completeTask() - completion() - } - } - - } } diff --git a/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift b/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift index ab7cb23ce..ab6d312d2 100644 --- a/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitArticlesZoneDelegate.swift @@ -30,6 +30,10 @@ class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate { return refresher }() + private lazy var cloudKitFeedRefresher: CloudKitFeedRefresher = { + return CloudKitFeedRefresher(refreshProgress: refreshProgress, refresher: refresher, articlesZone: articlesZone) + }() + init(account: Account, database: SyncDatabase, articlesZone: CloudKitArticlesZone, refreshProgress: DownloadProgress?) { self.account = account self.database = database @@ -114,7 +118,11 @@ private extension CloudKitArticlesZoneDelegate { if webFeeds.isEmpty { group.leave() } else { - self.refresher.refreshFeeds(webFeeds) { + if let account = self.account { + self.cloudKitFeedRefresher.refresh(account, webFeeds) { + group.leave() + } + } else { group.leave() } } diff --git a/Frameworks/Account/CloudKit/CloudKitFeedRefresher.swift b/Frameworks/Account/CloudKit/CloudKitFeedRefresher.swift new file mode 100644 index 000000000..a1352dc0c --- /dev/null +++ b/Frameworks/Account/CloudKit/CloudKitFeedRefresher.swift @@ -0,0 +1,94 @@ +// +// CloudKitFeedRefresher.swift +// Account +// +// Created by Maurice Parker on 4/25/20. +// Copyright © 2020 Ranchero Software, LLC. All rights reserved. +// + +import Foundation +import os.log +import RSWeb +import Articles + +final class CloudKitFeedRefresher { + + private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit") + + weak var refreshProgress: DownloadProgress? + weak var refresher: LocalAccountRefresher? + weak var articlesZone: CloudKitArticlesZone? + + init(refreshProgress: DownloadProgress?, refresher: LocalAccountRefresher?, articlesZone: CloudKitArticlesZone?) { + self.refreshProgress = refreshProgress + self.refresher = refresher + self.articlesZone = articlesZone + } + + func refresh(_ account: Account, _ webFeeds: Set, completion: @escaping () -> Void) { + guard let refreshProgress = refreshProgress, let refresher = refresher, let articlesZone = articlesZone else { return } + + var newArticles = Set
() + var deletedArticles = Set
() + + var refresherWebFeeds = Set() + let group = DispatchGroup() + + refreshProgress.addToNumberOfTasksAndRemaining(2) + + 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): + + newArticles.formUnion(articleChanges.newArticles ?? Set
()) + deletedArticles.formUnion(articleChanges.deletedArticles ?? Set
()) + + refreshProgress.completeTask() + group.leave() + + case .failure(let error): + os_log(.error, log: self.log, "CloudKit Feed refresh update error: %@.", error.localizedDescription) + refreshProgress.completeTask() + group.leave() + } + + } + + case .failure(let error): + os_log(.error, log: self.log, "CloudKit Feed refresh error: %@.", error.localizedDescription) + refreshProgress.completeTask() + group.leave() + } + } + } else { + refresherWebFeeds.insert(webFeed) + } + } + + group.enter() + refresher.refreshFeeds(refresherWebFeeds) { + group.leave() + } + + group.notify(queue: DispatchQueue.main) { + + articlesZone.deleteArticles(deletedArticles) { _ in + refreshProgress.completeTask() + articlesZone.sendNewArticles(newArticles) { _ in + refreshProgress.completeTask() + completion() + } + } + + } + + } + +}