From 0b87acec1e12eef485bd9db47e97750853593724 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 4 Apr 2020 17:35:09 -0500 Subject: [PATCH] Add subscriptions to OPML imports. --- .../CloudKit/CloudKitAccountDelegate.swift | 56 +++++++++++++++++-- .../Account/CloudKit/CloudKitPublicZone.swift | 11 ++-- .../Account/CloudKit/CloudKitZone.swift | 8 +-- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift index 62b0839d6..9d456e461 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift @@ -178,7 +178,55 @@ final class CloudKitAccountDelegate: AccountDelegate { let normalizedItems = OPMLNormalizer.normalize(opmlItems) - accountZone.importOPML(rootExternalID: rootExternalID, items: normalizedItems, completion: completion) + var webFeedURLs = Set() + for opmlItem in normalizedItems { + if let webFeedURL = opmlItem.feedSpecifier?.feedURL { + webFeedURLs.insert(webFeedURL) + } else { + if let childItems = opmlItem.children { + for childItem in childItems { + if let webFeedURL = childItem.feedSpecifier?.feedURL { + webFeedURLs.insert(webFeedURL) + } + } + } + } + } + + refreshProgress.addToNumberOfTasksAndRemaining(webFeedURLs.count + 1) + var errorOccurred = false + + // You have to single thread these or CloudKit gets overwhelmed and freaks out + func takeOneAndPassItOn(_ webFeedURLs: [String], completion: @escaping () -> Void) { + var remainingWebFeedURLS = webFeedURLs + + if let webFeedURL = remainingWebFeedURLS.popLast() { + publicZone.createSubscription(webFeedURL) { result in + self.refreshProgress.completeTask() + if case .failure(let error) = result { + os_log(.error, log: self.log, "Error while subscribing to the feed: %@", error.localizedDescription) + errorOccurred = true + } + takeOneAndPassItOn(remainingWebFeedURLS, completion: completion) + } + } else { + completion() + } + + } + + takeOneAndPassItOn(Array(webFeedURLs)) { + if errorOccurred { + self.refreshProgress.completeTask() + completion(.failure(CloudKitZoneError.unknown)) + } else { + self.accountZone.importOPML(rootExternalID: rootExternalID, items: normalizedItems) { _ in + self.refreshProgress.completeTask() + completion(.success(())) + } + } + } + } func createWebFeed(for account: Account, url urlString: String, name: String?, container: Container, completion: @escaping (Result) -> Void) { @@ -221,10 +269,10 @@ final class CloudKitAccountDelegate: AccountDelegate { feed.externalID = externalID container.addWebFeed(feed) - self.publicZone.createSubscription(feed) { result in + self.publicZone.createSubscription(feed.url) { result in self.refreshProgress.completeTask() if case .failure(let error) = result { - os_log(.error, log: self.log, "An error occurred while creating the subscription: %@.", error.localizedDescription) + os_log(.error, log: self.log, "An error occurred while creating the subscription: %@", error.localizedDescription) } } @@ -442,7 +490,7 @@ final class CloudKitAccountDelegate: AccountDelegate { } func accountDidInitialize(_ account: Account) { - accountZone.delegate = CloudKitAcountZoneDelegate(account: account, refreshProgress: refreshProgress) + accountZone.delegate = CloudKitAcountZoneDelegate(account: account, publicZone: publicZone, refreshProgress: refreshProgress) articlesZone.delegate = CloudKitArticlesZoneDelegate(account: account, database: database, articlesZone: articlesZone) // Check to see if this is a new account and initialize anything we need diff --git a/Frameworks/Account/CloudKit/CloudKitPublicZone.swift b/Frameworks/Account/CloudKit/CloudKitPublicZone.swift index 7ede9b48e..ca6a76a3e 100644 --- a/Frameworks/Account/CloudKit/CloudKitPublicZone.swift +++ b/Frameworks/Account/CloudKit/CloudKitPublicZone.swift @@ -60,8 +60,9 @@ final class CloudKitPublicZone: CloudKitZone { } /// Create a CloudKit subscription for the webfeed and any other supporting records that we need - func createSubscription(_ webFeed: WebFeed, completion: @escaping (Result) -> Void) { - + func createSubscription(_ webFeedURL: String, completion: @escaping (Result) -> Void) { + let webFeedURLMD5String = webFeedURL.md5String + func createSubscription(_ webFeedRecordRef: CKRecord.Reference) { let predicate = NSPredicate(format: "webFeed = %@", webFeedRecordRef) let subscription = CKQuerySubscription(recordType: CloudKitWebFeed.recordType, predicate: predicate, options: [.firesOnRecordUpdate]) @@ -88,7 +89,7 @@ final class CloudKitPublicZone: CloudKitZone { } } - fetch(externalID: webFeed.url.md5String) { result in + fetch(externalID: webFeedURLMD5String) { result in switch result { case .success(let record): @@ -97,10 +98,10 @@ final class CloudKitPublicZone: CloudKitZone { case .failure: - let webFeedRecordID = CKRecord.ID(recordName: webFeed.url.md5String, zoneID: Self.zoneID) + let webFeedRecordID = CKRecord.ID(recordName: webFeedURLMD5String, zoneID: Self.zoneID) let webFeedRecordRef = CKRecord.Reference(recordID: webFeedRecordID, action: .none) let webFeedRecord = CKRecord(recordType: CloudKitWebFeed.recordType, recordID: webFeedRecordID) - webFeedRecord[CloudKitWebFeed.Fields.url] = webFeed.url + webFeedRecord[CloudKitWebFeed.Fields.url] = webFeedURL webFeedRecord[CloudKitWebFeed.Fields.httpLastModified] = "" webFeedRecord[CloudKitWebFeed.Fields.httpEtag] = "" diff --git a/Frameworks/Account/CloudKit/CloudKitZone.swift b/Frameworks/Account/CloudKit/CloudKitZone.swift index 4a47d2baf..dc528e655 100644 --- a/Frameworks/Account/CloudKit/CloudKitZone.swift +++ b/Frameworks/Account/CloudKit/CloudKitZone.swift @@ -72,7 +72,7 @@ extension CloudKitZone { save(subscription) { result in if case .failure(let error) = result { - os_log(.error, log: self.log, "%@ zone subscribe to changes error: %@.", Self.zoneID.zoneName, error.localizedDescription) + os_log(.error, log: self.log, "%@ zone subscribe to changes error: %@", Self.zoneID.zoneName, error.localizedDescription) } } } @@ -86,7 +86,7 @@ extension CloudKitZone { fetchChangesInZone() { result in if case .failure(let error) = result { - os_log(.error, log: self.log, "%@ zone remote notification fetch error: %@.", Self.zoneID.zoneName, error.localizedDescription) + os_log(.error, log: self.log, "%@ zone remote notification fetch error: %@", Self.zoneID.zoneName, error.localizedDescription) } completion() } @@ -319,7 +319,7 @@ extension CloudKitZone { group.enter() self.modify(recordsToSave: chunk, recordIDsToDelete: recordIDsToDelete) { result in if case .failure(let error) = result { - os_log(.error, log: self.log, "%@ zone modify records error: %@.", Self.zoneID.zoneName, error.localizedDescription) + os_log(.error, log: self.log, "%@ zone modify records error: %@", Self.zoneID.zoneName, error.localizedDescription) errorOccurred = true } group.leave() @@ -396,7 +396,7 @@ extension CloudKitZone { self.fetchChangesInZone(completion: completion) } default: - os_log(.error, log: self.log, "%@ zone fetch changes error: %@.", zoneID.zoneName, error?.localizedDescription ?? "Unknown") + os_log(.error, log: self.log, "%@ zone fetch changes error: %@", zoneID.zoneName, error?.localizedDescription ?? "Unknown") } }