From d1e4c84a9e73a17d1b09995c444545455b7b5c23 Mon Sep 17 00:00:00 2001 From: Kiel Gillard Date: Thu, 10 Oct 2019 21:24:45 +1100 Subject: [PATCH] Refactoring create feed --- .../Account/Account.xcodeproj/project.pbxproj | 12 +++ .../Feedly/FeedlyAccountDelegate.swift | 76 +++----------- .../Feedly/FeedlyCompoundOperation.swift | 6 +- .../Feedly/FeedlyCreateFeedRequest.swift | 99 +++++++++++++++++++ .../Feedly/FeedlyFeedContainerValidator.swift | 36 +++++++ .../FeedlySubscribeToFeedOperation.swift | 69 +++++++++++++ ...teFeedsForCollectionFoldersOperation.swift | 19 ++-- .../Refresh/FeedlyGetStreamOperation.swift | 18 +++- 8 files changed, 260 insertions(+), 75 deletions(-) create mode 100644 Frameworks/Account/Feedly/FeedlyCreateFeedRequest.swift create mode 100644 Frameworks/Account/Feedly/FeedlyFeedContainerValidator.swift create mode 100644 Frameworks/Account/Feedly/FeedlySubscribeToFeedOperation.swift diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index 5c82a7f64..1a0c7387f 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -91,6 +91,7 @@ 9E1D15572334355900F4944C /* FeedlyRequestStreamsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D15562334355900F4944C /* FeedlyRequestStreamsOperation.swift */; }; 9E1D155B2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D155A2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift */; }; 9E1D155D233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D155C233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift */; }; + 9E510D6E234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E510D6D234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift */; }; 9E713653233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E713652233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift */; }; 9E7F15072341E96700F860D1 /* AccountFeedlySyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7F15062341E96700F860D1 /* AccountFeedlySyncTest.swift */; }; 9E7F150A2341EF5A00F860D1 /* feedly_collections_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 9E7F15092341EF5A00F860D1 /* feedly_collections_initial.json */; }; @@ -115,7 +116,9 @@ 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 */; }; + 9EE4CCFA234F106600FBAE4B /* FeedlyFeedContainerValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EE4CCF9234F106600FBAE4B /* FeedlyFeedContainerValidator.swift */; }; 9EF35F7A234E830E003AE2AE /* FeedlyCompoundOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */; }; + 9EF35F7C234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF35F7B234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -264,6 +267,7 @@ 9E1D15562334355900F4944C /* FeedlyRequestStreamsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRequestStreamsOperation.swift; sourceTree = ""; }; 9E1D155A2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyOrganiseParsedItemsByFeedOperation.swift; sourceTree = ""; }; 9E1D155C233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyUpdateAccountFeedsWithItemsOperation.swift; sourceTree = ""; }; + 9E510D6D234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCreateFeedRequest.swift; sourceTree = ""; }; 9E713652233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRefreshStreamEntriesStatusOperation.swift; sourceTree = ""; }; 9E7F15062341E96700F860D1 /* AccountFeedlySyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFeedlySyncTest.swift; sourceTree = ""; }; 9E7F15092341EF5A00F860D1 /* feedly_collections_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = feedly_collections_initial.json; sourceTree = ""; }; @@ -288,7 +292,9 @@ 9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FeedlyAccountDelegate+OAuth.swift"; sourceTree = ""; }; 9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthAuthorizationCodeGranting.swift; sourceTree = ""; }; 9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyAccountDelegateError.swift; sourceTree = ""; }; + 9EE4CCF9234F106600FBAE4B /* FeedlyFeedContainerValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFeedContainerValidator.swift; sourceTree = ""; }; 9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCompoundOperation.swift; sourceTree = ""; }; + 9EF35F7B234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlySubscribeToFeedOperation.swift; sourceTree = ""; }; D511EEB5202422BB00712EC3 /* Account_project_debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_debug.xcconfig; sourceTree = ""; }; D511EEB6202422BB00712EC3 /* Account_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_target.xcconfig; sourceTree = ""; }; D511EEB7202422BB00712EC3 /* Account_project_release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_release.xcconfig; sourceTree = ""; }; @@ -542,12 +548,15 @@ isa = PBXGroup; children = ( 9EA3133A231E368100268BA0 /* FeedlyAccountDelegate.swift */, + 9EE4CCF9234F106600FBAE4B /* FeedlyFeedContainerValidator.swift */, 9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */, 9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */, 9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */, 9EC688E9232B973C00A8D0A2 /* FeedlyAPICaller.swift */, + 9E510D6D234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift */, 9E1D1554233431A600F4944C /* FeedlyOperation.swift */, 9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */, + 9EF35F7B234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift */, 9EBC31B6233987C1002A567B /* FeedlyArticleStatusCoordinator.swift */, 9EBC31B32338AC2E002A567B /* Models */, 9EBC31B22338AC0F002A567B /* Refresh */, @@ -812,9 +821,11 @@ 515E4EB62324FF8C0057B0E7 /* URLRequest+RSWeb.swift in Sources */, 5154367B228EEB28005E1CDF /* FeedbinImportResult.swift in Sources */, 84B2D4D02238CD8A00498ADA /* FeedMetadata.swift in Sources */, + 9EF35F7C234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift in Sources */, 9EAEC624233315F60085D7C9 /* FeedlyEntry.swift in Sources */, 5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */, 84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */, + 9E510D6E234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift in Sources */, 5133231122810EB200C30F19 /* FeedbinIcon.swift in Sources */, 846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */, 55203300229D5D5A009559E0 /* ReaderAPICaller.swift in Sources */, @@ -833,6 +844,7 @@ 5170743C232AEDB500A461A3 /* OPMLFile.swift in Sources */, 51BB7B84233531BC008E8144 /* AccountBehaviors.swift in Sources */, 9E1773D923458D590056A5A8 /* FeedlyResourceId.swift in Sources */, + 9EE4CCFA234F106600FBAE4B /* FeedlyFeedContainerValidator.swift in Sources */, 552032FC229D5D5A009559E0 /* ReaderAPIUnreadEntry.swift in Sources */, 51FE1008234635A20056195D /* DeepLinkProvider.swift in Sources */, 9EC688EA232B973C00A8D0A2 /* FeedlyAPICaller.swift in Sources */, diff --git a/Frameworks/Account/Feedly/FeedlyAccountDelegate.swift b/Frameworks/Account/Feedly/FeedlyAccountDelegate.swift index 591155b60..359f6b551 100644 --- a/Frameworks/Account/Feedly/FeedlyAccountDelegate.swift +++ b/Frameworks/Account/Feedly/FeedlyAccountDelegate.swift @@ -238,75 +238,21 @@ final class FeedlyAccountDelegate: AccountDelegate { return (folder, collectionId) } + var createFeedRequest: FeedlyCreateFeedRequest? + func createFeed(for account: Account, url: String, name: String?, container: Container, completion: @escaping (Result) -> Void) { - let (folder, collectionId): (Folder, String) - do { - (folder, collectionId) = try isValidContainer(for: account, container: container) - } catch { - return DispatchQueue.main.async { - completion(.failure(error)) - } - } - - let resourceId = FeedlyFeedResourceId(url: url) - + let progress = refreshProgress progress.addToNumberOfTasksAndRemaining(1) - caller.addFeed(with: resourceId, title: name, toCollectionWith: collectionId) { [weak self] result in - defer { progress.completeTask() } - - switch result { - case .success(let feedlyFeeds): - let feedsBefore = folder.flattenedFeeds() - for feedlyFeed in feedlyFeeds where !account.hasFeed(with: feedlyFeed.feedId) { - let resourceId = FeedlyFeedResourceId(id: feedlyFeed.id) - let feed = account.createFeed(with: feedlyFeed.title, - url: resourceId.url, - feedID: feedlyFeed.id, - homePageURL: feedlyFeed.website) - folder.addFeed(feed) - } - - let feedsAfter = folder.flattenedFeeds() - let added = feedsAfter.subtracting(feedsBefore) - - guard let first = added.first else { - return completion(.failure(AccountError.createErrorNotFound)) - } - - let group = DispatchGroup() - progress.addToNumberOfTasksAndRemaining(1) - - if let self = self { - for feed in added { - group.enter() - let resourceId = FeedlyFeedResourceId(id: feed.feedID) - self.caller.getStream(for: resourceId, newerThan: nil, unreadOnly: nil) { result in - switch result { - case .success(let stream): - let items = Set(stream.items.map { FeedlyEntryParser(entry: $0).parsedItemRepresentation }) - - account.update(feed, parsedItems: items, defaultRead: false) { - group.leave() - } - - case .failure: - // Feed will remain empty until new articles appear. - group.leave() - } - } - } - } - - group.notify(queue: .main) { - progress.completeTask() - completion(.success(first)) - } - - case .failure(let error): - completion(.failure(error)) - } + let createFeedRequest = FeedlyCreateFeedRequest(account: account, caller: caller, container: container, log: log) + + self.createFeedRequest = createFeedRequest + + createFeedRequest.start(url: url, name: name) { [weak self] result in + progress.completeTask() + self?.createFeedRequest = nil + completion(result) } } diff --git a/Frameworks/Account/Feedly/FeedlyCompoundOperation.swift b/Frameworks/Account/Feedly/FeedlyCompoundOperation.swift index 093c57ca7..c4dd061f2 100644 --- a/Frameworks/Account/Feedly/FeedlyCompoundOperation.swift +++ b/Frameworks/Account/Feedly/FeedlyCompoundOperation.swift @@ -8,6 +8,7 @@ import Foundation +/// An operation with a queue of its own. final class FeedlyCompoundOperation: FeedlyOperation { private let operationQueue = OperationQueue() private let operations: [Operation] @@ -31,6 +32,9 @@ final class FeedlyCompoundOperation: FeedlyOperation { finishOperation.addDependency(operation) } - operationQueue.addOperations(operations, waitUntilFinished: false) + var operationsWithFinish = operations + operationsWithFinish.append(finishOperation) + + operationQueue.addOperations(operationsWithFinish, waitUntilFinished: false) } } diff --git a/Frameworks/Account/Feedly/FeedlyCreateFeedRequest.swift b/Frameworks/Account/Feedly/FeedlyCreateFeedRequest.swift new file mode 100644 index 000000000..3bc26684a --- /dev/null +++ b/Frameworks/Account/Feedly/FeedlyCreateFeedRequest.swift @@ -0,0 +1,99 @@ +// +// FeedlyCreateFeedRequest.swift +// Account +// +// Created by Kiel Gillard on 10/10/19. +// Copyright © 2019 Ranchero Software, LLC. All rights reserved. +// + +import Foundation +import os.log + +final class FeedlyCreateFeedRequest { + let account: Account + let caller: FeedlyAPICaller + let container: Container + let log: OSLog + + init(account: Account, caller: FeedlyAPICaller, container: Container, log: OSLog) { + self.account = account + self.caller = caller + self.container = container + self.log = log + } + + private class Delegate: FeedlyOperationDelegate { + let resourceProvider: FeedlyResourceProviding + + init(resourceProvider: FeedlyResourceProviding) { + self.resourceProvider = resourceProvider + } + + var completionHandler: ((Result) -> ())? + var error: Error? + + func feedlyOperation(_ operation: FeedlyOperation, didFailWith error: Error) { + self.error = error + } + } + + func start(url: String, name: String?, completion: @escaping (Result) -> Void) { + + let (folder, collectionId): (Folder, String) + do { + let validator = FeedlyFeedContainerValidator(container: container, userId: caller.credentials?.username) + (folder, collectionId) = try validator.getValidContainer() + } catch { + return DispatchQueue.main.async { + completion(.failure(error)) + } + } + + let subscribeRequest = FeedlySubscribeToFeedOperation(account: account, folder: folder, url: url, feedName: name, collectionId: collectionId, caller: caller) + + let delegate = Delegate(resourceProvider: subscribeRequest) + delegate.completionHandler = completion + + let createFeed = FeedlyCompoundOperation() { + let createFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: subscribeRequest, log: log) + let getStream = FeedlyGetStreamOperation(account: account, resourceProvider: subscribeRequest, caller: caller, newerThan: nil) + let organiseByFeed = FeedlyOrganiseParsedItemsByFeedOperation(account: account, entryProvider: getStream, log: log) + let updateAccount = FeedlyUpdateAccountFeedsWithItemsOperation(account: account, organisedItemsProvider: organiseByFeed, log: log) + + createFeeds.addDependency(subscribeRequest) + getStream.addDependency(createFeeds) + organiseByFeed.addDependency(getStream) + updateAccount.addDependency(organiseByFeed) + + let operations = [subscribeRequest, createFeeds, getStream, organiseByFeed, updateAccount] + + for operation in operations { + operation.delegate = delegate + } + + return operations + } + + let callback = BlockOperation() { + guard let handler = delegate.completionHandler else { + return + } + + defer { delegate.completionHandler = nil } + + if let error = delegate.error { + handler(.failure(error)) + + } else if let feed = folder.existingFeed(withFeedID: subscribeRequest.resource.id) { + handler(.success(feed)) + + } else { + handler(.failure(AccountError.createErrorNotFound)) + } + } + + callback.addDependency(createFeed) + + OperationQueue.main.addOperations([createFeed, callback], waitUntilFinished: false) + } +} diff --git a/Frameworks/Account/Feedly/FeedlyFeedContainerValidator.swift b/Frameworks/Account/Feedly/FeedlyFeedContainerValidator.swift new file mode 100644 index 000000000..1e504c8c6 --- /dev/null +++ b/Frameworks/Account/Feedly/FeedlyFeedContainerValidator.swift @@ -0,0 +1,36 @@ +// +// FeedlyFeedContainerValidator.swift +// Account +// +// Created by Kiel Gillard on 10/10/19. +// Copyright © 2019 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +struct FeedlyFeedContainerValidator { + var container: Container + var userId: String? + + func getValidContainer() throws -> (Folder, String) { + guard let folder = container as? Folder else { + throw FeedlyAccountDelegateError.addFeedChooseFolder + } + + guard let collectionId = folder.externalID else { + throw FeedlyAccountDelegateError.addFeedInvalidFolder(folder) + } + + guard let userId = userId else { + throw FeedlyAccountDelegateError.notLoggedIn + } + + let uncategorized = FeedlyCategoryResourceId.uncategorized(for: userId) + + guard collectionId != uncategorized.id else { + throw FeedlyAccountDelegateError.addFeedInvalidFolder(folder) + } + + return (folder, collectionId) + } +} diff --git a/Frameworks/Account/Feedly/FeedlySubscribeToFeedOperation.swift b/Frameworks/Account/Feedly/FeedlySubscribeToFeedOperation.swift new file mode 100644 index 000000000..50744d694 --- /dev/null +++ b/Frameworks/Account/Feedly/FeedlySubscribeToFeedOperation.swift @@ -0,0 +1,69 @@ +// +// FeedlyCreateFeedOperation.swift +// Account +// +// Created by Kiel Gillard on 10/10/19. +// Copyright © 2019 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +protocol FeedlyResourceProviding { + var resource: FeedlyResourceId { get } +} + +final class FeedlySubscribeToFeedOperation: FeedlyOperation, FeedlyFeedsAndFoldersProviding, FeedlyResourceProviding { + let url: String + let feedName: String? + let collectionId: String + let caller: FeedlyAPICaller + let account: Account + let folder: Folder + let feedResource: FeedlyFeedResourceId + + init(account: Account, folder: Folder, url: String, feedName: String?, collectionId: String, caller: FeedlyAPICaller) { + self.account = account + self.folder = folder + self.url = url + self.feedResource = FeedlyFeedResourceId(url: url) + self.feedName = feedName + self.collectionId = collectionId + self.caller = caller + } + + private(set) var feedsAndFolders = [([FeedlyFeed], Folder)]() + + var resource: FeedlyResourceId { + return feedResource + } + + override func main() { + guard !isCancelled else { + return didFinish() + } + + caller.addFeed(with: feedResource, title: feedName, toCollectionWith: collectionId) { [weak self] result in + guard let self = self else { return } + guard !self.isCancelled else { return self.didFinish() } + self.didCompleteRequest(result) + } + } + + private func didCompleteRequest(_ result: Result<[FeedlyFeed], Error>) { + switch result { + case .success(let feedlyFeeds): + feedsAndFolders = [(feedlyFeeds, folder)] + + let feedsWithCreatedFeedId = feedlyFeeds.filter { $0.feedId == resource.id } + + if feedsWithCreatedFeedId.isEmpty { + didFinish(AccountError.createErrorNotFound) + } else { + didFinish() + } + + case .failure(let error): + didFinish(error) + } + } +} diff --git a/Frameworks/Account/Feedly/Refresh/FeedlyCreateFeedsForCollectionFoldersOperation.swift b/Frameworks/Account/Feedly/Refresh/FeedlyCreateFeedsForCollectionFoldersOperation.swift index 69f4926fb..b5d8cba92 100644 --- a/Frameworks/Account/Feedly/Refresh/FeedlyCreateFeedsForCollectionFoldersOperation.swift +++ b/Frameworks/Account/Feedly/Refresh/FeedlyCreateFeedsForCollectionFoldersOperation.swift @@ -27,10 +27,12 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation { guard !isCancelled else { return } - var localFeeds = account.flattenedFeeds() - let feedsBefore = localFeeds let pairs = feedsAndFoldersProvider.feedsAndFolders + let feedsBefore = Set(pairs + .map { $0.1 } + .flatMap { $0.topLevelFeeds }) + // Remove feeds in a folder which are not in the corresponding collection. for (collectionFeeds, folder) in pairs { let feedsInFolder = folder.topLevelFeeds @@ -44,6 +46,8 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation { } // Pair each Feed with its Folder. + var feedsAdded = Set() + let feedsAndFolders = pairs .map({ (collectionFeeds, folder) -> [(FeedlyFeed, Folder)] in return collectionFeeds.map { feed -> (FeedlyFeed, Folder) in @@ -53,9 +57,12 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation { .flatMap { $0 } .compactMap { (collectionFeed, folder) -> (Feed, Folder) in - // find an existing feed - for feed in localFeeds { - if feed.feedID == collectionFeed.id { + // find an existing feed previously added to the account + if let feed = account.existingFeed(withFeedID: collectionFeed.id) { + return (feed, folder) + } else { + // find an existing feed we created below in an earlier value + for feed in feedsAdded where feed.feedID == collectionFeed.id { return (feed, folder) } } @@ -66,7 +73,7 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation { let feed = account.createFeed(with: collectionFeed.title, url: url, feedID: id, homePageURL: collectionFeed.website) // So the same feed isn't created more than once. - localFeeds.insert(feed) + feedsAdded.insert(feed) return (feed, folder) } diff --git a/Frameworks/Account/Feedly/Refresh/FeedlyGetStreamOperation.swift b/Frameworks/Account/Feedly/Refresh/FeedlyGetStreamOperation.swift index 0703562ed..0c2dbb076 100644 --- a/Frameworks/Account/Feedly/Refresh/FeedlyGetStreamOperation.swift +++ b/Frameworks/Account/Feedly/Refresh/FeedlyGetStreamOperation.swift @@ -18,7 +18,15 @@ protocol FeedlyEntryProviding: class { /// Single responsibility is to get the stream content of a Collection from Feedly. final class FeedlyGetStreamOperation: FeedlyOperation, FeedlyEntryProviding { - private(set) var resource: FeedlyResourceId + struct ResourceProvider: FeedlyResourceProviding { + var resource: FeedlyResourceId + } + + let resourceProvider: FeedlyResourceProviding + + var resource: FeedlyResourceId { + return resourceProvider.resource + } var entries: [FeedlyEntry] { guard let entries = storedStream?.items else { @@ -55,19 +63,23 @@ final class FeedlyGetStreamOperation: FeedlyOperation, FeedlyEntryProviding { init(account: Account, resource: FeedlyResourceId, caller: FeedlyAPICaller, newerThan: Date?, unreadOnly: Bool? = nil) { self.account = account - self.resource = resource + self.resourceProvider = ResourceProvider(resource: resource) self.caller = caller self.unreadOnly = unreadOnly self.newerThan = newerThan } + convenience init(account: Account, resourceProvider: FeedlyResourceProviding, caller: FeedlyAPICaller, newerThan: Date?, unreadOnly: Bool? = nil) { + self.init(account: account, resource: resourceProvider.resource, caller: caller, newerThan: newerThan, unreadOnly: unreadOnly) + } + override func main() { guard !isCancelled else { didFinish() return } - caller.getStream(for: resource, newerThan: newerThan, unreadOnly: unreadOnly) { result in + caller.getStream(for: resourceProvider.resource, newerThan: newerThan, unreadOnly: unreadOnly) { result in switch result { case .success(let stream): self.storedStream = stream