Convert removeFolder to async/await.

This commit is contained in:
Brent Simmons
2023-10-10 22:54:22 -07:00
parent d92c72c15d
commit 029842d04d
10 changed files with 191 additions and 186 deletions

View File

@@ -638,8 +638,8 @@ public enum FetchType {
try await delegate.createFolder(for: self, name: name)
}
public func removeFolder(_ folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.removeFolder(for: self, with: folder, completion: completion)
public func removeFolder(_ folder: Folder) async throws {
try await delegate.removeFolder(for: self, with: folder)
}
public func renameFolder(_ folder: Folder, to name: String) async throws {

View File

@@ -34,7 +34,7 @@ import Secrets
func createFolder(for account: Account, name: String) async throws -> Folder
func renameFolder(for account: Account, with folder: Folder, to name: String) async throws
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void)
func removeFolder(for account: Account, with folder: Folder) async throws
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<Feed, Error>) -> Void)
func renameFeed(for account: Account, feed: Feed, name: String) async throws

View File

@@ -321,67 +321,65 @@ public enum FeedbinAccountDelegateError: String, Error {
}
}
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
func removeFolder(for account: Account, with folder: Folder) async throws {
// Feedbin uses tags and if at least one feed isn't tagged, then the folder doesn't exist on their system
guard folder.hasAtLeastOneFeed() else {
account.removeFolder(folder)
completion(.success(()))
try await account.removeFolder(folder)
return
}
let group = DispatchGroup()
for feed in folder.topLevelFeeds {
if feed.folderRelationship?.count ?? 0 > 1 {
if let feedTaggingID = feed.folderRelationship?[folder.name ?? ""] {
group.enter()
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.deleteTagging(taggingID: feedTaggingID) { result in
self.refreshProgress.completeTask()
group.leave()
switch result {
case .success:
DispatchQueue.main.async {
self.clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
try await withCheckedThrowingContinuation { continuation in
let group = DispatchGroup()
for feed in folder.topLevelFeeds {
if feed.folderRelationship?.count ?? 0 > 1 {
if let feedTaggingID = feed.folderRelationship?[folder.name ?? ""] {
group.enter()
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.deleteTagging(taggingID: feedTaggingID) { result in
self.refreshProgress.completeTask()
group.leave()
switch result {
case .success:
DispatchQueue.main.async {
self.clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
}
case .failure(let error):
self.logger.error("Remove feed error: \(error.localizedDescription, privacy: .public)")
}
}
}
} else {
if let subscriptionID = feed.externalID {
group.enter()
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.deleteSubscription(subscriptionID: subscriptionID) { result in
self.refreshProgress.completeTask()
group.leave()
switch result {
case .success:
DispatchQueue.main.async {
account.clearFeedMetadata(feed)
}
case .failure(let error):
self.logger.error("Remove feed error: \(error.localizedDescription, privacy: .public)")
}
case .failure(let error):
self.logger.error("Remove feed error: \(error.localizedDescription, privacy: .public)")
}
}
}
} else {
if let subscriptionID = feed.externalID {
group.enter()
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.deleteSubscription(subscriptionID: subscriptionID) { result in
self.refreshProgress.completeTask()
group.leave()
switch result {
case .success:
DispatchQueue.main.async {
account.clearFeedMetadata(feed)
}
case .failure(let error):
self.logger.error("Remove feed error: \(error.localizedDescription, privacy: .public)")
}
}
}
}
group.notify(queue: DispatchQueue.main) {
account.removeFolder(folder)
continuation.resume()
}
}
group.notify(queue: DispatchQueue.main) {
account.removeFolder(folder)
completion(.success(()))
}
}
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<Feed, Error>) -> Void) {

View File

@@ -177,9 +177,8 @@ final class LocalAccountDelegate: AccountDelegate, Logging {
folder.name = name
}
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
account.removeFolder(folder)
completion(.success(()))
func removeFolder(for account: Account, with folder: Folder) async throws {
try await account.removeFolder(folder)
}
func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {

View File

@@ -398,10 +398,9 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
}
}
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> ()) {
func removeFolder(for account: Account, with folder: Folder) async throws {
guard let folderToRemove = folder.name else {
completion(.failure(NewsBlurError.invalidParameter))
return
throw NewsBlurError.invalidParameter
}
var feedIDs: [String] = []
@@ -415,15 +414,17 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.removeFolder(named: folderToRemove, feedIDs: feedIDs) { result in
self.refreshProgress.completeTask()
try await withCheckedThrowingContinuation { continuation in
caller.removeFolder(named: folderToRemove, feedIDs: feedIDs) { result in
self.refreshProgress.completeTask()
switch result {
case .success:
account.removeFolder(folder)
completion(.success(()))
case .failure(let error):
completion(.failure(error))
switch result {
case .success:
account.removeFolder(folder)
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}

View File

@@ -320,72 +320,70 @@ public enum ReaderAPIAccountDelegateError: LocalizedError {
}
}
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
func removeFolder(for account: Account, with folder: Folder) async throws {
let group = DispatchGroup()
for feed in folder.topLevelFeeds {
if feed.folderRelationship?.count ?? 0 > 1 {
if let feedExternalID = feed.externalID {
group.enter()
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.deleteTagging(subscriptionID: feedExternalID, tagName: folder.nameForDisplay) { result in
self.refreshProgress.completeTask()
group.leave()
switch result {
case .success:
DispatchQueue.main.async {
self.clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
try await withCheckedThrowingContinuation { continuation in
let group = DispatchGroup()
for feed in folder.topLevelFeeds {
if feed.folderRelationship?.count ?? 0 > 1 {
if let feedExternalID = feed.externalID {
group.enter()
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.deleteTagging(subscriptionID: feedExternalID, tagName: folder.nameForDisplay) { result in
self.refreshProgress.completeTask()
group.leave()
switch result {
case .success:
Task { @MainActor in
self.clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
}
case .failure(let error):
self.logger.error("Remove feed error: \(error.localizedDescription, privacy: .public)")
}
}
}
} else {
if let subscriptionID = feed.externalID {
group.enter()
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.deleteSubscription(subscriptionID: subscriptionID) { result in
self.refreshProgress.completeTask()
group.leave()
switch result {
case .success:
Task { @MainActor in
account.clearFeedMetadata(feed)
}
case .failure(let error):
self.logger.error("Remove feed error: \(error.localizedDescription, privacy: .public)")
}
case .failure(let error):
self.logger.error("Remove feed error: \(error.localizedDescription, privacy: .public)")
}
}
}
} else {
if let subscriptionID = feed.externalID {
group.enter()
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.deleteSubscription(subscriptionID: subscriptionID) { result in
self.refreshProgress.completeTask()
group.leave()
switch result {
case .success:
DispatchQueue.main.async {
account.clearFeedMetadata(feed)
}
case .failure(let error):
self.logger.error("Remove feed error: \(error.localizedDescription, privacy: .public)")
}
}
}
}
}
group.notify(queue: DispatchQueue.main) {
if self.variant == .theOldReader {
account.removeFolder(folder)
completion(.success(()))
} else {
self.caller.deleteTag(folderExternalID: folder.externalID) { result in
switch result {
case .success:
account.removeFolder(folder)
completion(.success(()))
case .failure(let error):
completion(.failure(error))
group.notify(queue: DispatchQueue.main) {
if self.variant == .theOldReader {
account.removeFolder(folder)
continuation.resume()
} else {
self.caller.deleteTag(folderExternalID: folder.externalID) { result in
switch result {
case .success:
account.removeFolder(folder)
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}
}
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<Feed, Error>) -> Void) {

View File

@@ -328,58 +328,62 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
}
}
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
refreshProgress.addToNumberOfTasksAndRemaining(2)
accountZone.findFeedExternalIDs(for: folder) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let feedExternalIDs):
func removeFolder(for account: Account, with folder: Folder) async throws {
let feeds = feedExternalIDs.compactMap { account.existingFeed(withExternalID: $0) }
let group = DispatchGroup()
var errorOccurred = false
for feed in feeds {
group.enter()
self.removeFeedFromCloud(for: account, with: feed, from: folder) { [weak self] result in
group.leave()
if case .failure(let error) = result {
self?.logger.error("Remove folder, remove feed error: \(error.localizedDescription, privacy: .public)")
errorOccurred = true
}
}
}
group.notify(queue: DispatchQueue.global(qos: .background)) {
DispatchQueue.main.async {
guard !errorOccurred else {
self.refreshProgress.completeTask()
completion(.failure(CloudKitAccountDelegateError.unknown))
return
}
self.accountZone.removeFolder(folder) { result in
self.refreshProgress.completeTask()
switch result {
case .success:
account.removeFolder(folder)
completion(.success(()))
case .failure(let error):
completion(.failure(error))
refreshProgress.addToNumberOfTasksAndRemaining(2)
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) -> Void in
accountZone.findFeedExternalIDs(for: folder) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let feedExternalIDs):
let feeds = feedExternalIDs.compactMap { account.existingFeed(withExternalID: $0) }
let group = DispatchGroup()
var errorOccurred = false
for feed in feeds {
group.enter()
self.removeFeedFromCloud(for: account, with: feed, from: folder) { [weak self] result in
group.leave()
if case .failure(let error) = result {
self?.logger.error("Remove folder, remove feed error: \(error.localizedDescription, privacy: .public)")
errorOccurred = true
}
}
}
group.notify(queue: DispatchQueue.global(qos: .background)) {
DispatchQueue.main.async {
guard !errorOccurred else {
self.refreshProgress.completeTask()
continuation.resume(throwing: CloudKitAccountDelegateError.unknown)
return
}
self.accountZone.removeFolder(folder) { result in
self.refreshProgress.completeTask()
switch result {
case .success:
account.removeFolder(folder)
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
case .failure(let error):
self.refreshProgress.completeTask()
self.refreshProgress.completeTask()
self.processAccountError(account, error)
continuation.resume(throwing: error)
}
case .failure(let error):
self.refreshProgress.completeTask()
self.refreshProgress.completeTask()
self.processAccountError(account, error)
completion(.failure(error))
}
}
}
func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {

View File

@@ -292,25 +292,25 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
}
}
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
func removeFolder(for account: Account, with folder: Folder) async throws {
guard let id = folder.externalID else {
return DispatchQueue.main.async {
completion(.failure(FeedlyAccountDelegateError.unableToRemoveFolder(folder.nameForDisplay)))
}
throw FeedlyAccountDelegateError.unableToRemoveFolder(folder.nameForDisplay)
}
let progress = refreshProgress
progress.addToNumberOfTasksAndRemaining(1)
caller.deleteCollection(with: id) { result in
progress.completeTask()
switch result {
case .success:
account.removeFolder(folder)
completion(.success(()))
case .failure(let error):
completion(.failure(error))
try await withCheckedThrowingContinuation { continuation in
caller.deleteCollection(with: id) { result in
progress.completeTask()
switch result {
case .success:
account.removeFolder(folder)
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}

View File

@@ -69,9 +69,10 @@ import RSCore
func deleteElement(_ element:ScriptingObject) {
if let scriptableFolder = element as? ScriptableFolder {
BatchUpdate.shared.perform {
account.removeFolder(scriptableFolder.folder) { result in
}
BatchUpdate.shared.start()
Task { @MainActor in
try? await account.removeFolder(scriptableFolder.folder)
BatchUpdate.shared.end()
}
} else if let scriptableFeed = element as? ScriptableFeed {
BatchUpdate.shared.perform {

View File

@@ -163,12 +163,16 @@ private struct SidebarItemSpecifier {
} else if let folder = folder {
BatchUpdate.shared.start()
account?.removeFolder(folder) { result in
Task { @MainActor in
do {
try await account?.removeFolder(folder)
} catch {
errorHandler(error)
}
BatchUpdate.shared.end()
completion()
self.checkResult(result)
}
}
}