diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index e958c324f..38efd6984 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -42,7 +42,6 @@ 5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA48227B497600D19003 /* FeedbinAPICaller.swift */; }; 5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA4D227B829A00D19003 /* FeedbinAccountDelegate.swift */; }; 514BF5202391B0DB00902FE8 /* SingleArticleFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514BF51F2391B0DB00902FE8 /* SingleArticleFetcher.swift */; }; - 515000002438682300C1A442 /* CloudKitPublicZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5150FFFF2438682300C1A442 /* CloudKitPublicZone.swift */; }; 5150FFFE243823B800C1A442 /* CloudKitError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5150FFFD243823B800C1A442 /* CloudKitError.swift */; }; 5154367B228EEB28005E1CDF /* FeedbinImportResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5154367A228EEB28005E1CDF /* FeedbinImportResult.swift */; }; 515E4EB52324FF8C0057B0E7 /* CredentialsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515E4EB22324FF8C0057B0E7 /* CredentialsManager.swift */; }; @@ -60,7 +59,6 @@ 519E84A62433D49000D238B0 /* OPMLNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84A52433D49000D238B0 /* OPMLNormalizer.swift */; }; 519E84A82434C5EF00D238B0 /* CloudKitArticlesZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */; }; 519E84AC2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84AB2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift */; }; - 51B544672438F410003F03BF /* CKContainer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B544662438F410003F03BF /* CKContainer+Extensions.swift */; }; 51BB7B84233531BC008E8144 /* AccountBehaviors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7B83233531BC008E8144 /* AccountBehaviors.swift */; }; 51BC8FCC237EC055004F8B56 /* Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC8FCB237EC055004F8B56 /* Feed.swift */; }; 51BFDECE238B508D00216323 /* ContainerIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BFDECD238B508D00216323 /* ContainerIdentifier.swift */; }; @@ -280,7 +278,6 @@ 5144EA4D227B829A00D19003 /* FeedbinAccountDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAccountDelegate.swift; sourceTree = ""; }; 514BF51F2391B0DB00902FE8 /* SingleArticleFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleArticleFetcher.swift; sourceTree = ""; }; 5150FFFD243823B800C1A442 /* CloudKitError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudKitError.swift; sourceTree = ""; }; - 5150FFFF2438682300C1A442 /* CloudKitPublicZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitPublicZone.swift; sourceTree = ""; }; 5154367A228EEB28005E1CDF /* FeedbinImportResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinImportResult.swift; sourceTree = ""; }; 515E4EB22324FF8C0057B0E7 /* CredentialsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialsManager.swift; sourceTree = ""; }; 515E4EB32324FF8C0057B0E7 /* URLRequest+RSWeb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URLRequest+RSWeb.swift"; sourceTree = ""; }; @@ -298,7 +295,6 @@ 519E84A52433D49000D238B0 /* OPMLNormalizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLNormalizer.swift; sourceTree = ""; }; 519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitArticlesZone.swift; sourceTree = ""; }; 519E84AB2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudKitArticlesZoneDelegate.swift; sourceTree = ""; }; - 51B544662438F410003F03BF /* CKContainer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CKContainer+Extensions.swift"; sourceTree = ""; }; 51BB7B83233531BC008E8144 /* AccountBehaviors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountBehaviors.swift; sourceTree = ""; }; 51BC8FCB237EC055004F8B56 /* Feed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = ""; }; 51BFDECD238B508D00216323 /* ContainerIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerIdentifier.swift; sourceTree = ""; }; @@ -525,7 +521,6 @@ 5103A9D7242253DC00410853 /* CloudKit */ = { isa = PBXGroup; children = ( - 51B544662438F410003F03BF /* CKContainer+Extensions.swift */, 512DD4CA2431000600C17B1F /* CKRecord+Extensions.swift */, 5103A9D82422546800410853 /* CloudKitAccountDelegate.swift */, 51E4DB2F2426353D0091EB5B /* CloudKitAccountZone.swift */, @@ -533,7 +528,6 @@ 519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */, 519E84AB2435019100D238B0 /* CloudKitArticlesZoneDelegate.swift */, 5150FFFD243823B800C1A442 /* CloudKitError.swift */, - 5150FFFF2438682300C1A442 /* CloudKitPublicZone.swift */, 51E4DB2D242633ED0091EB5B /* CloudKitZone.swift */, 51C034DE242D65D20014DC71 /* CloudKitZoneResult.swift */, ); @@ -1116,7 +1110,6 @@ 84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */, 841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */, 510BD113232C3E9D002692E4 /* WebFeedMetadataFile.swift in Sources */, - 515000002438682300C1A442 /* CloudKitPublicZone.swift in Sources */, 5103A9D92422546800410853 /* CloudKitAccountDelegate.swift in Sources */, 5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */, 9E784EBE237E890600099B1B /* FeedlyLogoutOperation.swift in Sources */, @@ -1159,7 +1152,6 @@ 84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */, 51BC8FCC237EC055004F8B56 /* Feed.swift in Sources */, 846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */, - 51B544672438F410003F03BF /* CKContainer+Extensions.swift in Sources */, 9EA643CF2391D3560018A28C /* FeedlyAddExistingFeedOperation.swift in Sources */, 55203300229D5D5A009559E0 /* ReaderAPICaller.swift in Sources */, 9E1D154F233371DD00F4944C /* FeedlyGetCollectionsOperation.swift in Sources */, diff --git a/Frameworks/Account/CloudKit/CKContainer+Extensions.swift b/Frameworks/Account/CloudKit/CKContainer+Extensions.swift deleted file mode 100644 index afa27e410..000000000 --- a/Frameworks/Account/CloudKit/CKContainer+Extensions.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// CKContainer+Extensions.swift -// Account -// -// Created by Maurice Parker on 4/4/20. -// Copyright © 2020 Ranchero Software, LLC. All rights reserved. -// - -import Foundation -import CloudKit - -extension CKContainer { - - private static let userRecordIDKey = "cloudkit.server.userRecordID" - - var userRecordID: String? { - get { - return UserDefaults.standard.string(forKey: Self.userRecordIDKey) - } - set { - guard let userRecordID = newValue else { - UserDefaults.standard.removeObject(forKey: Self.userRecordIDKey) - return - } - UserDefaults.standard.set(userRecordID, forKey: Self.userRecordIDKey) - } - } - - func fetchUserRecordID() { - guard userRecordID == nil else { return } - fetchUserRecordID { recordID, error in - guard let recordID = recordID, error == nil else { - return - } - DispatchQueue.main.async { - self.userRecordID = recordID.recordName - } - } - } - -} diff --git a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift index 6317434d5..407e4e31c 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift @@ -30,10 +30,9 @@ final class CloudKitAccountDelegate: AccountDelegate { return CKContainer(identifier: "iCloud.\(orgID).NetNewsWire") }() - private lazy var zones: [CloudKitZone] = [accountZone, articlesZone, publicZone] + private lazy var zones: [CloudKitZone] = [accountZone, articlesZone] private let accountZone: CloudKitAccountZone private let articlesZone: CloudKitArticlesZone - private let publicZone: CloudKitPublicZone private let refresher = LocalAccountRefresher() @@ -49,7 +48,6 @@ final class CloudKitAccountDelegate: AccountDelegate { init(dataFolder: String) { accountZone = CloudKitAccountZone(container: container) articlesZone = CloudKitArticlesZone(container: container) - publicZone = CloudKitPublicZone(container: container) let databaseFilePath = (dataFolder as NSString).appendingPathComponent("Sync.sqlite3") database = SyncDatabase(databaseFilePath: databaseFilePath) @@ -195,17 +193,10 @@ final class CloudKitAccountDelegate: AccountDelegate { } } - refreshProgress.addToNumberOfTasksAndRemaining(2) - publicZone.manageSubscriptions(webFeedURLs) { result in - self.refreshProgress.completeTask() - switch result { - case .success: - self.accountZone.importOPML(rootExternalID: rootExternalID, items: normalizedItems) { _ in - self.refreshAll(for: account, downloadFeeds: false, completion: completion) - } - case .failure(let error): - completion(.failure(error)) - } + // Add one task here to show we started immediately. We don't need to complete is because refreshAll clears everything at the end. + refreshProgress.addToNumberOfTasksAndRemaining(1) + self.accountZone.importOPML(rootExternalID: rootExternalID, items: normalizedItems) { _ in + self.refreshAll(for: account, downloadFeeds: false, completion: completion) } } @@ -219,7 +210,7 @@ final class CloudKitAccountDelegate: AccountDelegate { } BatchUpdate.shared.start() - refreshProgress.addToNumberOfTasksAndRemaining(4) + refreshProgress.addToNumberOfTasksAndRemaining(3) FeedFinder.find(url: url) { result in self.refreshProgress.completeTask() @@ -250,13 +241,6 @@ final class CloudKitAccountDelegate: AccountDelegate { feed.externalID = externalID container.addWebFeed(feed) - self.publicZone.manageSubscriptions(account.flattenedWebFeedURLs) { 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) - } - } - InitialFeedDownloader.download(url) { parsedFeed in self.refreshProgress.completeTask() @@ -302,25 +286,13 @@ final class CloudKitAccountDelegate: AccountDelegate { } func removeWebFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result) -> Void) { - refreshProgress.addToNumberOfTasksAndRemaining(2) + refreshProgress.addToNumberOfTasksAndRemaining(1) accountZone.removeWebFeed(feed, from: container) { result in self.refreshProgress.completeTask() switch result { - case .success(let deleted): + case .success: container.removeWebFeed(feed) - if deleted { - self.publicZone.manageSubscriptions(account.flattenedWebFeedURLs) { result in - self.refreshProgress.completeTask() - switch result { - case .success: - completion(.success(())) - case .failure(let error): - completion(.failure(error)) - } - } - } else { - completion(.success(())) - } + completion(.success(())) case .failure(let error): completion(.failure(error)) } @@ -484,7 +456,6 @@ final class CloudKitAccountDelegate: AccountDelegate { // Check to see if this is a new account and initialize anything we need if account.externalID == nil { - container.fetchUserRecordID() accountZone.findOrCreateAccount() { result in switch result { case .success(let externalID): diff --git a/Frameworks/Account/CloudKit/CloudKitPublicZone.swift b/Frameworks/Account/CloudKit/CloudKitPublicZone.swift deleted file mode 100644 index 05ef0d7d7..000000000 --- a/Frameworks/Account/CloudKit/CloudKitPublicZone.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// CloudKitPublicZone.swift -// Account -// -// Created by Maurice Parker on 4/4/20. -// Copyright © 2020 Ranchero Software, LLC. All rights reserved. -// - -import Foundation -import CloudKit -import os.log - -final class CloudKitPublicZone: CloudKitZone { - - static var zoneID: CKRecordZone.ID { - return CKRecordZone.default().zoneID - } - - var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit") - - weak var container: CKContainer? - weak var database: CKDatabase? - var delegate: CloudKitZoneDelegate? - - struct CloudKitWebFeed { - static let recordType = "WebFeed" - struct Fields { - static let url = "url" - static let httpLastModified = "httpLastModified" - static let httpEtag = "httpEtag" - } - } - - struct CloudKitWebFeedCheck { - static let recordType = "WebFeedCheck" - struct Fields { - static let webFeed = "webFeed" - static let lastCheck = "lastCheck" - } - } - - struct CloudKitUserSubscription { - static let recordType = "UserSubscription" - struct Fields { - static let userRecordID = "userRecordID" - static let webFeed = "webFeed" - static let subscriptionID = "subscriptionID" - } - } - - init(container: CKContainer) { - self.container = container - self.database = container.publicCloudDatabase - } - - func subscribeToZoneChanges() {} - - func receiveRemoteNotification(userInfo: [AnyHashable : Any], completion: @escaping () -> Void) { - completion() - } - - /// Create any new subscriptions and delete any old ones - func manageSubscriptions(_ webFeedURLs: Set, completion: @escaping (Result) -> Void) { - - var webFeedRecords = [CKRecord]() - for webFeedURL in webFeedURLs { - let webFeedRecordID = CKRecord.ID(recordName: webFeedURL.md5String, zoneID: Self.zoneID) - let webFeedRecord = CKRecord(recordType: CloudKitWebFeed.recordType, recordID: webFeedRecordID) - webFeedRecord[CloudKitWebFeed.Fields.url] = webFeedURL - webFeedRecord[CloudKitWebFeed.Fields.httpLastModified] = "" - webFeedRecord[CloudKitWebFeed.Fields.httpEtag] = "" - webFeedRecords.append(webFeedRecord) - } - - self.saveIfNew(webFeedRecords) { _ in - - var subscriptions = [CKSubscription]() - let webFeedURLChunks = Array(webFeedURLs).chunked(into: 20) - for webFeedURLChunk in webFeedURLChunks { - - let predicate = NSPredicate(format: "url in %@", webFeedURLChunk) - let subscription = CKQuerySubscription(recordType: CloudKitWebFeed.recordType, predicate: predicate, options: [.firesOnRecordUpdate]) - let info = CKSubscription.NotificationInfo() - info.shouldSendContentAvailable = true - info.desiredKeys = [CloudKitWebFeed.Fields.httpLastModified, CloudKitWebFeed.Fields.httpEtag] - subscription.notificationInfo = info - subscriptions.append(subscription) - - } - - self.fetchAllUserSubscriptions() { result in - switch result { - case .success(let subscriptionsToDelete): - let subscriptionToDeleteIDs = subscriptionsToDelete.map({ $0.subscriptionID }) - self.modify(subscriptionsToSave: subscriptions, subscriptionIDsToDelete: subscriptionToDeleteIDs, completion: completion) - case .failure(let error): - completion(.failure(error)) - } - } - - } - - } - -} diff --git a/Frameworks/Account/CloudKit/CloudKitZone.swift b/Frameworks/Account/CloudKit/CloudKitZone.swift index bf18652e3..fb7a3269f 100644 --- a/Frameworks/Account/CloudKit/CloudKitZone.swift +++ b/Frameworks/Account/CloudKit/CloudKitZone.swift @@ -337,32 +337,6 @@ extension CloudKitZone { } } - /// Bulk add (or modify I suppose) and delete of subscriptions - func modify(subscriptionsToSave: [CKSubscription], subscriptionIDsToDelete: [CKSubscription.ID], completion: @escaping (Result) -> Void) { - let op = CKModifySubscriptionsOperation(subscriptionsToSave: subscriptionsToSave, subscriptionIDsToDelete: subscriptionIDsToDelete) - - op.modifySubscriptionsCompletionBlock = { [weak self] (_, _, error) in - guard let self = self else { return } - - switch CloudKitZoneResult.resolve(error) { - case .success: - DispatchQueue.main.async { - completion(.success(())) - } - case .retry(let timeToWait): - self.retryIfPossible(after: timeToWait) { - self.modify(subscriptionsToSave: subscriptionsToSave, subscriptionIDsToDelete: subscriptionIDsToDelete, completion: completion) - } - default: - DispatchQueue.main.async { - completion(.failure(CloudKitError(error!))) - } - } - } - - database?.add(op) - } - /// Modify and delete the supplied CKRecords and CKRecord.IDs func modify(recordsToSave: [CKRecord], recordIDsToDelete: [CKRecord.ID], completion: @escaping (Result) -> Void) { let op = CKModifyRecordsOperation(recordsToSave: recordsToSave, recordIDsToDelete: recordIDsToDelete) @@ -432,26 +406,6 @@ extension CloudKitZone { database?.add(op) } - /// Fetch all the subscriptions that a user has in the current database in all zones - func fetchAllUserSubscriptions(completion: @escaping (Result<[CKSubscription], Error>) -> Void) { - database?.fetchAllSubscriptions() { subscriptions, error in - switch CloudKitZoneResult.resolve(error) { - case .success: - DispatchQueue.main.async { - completion(.success((subscriptions!))) - } - case .retry(let timeToWait): - self.retryIfPossible(after: timeToWait) { - self.fetchAllUserSubscriptions(completion: completion) - } - default: - DispatchQueue.main.async { - completion(.failure(CloudKitError(error!))) - } - } - } - } - /// Fetch all the changes in the CKZone since the last time we checked func fetchChangesInZone(completion: @escaping (Result) -> Void) {