Simplifying operations to improve their reusability and composability

This commit is contained in:
Kiel Gillard
2019-10-10 09:15:22 +11:00
parent 3b722021f9
commit 7cac3e6238
14 changed files with 214 additions and 184 deletions

View File

@@ -86,10 +86,9 @@
9E1D154D233370D800F4944C /* FeedlySyncStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D154C233370D800F4944C /* FeedlySyncStrategy.swift */; };
9E1D154F233371DD00F4944C /* FeedlyGetCollectionsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D154E233371DD00F4944C /* FeedlyGetCollectionsOperation.swift */; };
9E1D15512334282100F4944C /* FeedlyMirrorCollectionsAsFoldersOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D15502334282100F4944C /* FeedlyMirrorCollectionsAsFoldersOperation.swift */; };
9E1D15532334304B00F4944C /* FeedlyGetCollectionStreamOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D15522334304B00F4944C /* FeedlyGetCollectionStreamOperation.swift */; };
9E1D15532334304B00F4944C /* FeedlyGetStreamOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D15522334304B00F4944C /* FeedlyGetStreamOperation.swift */; };
9E1D1555233431A600F4944C /* FeedlyOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D1554233431A600F4944C /* FeedlyOperation.swift */; };
9E1D15572334355900F4944C /* FeedlyRequestStreamsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D15562334355900F4944C /* FeedlyRequestStreamsOperation.swift */; };
9E1D155923343B2A00F4944C /* FeedlyGetStreamParsedItemsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D155823343B2A00F4944C /* FeedlyGetStreamParsedItemsOperation.swift */; };
9E1D155B2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D155A2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift */; };
9E1D155D233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D155C233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift */; };
9E713653233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E713652233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift */; };
@@ -116,6 +115,7 @@
9EC688EC232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */; };
9EC688EE232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */; };
9ECC9A85234DC16E009B5144 /* FeedlyAccountDelegateError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */; };
9EF35F7A234E830E003AE2AE /* FeedlyCompoundOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -259,10 +259,9 @@
9E1D154C233370D800F4944C /* FeedlySyncStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlySyncStrategy.swift; sourceTree = "<group>"; };
9E1D154E233371DD00F4944C /* FeedlyGetCollectionsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetCollectionsOperation.swift; sourceTree = "<group>"; };
9E1D15502334282100F4944C /* FeedlyMirrorCollectionsAsFoldersOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyMirrorCollectionsAsFoldersOperation.swift; sourceTree = "<group>"; };
9E1D15522334304B00F4944C /* FeedlyGetCollectionStreamOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetCollectionStreamOperation.swift; sourceTree = "<group>"; };
9E1D15522334304B00F4944C /* FeedlyGetStreamOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetStreamOperation.swift; sourceTree = "<group>"; };
9E1D1554233431A600F4944C /* FeedlyOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyOperation.swift; sourceTree = "<group>"; };
9E1D15562334355900F4944C /* FeedlyRequestStreamsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRequestStreamsOperation.swift; sourceTree = "<group>"; };
9E1D155823343B2A00F4944C /* FeedlyGetStreamParsedItemsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetStreamParsedItemsOperation.swift; sourceTree = "<group>"; };
9E1D155A2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyOrganiseParsedItemsByFeedOperation.swift; sourceTree = "<group>"; };
9E1D155C233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyUpdateAccountFeedsWithItemsOperation.swift; sourceTree = "<group>"; };
9E713652233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRefreshStreamEntriesStatusOperation.swift; sourceTree = "<group>"; };
@@ -289,6 +288,7 @@
9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FeedlyAccountDelegate+OAuth.swift"; sourceTree = "<group>"; };
9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthAuthorizationCodeGranting.swift; sourceTree = "<group>"; };
9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyAccountDelegateError.swift; sourceTree = "<group>"; };
9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCompoundOperation.swift; sourceTree = "<group>"; };
D511EEB5202422BB00712EC3 /* Account_project_debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_debug.xcconfig; sourceTree = "<group>"; };
D511EEB6202422BB00712EC3 /* Account_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_target.xcconfig; sourceTree = "<group>"; };
D511EEB7202422BB00712EC3 /* Account_project_release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_release.xcconfig; sourceTree = "<group>"; };
@@ -547,6 +547,7 @@
9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */,
9EC688E9232B973C00A8D0A2 /* FeedlyAPICaller.swift */,
9E1D1554233431A600F4944C /* FeedlyOperation.swift */,
9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */,
9EBC31B6233987C1002A567B /* FeedlyArticleStatusCoordinator.swift */,
9EBC31B32338AC2E002A567B /* Models */,
9EBC31B22338AC0F002A567B /* Refresh */,
@@ -562,8 +563,7 @@
9E1D15502334282100F4944C /* FeedlyMirrorCollectionsAsFoldersOperation.swift */,
9E12B01F2334696A00ADE5A0 /* FeedlyCreateFeedsForCollectionFoldersOperation.swift */,
9E1D15562334355900F4944C /* FeedlyRequestStreamsOperation.swift */,
9E1D15522334304B00F4944C /* FeedlyGetCollectionStreamOperation.swift */,
9E1D155823343B2A00F4944C /* FeedlyGetStreamParsedItemsOperation.swift */,
9E1D15522334304B00F4944C /* FeedlyGetStreamOperation.swift */,
9E1D155A2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift */,
9E1D155C233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift */,
9E713652233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift */,
@@ -779,6 +779,7 @@
buildActionMask = 2147483647;
files = (
84C8B3F41F89DE430053CCA6 /* DataExtensions.swift in Sources */,
9EF35F7A234E830E003AE2AE /* FeedlyCompoundOperation.swift in Sources */,
552032F9229D5D5A009559E0 /* ReaderAPISubscription.swift in Sources */,
84C3654A1F899F3B001EC85C /* CombinedRefreshProgress.swift in Sources */,
9EC688EE232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift in Sources */,
@@ -789,9 +790,8 @@
9ECC9A85234DC16E009B5144 /* FeedlyAccountDelegateError.swift in Sources */,
9EA3133B231E368100268BA0 /* FeedlyAccountDelegate.swift in Sources */,
51E5959B228C781500FCC42B /* FeedbinStarredEntry.swift in Sources */,
9E1D155923343B2A00F4944C /* FeedlyGetStreamParsedItemsOperation.swift in Sources */,
846E77451F6EF9B900A165E2 /* Container.swift in Sources */,
9E1D15532334304B00F4944C /* FeedlyGetCollectionStreamOperation.swift in Sources */,
9E1D15532334304B00F4944C /* FeedlyGetStreamOperation.swift in Sources */,
9E12B0202334696A00ADE5A0 /* FeedlyCreateFeedsForCollectionFoldersOperation.swift in Sources */,
552032FD229D5D5A009559E0 /* ReaderAPITagging.swift in Sources */,
9EAEC62A23331EE70085D7C9 /* FeedlyOrigin.swift in Sources */,

View File

@@ -41,13 +41,9 @@ final class FeedlyArticleStatusCoordinator {
}
/// Ensures local articles have the same status as they do remotely.
func refreshArticleStatus(for account: Account, stream: FeedlyStream, collection: FeedlyCollection, completion: @escaping (() -> Void)) {
func refreshArticleStatus(for account: Account, entries: [FeedlyEntry], completion: @escaping (() -> Void)) {
let unreadArticleIds = Set(
stream.items
.filter { $0.unread }
.map { $0.id }
)
let unreadArticleIds = Set(entries.filter { $0.unread }.map { $0.id })
// Mark articles as unread
let currentUnreadArticleIDs = account.fetchUnreadArticleIDs()
@@ -55,17 +51,13 @@ final class FeedlyArticleStatusCoordinator {
let markUnreadArticles = account.fetchArticles(.articleIDs(deltaUnreadArticleIDs))
account.update(markUnreadArticles, statusKey: .read, flag: false)
let readAritcleIds = Set(
stream.items
.filter { !$0.unread }
.map { $0.id }
)
let readAritcleIds = Set(entries.filter { !$0.unread }.map { $0.id })
let deltaReadArticleIDs = currentUnreadArticleIDs.intersection(readAritcleIds)
let markReadArticles = account.fetchArticles(.articleIDs(deltaReadArticleIDs))
account.update(markReadArticles, statusKey: .read, flag: true)
os_log(.debug, log: log, "\"%@\" - updated %i UNREAD and %i read article(s).", collection.label, unreadArticleIds.count, markReadArticles.count)
// os_log(.debug, log: log, "\"%@\" - updated %i UNREAD and %i read article(s).", collection.label, unreadArticleIds.count, markReadArticles.count)
completion()

View File

@@ -0,0 +1,36 @@
//
// FeedlyCompoundOperation.swift
// Account
//
// Created by Kiel Gillard on 10/10/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import Foundation
final class FeedlyCompoundOperation: FeedlyOperation {
private let operationQueue = OperationQueue()
private let operations: [Operation]
init(operations: [Operation]) {
assert(!operations.isEmpty)
self.operations = operations
}
convenience init(operationsBlock: () -> ([Operation])) {
let operations = operationsBlock()
self.init(operations: operations)
}
override func main() {
let finishOperation = BlockOperation { [weak self] in
self?.didFinish()
}
for operation in operations {
finishOperation.addDependency(operation)
}
operationQueue.addOperations(operations, waitUntilFinished: false)
}
}

View File

@@ -49,3 +49,13 @@ struct FeedlyCategoryResourceId: FeedlyResourceId {
return FeedlyCategoryResourceId(id: id)
}
}
struct FeedlyTagResourceId: FeedlyResourceId {
var id: String
static func saved(for userId: String) -> FeedlyTagResourceId {
// https://developer.feedly.com/cloud/#global-resource-ids
let id = "user/\(userId)/tag/global.saved"
return FeedlyTagResourceId(id: id)
}
}

View File

@@ -13,11 +13,11 @@ import os.log
final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
let account: Account
let collectionsAndFoldersProvider: FeedlyCollectionsAndFoldersProviding
let feedsAndFoldersProvider: FeedlyFeedsAndFoldersProviding
let log: OSLog
init(account: Account, collectionsAndFoldersProvider: FeedlyCollectionsAndFoldersProviding, log: OSLog) {
self.collectionsAndFoldersProvider = collectionsAndFoldersProvider
init(account: Account, feedsAndFoldersProvider: FeedlyFeedsAndFoldersProviding, log: OSLog) {
self.feedsAndFoldersProvider = feedsAndFoldersProvider
self.account = account
self.log = log
}
@@ -29,23 +29,22 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
var localFeeds = account.flattenedFeeds()
let feedsBefore = localFeeds
let pairs = collectionsAndFoldersProvider.collectionsAndFolders
let pairs = feedsAndFoldersProvider.feedsAndFolders
// Remove feeds in a folder which are not in the corresponding collection.
for (collection, folder) in pairs {
for (collectionFeeds, folder) in pairs {
let feedsInFolder = folder.topLevelFeeds
let feedsInCollection = Set(collection.feeds.map { $0.id })
let feedsInCollection = Set(collectionFeeds.map { $0.id })
let feedsToRemove = feedsInFolder.filter { !feedsInCollection.contains($0.feedID) }
if !feedsToRemove.isEmpty {
folder.removeFeeds(feedsToRemove)
os_log(.debug, log: log, "\"%@\" - removed: %@", collection.label, feedsToRemove.map { $0.feedID }, feedsInCollection)
// os_log(.debug, log: log, "\"%@\" - removed: %@", collection.label, feedsToRemove.map { $0.feedID }, feedsInCollection)
}
}
// Pair each Feed with its Folder.
let feedsAndFolders = pairs
.compactMap { ($0.0.feeds, $0.1) }
.map({ (collectionFeeds, folder) -> [(FeedlyFeed, Folder)] in
return collectionFeeds.map { feed -> (FeedlyFeed, Folder) in
return (feed, folder) // pairs a folder for every feed in parallel

View File

@@ -1,60 +0,0 @@
//
// FeedlyGetCollectionStreamOperation.swift
// Account
//
// Created by Kiel Gillard on 20/9/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import Foundation
protocol FeedlyCollectionStreamProviding: class {
var collection: FeedlyCollection { get }
var stream: FeedlyStream { get }
}
/// Single responsibility is to get the stream content of a Collection from Feedly.
final class FeedlyGetCollectionStreamOperation: FeedlyOperation, FeedlyCollectionStreamProviding {
private(set) var collection: FeedlyCollection
var stream: FeedlyStream {
guard let stream = storedStream else {
// TODO: this is probably more error prone than it seems!
fatalError("\(type(of: self)) has been told to finish too early or a dependency is ignoring cancellation.")
}
return stream
}
private var storedStream: FeedlyStream?
let account: Account
let caller: FeedlyAPICaller
let unreadOnly: Bool?
let newerThan: Date?
init(account: Account, collection: FeedlyCollection, caller: FeedlyAPICaller, newerThan: Date?, unreadOnly: Bool? = nil) {
self.account = account
self.collection = collection
self.caller = caller
self.unreadOnly = unreadOnly
self.newerThan = newerThan
}
override func main() {
guard !isCancelled else {
didFinish()
return
}
caller.getStream(for: collection, newerThan: newerThan, unreadOnly: unreadOnly) { result in
switch result {
case .success(let stream):
self.storedStream = stream
self.didFinish()
case .failure(let error):
self.didFinish(error)
}
}
}
}

View File

@@ -0,0 +1,80 @@
//
// FeedlyGetStreamOperation.swift
// Account
//
// Created by Kiel Gillard on 20/9/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import RSParser
protocol FeedlyEntryProviding: class {
var resource: FeedlyResourceId { get }
var entries: [FeedlyEntry] { get }
var parsedEntries: Set<ParsedItem> { get }
}
/// Single responsibility is to get the stream content of a Collection from Feedly.
final class FeedlyGetStreamOperation: FeedlyOperation, FeedlyEntryProviding {
private(set) var resource: FeedlyResourceId
var entries: [FeedlyEntry] {
guard let entries = storedStream?.items else {
assertionFailure("Has a prior operation finished too early? Is the operation included in \(self.dependencies)?")
return []
}
return entries
}
var parsedEntries: Set<ParsedItem> {
if let entries = storedParsedEntries {
return entries
}
let parsed = Set(entries.map { FeedlyEntryParser(entry: $0).parsedItemRepresentation })
storedParsedEntries = parsed
return parsed
}
private var storedStream: FeedlyStream? {
didSet {
storedParsedEntries = nil
}
}
private var storedParsedEntries: Set<ParsedItem>?
let account: Account
let caller: FeedlyAPICaller
let unreadOnly: Bool?
let newerThan: Date?
init(account: Account, resource: FeedlyResourceId, caller: FeedlyAPICaller, newerThan: Date?, unreadOnly: Bool? = nil) {
self.account = account
self.resource = resource
self.caller = caller
self.unreadOnly = unreadOnly
self.newerThan = newerThan
}
override func main() {
guard !isCancelled else {
didFinish()
return
}
caller.getStream(for: resource, newerThan: newerThan, unreadOnly: unreadOnly) { result in
switch result {
case .success(let stream):
self.storedStream = stream
self.didFinish()
case .failure(let error):
self.didFinish(error)
}
}
}
}

View File

@@ -1,52 +0,0 @@
//
// FeedlyGetStreamParsedItemsOperation.swift
// Account
//
// Created by Kiel Gillard on 20/9/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import RSParser
import os.log
protocol FeedlyStreamParsedItemsProviding: class {
var collection: FeedlyCollection { get }
var stream: FeedlyStream { get }
var parsedItems: [ParsedItem] { get }
}
/// Single responsibility is to model articles as ParsedItems for entries in a Collection's stream from Feedly.
final class FeedlyGetStreamParsedItemsOperation: FeedlyOperation, FeedlyStreamParsedItemsProviding {
private let account: Account
private let caller: FeedlyAPICaller
private let collectionStreamProvider: FeedlyCollectionStreamProviding
private let log: OSLog
var collection: FeedlyCollection {
return collectionStreamProvider.collection
}
var stream: FeedlyStream {
return collectionStreamProvider.stream
}
private(set) var parsedItems = [ParsedItem]()
init(account: Account, collectionStreamProvider: FeedlyCollectionStreamProviding, caller: FeedlyAPICaller, log: OSLog) {
self.account = account
self.caller = caller
self.collectionStreamProvider = collectionStreamProvider
self.log = log
}
override func main() {
defer { didFinish() }
guard !isCancelled else { return }
parsedItems = stream.items.map { FeedlyEntryParser(entry: $0).parsedItemRepresentation }
os_log(.debug, log: log, "Parsed %i items of %i entries for %@", parsedItems.count, stream.items.count, collection.label)
}
}

View File

@@ -13,8 +13,12 @@ protocol FeedlyCollectionsAndFoldersProviding: class {
var collectionsAndFolders: [(FeedlyCollection, Folder)] { get }
}
protocol FeedlyFeedsAndFoldersProviding {
var feedsAndFolders: [([FeedlyFeed], Folder)] { get }
}
/// Single responsibility is accurately reflect Collections from Feedly as Folders.
final class FeedlyMirrorCollectionsAsFoldersOperation: FeedlyOperation, FeedlyCollectionsAndFoldersProviding {
final class FeedlyMirrorCollectionsAsFoldersOperation: FeedlyOperation, FeedlyCollectionsAndFoldersProviding, FeedlyFeedsAndFoldersProviding {
let caller: FeedlyAPICaller
let account: Account
@@ -22,6 +26,7 @@ final class FeedlyMirrorCollectionsAsFoldersOperation: FeedlyOperation, FeedlyCo
let log: OSLog
private(set) var collectionsAndFolders = [(FeedlyCollection, Folder)]()
private(set) var feedsAndFolders = [([FeedlyFeed], Folder)]()
init(account: Account, collectionsProvider: FeedlyCollectionProviding, caller: FeedlyAPICaller, log: OSLog) {
self.collectionsProvider = collectionsProvider
@@ -50,6 +55,10 @@ final class FeedlyMirrorCollectionsAsFoldersOperation: FeedlyOperation, FeedlyCo
collectionsAndFolders = pairs
os_log(.debug, log: log, "Ensured %i folders for %i collections.", pairs.count, collections.count)
feedsAndFolders = pairs.map { (collection, folder) -> (([FeedlyFeed], Folder)) in
return (collection.feeds, folder)
}
// Remove folders without a corresponding collection
let collectionFolders = Set(pairs.map { $0.1 })
let foldersWithoutCollections = localFolders.subtracting(collectionFolders)

View File

@@ -11,8 +11,6 @@ import RSParser
import os.log
protocol FeedlyParsedItemsByFeedProviding {
var collection: FeedlyCollection { get }
var stream: FeedlyStream { get }
var allFeeds: Set<Feed> { get }
func parsedItems(for feed: Feed) -> Set<ParsedItem>?
}
@@ -20,7 +18,7 @@ protocol FeedlyParsedItemsByFeedProviding {
/// Single responsibility is to group articles by their feeds.
final class FeedlyOrganiseParsedItemsByFeedOperation: FeedlyOperation, FeedlyParsedItemsByFeedProviding {
private let account: Account
private let parsedItemsProvider: FeedlyStreamParsedItemsProviding
private let entryProvider: FeedlyEntryProviding
private let log: OSLog
var allFeeds: Set<Feed> {
@@ -32,19 +30,11 @@ final class FeedlyOrganiseParsedItemsByFeedOperation: FeedlyOperation, FeedlyPar
return itemsKeyedByFeedId[feed.feedID]
}
var collection: FeedlyCollection {
return parsedItemsProvider.collection
}
var stream: FeedlyStream {
return parsedItemsProvider.stream
}
private var itemsKeyedByFeedId = [String: Set<ParsedItem>]()
init(account: Account, parsedItemsProvider: FeedlyStreamParsedItemsProviding, log: OSLog) {
init(account: Account, entryProvider: FeedlyEntryProviding, log: OSLog) {
self.account = account
self.parsedItemsProvider = parsedItemsProvider
self.entryProvider = entryProvider
self.log = log
}
@@ -53,7 +43,7 @@ final class FeedlyOrganiseParsedItemsByFeedOperation: FeedlyOperation, FeedlyPar
guard !isCancelled else { return }
let items = parsedItemsProvider.parsedItems
let items = entryProvider.parsedEntries
var dict = [String: Set<ParsedItem>](minimumCapacity: items.count)
for item in items {
@@ -71,7 +61,7 @@ final class FeedlyOrganiseParsedItemsByFeedOperation: FeedlyOperation, FeedlyPar
guard !isCancelled else { return }
}
os_log(.debug, log: log, "Grouped %i items by %i feeds for %@", items.count, dict.count, parsedItemsProvider.collection.label)
// os_log(.debug, log: log, "Grouped %i items by %i feeds for %@", items.count, dict.count, parsedItemsProvider.collection.label)
itemsKeyedByFeedId = dict
}

View File

@@ -12,14 +12,14 @@ import os.log
/// Single responsibility is to update the read status of articles stored locally with the unread status of the entries in a Collection's stream from Feedly.
final class FeedlyRefreshStreamEntriesStatusOperation: FeedlyOperation {
private let account: Account
private let collectionStreamProvider: FeedlyCollectionStreamProviding
private let entryProvider: FeedlyEntryProviding
private let log: OSLog
let articleStatusCoordinator: FeedlyArticleStatusCoordinator
init(account: Account, collectionStreamProvider: FeedlyCollectionStreamProviding, articleStatusCoordinator: FeedlyArticleStatusCoordinator, log: OSLog) {
init(account: Account, entryProvider: FeedlyEntryProviding, articleStatusCoordinator: FeedlyArticleStatusCoordinator, log: OSLog) {
self.account = account
self.articleStatusCoordinator = articleStatusCoordinator
self.collectionStreamProvider = collectionStreamProvider
self.entryProvider = entryProvider
self.log = log
}
@@ -29,9 +29,7 @@ final class FeedlyRefreshStreamEntriesStatusOperation: FeedlyOperation {
return
}
let collection = collectionStreamProvider.collection
let stream = collectionStreamProvider.stream
articleStatusCoordinator.refreshArticleStatus(for: account, stream: stream, collection: collection) {
articleStatusCoordinator.refreshArticleStatus(for: account, entries: entryProvider.entries) {
self.didFinish()
}
}

View File

@@ -10,7 +10,7 @@ import Foundation
import os.log
protocol FeedlyRequestStreamsOperationDelegate: class {
func feedlyRequestStreamsOperation(_ operation: FeedlyRequestStreamsOperation, enqueue collectionStreamOperation: FeedlyGetCollectionStreamOperation)
func feedlyRequestStreamsOperation(_ operation: FeedlyRequestStreamsOperation, enqueue collectionStreamOperation: FeedlyGetStreamOperation)
}
/// Single responsibility is to create one stream request operation for one Feedly collection.
@@ -45,8 +45,9 @@ final class FeedlyRequestStreamsOperation: FeedlyOperation {
// TODO: Prioritise the must read collection/category before others so the most important content for the user loads first.
for collection in collectionsProvider.collections {
let operation = FeedlyGetCollectionStreamOperation(account: account,
collection: collection,
let resource = FeedlyCategoryResourceId(id: collection.id)
let operation = FeedlyGetStreamOperation(account: account,
resource: resource,
caller: caller,
newerThan: newerThan,
unreadOnly: unreadOnly)

View File

@@ -62,7 +62,7 @@ final class FeedlySyncStrategy {
// Ensure feeds are created and grouped by their folders.
let createFeedsOperation = FeedlyCreateFeedsForCollectionFoldersOperation(account: account,
collectionsAndFoldersProvider: mirrorCollectionsAsFolders,
feedsAndFoldersProvider: mirrorCollectionsAsFolders,
log: log)
createFeedsOperation.delegate = self
createFeedsOperation.addDependency(mirrorCollectionsAsFolders)
@@ -79,6 +79,41 @@ final class FeedlySyncStrategy {
getCollectionStreams.queueDelegate = self
getCollectionStreams.addDependency(getCollections)
// if let user = caller.credentials?.username {
//
// let syncSaved = FeedlyCompoundOperation {
//
// let saved = FeedlyTagResourceId.saved(for: user)
// let getSavedStream = FeedlyGetStreamOperation(account: account,
// resource: saved,
// caller: caller,
// newerThan: newerThan)
// getSavedStream.delegate = self
//
// getSavedStream.addDependency(getCollections)
// getSavedStream.addDependency(mirrorCollectionsAsFolders)
// getSavedStream.addDependency(createFeedsOperation)
//
// let organiseByFeed = FeedlyOrganiseParsedItemsByFeedOperation(account: account,
// streamProvider: getSavedStream,
// log: log)
// organiseByFeed.delegate = self
// organiseByFeed.addDependency(getSavedStream)
//
// let updateAccount = FeedlyUpdateAccountFeedsWithItemsOperation(account: account,
// organisedItemsProvider: organiseByFeed,
// log: log)
// updateAccount.delegate = self
// updateAccount.addDependency(organiseByFeed)
//
// // refresh stream entries status
//
// return [getSavedStream, organiseByFeed]
// }
//
// operationQueue.addOperation(syncSaved)
// }
// Last operation to perform, which should be dependent on any other operation added to the queue.
let syncId = UUID().uuidString
let lastArticleFetchDate = Date()
@@ -117,26 +152,18 @@ final class FeedlySyncStrategy {
extension FeedlySyncStrategy: FeedlyRequestStreamsOperationDelegate {
func feedlyRequestStreamsOperation(_ operation: FeedlyRequestStreamsOperation, enqueue collectionStreamOperation: FeedlyGetCollectionStreamOperation) {
func feedlyRequestStreamsOperation(_ operation: FeedlyRequestStreamsOperation, enqueue streamOperation: FeedlyGetStreamOperation) {
collectionStreamOperation.delegate = self
streamOperation.delegate = self
os_log(.debug, log: log, "Requesting stream for collection \"%@\"", collectionStreamOperation.collection.label)
// Parse the contents of this collection's stream.
let parseItemsOperation = FeedlyGetStreamParsedItemsOperation(account: account,
collectionStreamProvider: collectionStreamOperation,
caller: caller,
log: log)
parseItemsOperation.delegate = self
parseItemsOperation.addDependency(collectionStreamOperation)
// os_log(.debug, log: log, "Requesting stream for collection \"%@\"", streamOperation.collection.label)
// Group the stream's content by feed.
let groupItemsByFeed = FeedlyOrganiseParsedItemsByFeedOperation(account: account,
parsedItemsProvider: parseItemsOperation,
entryProvider: streamOperation,
log: log)
groupItemsByFeed.delegate = self
groupItemsByFeed.addDependency(parseItemsOperation)
groupItemsByFeed.addDependency(streamOperation)
// Update the account with the articles for the feeds in the stream.
let updateOperation = FeedlyUpdateAccountFeedsWithItemsOperation(account: account,
@@ -147,7 +174,7 @@ extension FeedlySyncStrategy: FeedlyRequestStreamsOperationDelegate {
// Once the articles are in the account, ensure they have the correct status
let ensureUnreadOperation = FeedlyRefreshStreamEntriesStatusOperation(account: account,
collectionStreamProvider: collectionStreamOperation,
entryProvider: streamOperation,
articleStatusCoordinator: articleStatusCoordinator,
log: log)
@@ -159,7 +186,7 @@ extension FeedlySyncStrategy: FeedlyRequestStreamsOperationDelegate {
operation.addDependency(ensureUnreadOperation)
}
let operations = [collectionStreamOperation, parseItemsOperation, groupItemsByFeed, updateOperation, ensureUnreadOperation]
let operations = [streamOperation, groupItemsByFeed, updateOperation, ensureUnreadOperation]
operationQueue.addOperations(operations, waitUntilFinished: false)
}

View File

@@ -31,14 +31,14 @@ final class FeedlyUpdateAccountFeedsWithItemsOperation: FeedlyOperation {
let group = DispatchGroup()
let allFeeds = organisedItemsProvider.allFeeds
os_log(.debug, log: log, "Begin updating %i feeds in collection \"%@\"", allFeeds.count, organisedItemsProvider.collection.label)
// os_log(.debug, log: log, "Begin updating %i feeds in collection \"%@\"", allFeeds.count, organisedItemsProvider.collection.label)
for feed in allFeeds {
guard let items = organisedItemsProvider.parsedItems(for: feed) else {
continue
}
group.enter()
os_log(.debug, log: log, "Updating %i items for feed \"%@\" in collection \"%@\"", items.count, feed.nameForDisplay, organisedItemsProvider.collection.label)
// os_log(.debug, log: log, "Updating %i items for feed \"%@\" in collection \"%@\"", items.count, feed.nameForDisplay, organisedItemsProvider.collection.label)
account.update(feed, parsedItems: items, defaultRead: true) {
group.leave()
@@ -46,7 +46,7 @@ final class FeedlyUpdateAccountFeedsWithItemsOperation: FeedlyOperation {
}
group.notify(qos: .userInitiated, queue: .main) {
os_log(.debug, log: self.log, "Finished updating feeds in collection \"%@\"", self.organisedItemsProvider.collection.label)
// os_log(.debug, log: self.log, "Finished updating feeds in collection \"%@\"", self.organisedItemsProvider.collection.label)
self.didFinish()
}
}