Changed CloudKit account processing to be batch based so that background additions download the articles.

This commit is contained in:
Maurice Parker 2020-04-12 15:57:00 -05:00
parent 04bca17538
commit ddc364f4d6
5 changed files with 79 additions and 79 deletions

View File

@ -549,9 +549,7 @@ private extension CloudKitAccountDelegate {
completion(.failure(error)) completion(.failure(error))
} }
BatchUpdate.shared.start()
accountZone.fetchChangesInZone() { result in accountZone.fetchChangesInZone() { result in
BatchUpdate.shared.end()
switch result { switch result {
case .success: case .success:

View File

@ -247,24 +247,10 @@ final class CloudKitAccountZone: CloudKitZone {
} }
} }
default: default:
DispatchQueue.main.async { self.createContainer(name: "Account", isAccount: true, completion: completion)
completion(.failure(CloudKitError(error!)))
}
} }
} }
query(ckQuery) { result in
switch result {
case .success(let records):
if records.count > 0 {
completion(.success(records[0].externalID))
} else {
self.createContainer(name: "Account", isAccount: true, completion: completion)
}
case .failure:
self.createContainer(name: "Account", isAccount: true, completion: completion)
}
}
} }
func createFolder(name: String, completion: @escaping (Result<String, Error>) -> Void) { func createFolder(name: String, completion: @escaping (Result<String, Error>) -> Void) {

View File

@ -26,33 +26,43 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
self.refreshProgress = refreshProgress self.refreshProgress = refreshProgress
} }
func cloudKitDidChange(record: CKRecord) {
switch record.recordType {
case CloudKitAccountZone.CloudKitWebFeed.recordType:
addOrUpdateWebFeed(record)
case CloudKitAccountZone.CloudKitContainer.recordType:
addOrUpdateContainer(record)
default:
assertionFailure("Unknown record type: \(record.recordType)")
}
}
func cloudKitDidDelete(recordKey: CloudKitRecordKey) {
switch recordKey.recordType {
case CloudKitAccountZone.CloudKitWebFeed.recordType:
removeWebFeed(recordKey.recordID.externalID)
case CloudKitAccountZone.CloudKitContainer.recordType:
removeContainer(recordKey.recordID.externalID)
default:
assertionFailure("Unknown record type: \(recordKey.recordType)")
}
}
func cloudKitDidModify(changed: [CKRecord], deleted: [CloudKitRecordKey], completion: @escaping (Result<Void, Error>) -> Void) { func cloudKitDidModify(changed: [CKRecord], deleted: [CloudKitRecordKey], completion: @escaping (Result<Void, Error>) -> Void) {
let group = DispatchGroup()
for changedRecord in changed {
switch changedRecord.recordType {
case CloudKitAccountZone.CloudKitWebFeed.recordType:
group.enter()
addOrUpdateWebFeed(changedRecord) {
group.leave()
}
case CloudKitAccountZone.CloudKitContainer.recordType:
group.enter()
addOrUpdateContainer(changedRecord) {
group.leave()
}
default:
assertionFailure("Unknown record type: \(changedRecord.recordType)")
}
}
for deletedRecordKey in deleted {
switch deletedRecordKey.recordType {
case CloudKitAccountZone.CloudKitWebFeed.recordType:
removeWebFeed(deletedRecordKey.recordID.externalID)
case CloudKitAccountZone.CloudKitContainer.recordType:
removeContainer(deletedRecordKey.recordID.externalID)
default:
assertionFailure("Unknown record type: \(deletedRecordKey.recordType)")
}
}
group.notify(queue: DispatchQueue.main) {
completion(.success(())) completion(.success(()))
} }
}
func addOrUpdateWebFeed(_ record: CKRecord) { func addOrUpdateWebFeed(_ record: CKRecord, completion: @escaping () -> Void) {
guard let account = account, guard let account = account,
let urlString = record[CloudKitAccountZone.CloudKitWebFeed.Fields.url] as? String, let urlString = record[CloudKitAccountZone.CloudKitWebFeed.Fields.url] as? String,
let containerExternalIDs = record[CloudKitAccountZone.CloudKitWebFeed.Fields.containerExternalIDs] as? [String], let containerExternalIDs = record[CloudKitAccountZone.CloudKitWebFeed.Fields.containerExternalIDs] as? [String],
@ -61,19 +71,30 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
let editedName = record[CloudKitAccountZone.CloudKitWebFeed.Fields.editedName] as? String let editedName = record[CloudKitAccountZone.CloudKitWebFeed.Fields.editedName] as? String
if let webFeed = account.existingWebFeed(withExternalID: record.externalID) { if let webFeed = account.existingWebFeed(withExternalID: record.externalID) {
updateWebFeed(webFeed, editedName: editedName, containerExternalIDs: containerExternalIDs) updateWebFeed(webFeed, editedName: editedName, containerExternalIDs: containerExternalIDs)
completion()
} else { } else {
var webFeed: WebFeed? = nil
let group = DispatchGroup()
for containerExternalID in containerExternalIDs { for containerExternalID in containerExternalIDs {
group.enter()
if let container = account.existingContainer(withExternalID: containerExternalID) { if let container = account.existingContainer(withExternalID: containerExternalID) {
if webFeed == nil { createWebFeedIfNecessary(url: url, editedName: editedName, webFeedExternalID: record.externalID) { webFeed in
webFeed = createWebFeed(url: url, editedName: editedName, webFeedExternalID: record.externalID) container.addWebFeed(webFeed)
group.leave()
} }
container.addWebFeed(webFeed!)
} else { } else {
addUnclaimedWebFeed(url: url, editedName: editedName, webFeedExternalID: record.externalID, containerExternalID: containerExternalID) addUnclaimedWebFeed(url: url, editedName: editedName, webFeedExternalID: record.externalID, containerExternalID: containerExternalID)
group.leave()
} }
} }
group.notify(queue: DispatchQueue.main) {
completion()
}
} }
} }
@ -83,7 +104,7 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
} }
} }
func addOrUpdateContainer(_ record: CKRecord) { func addOrUpdateContainer(_ record: CKRecord, completion: @escaping () -> Void) {
guard let account = account, guard let account = account,
let name = record[CloudKitAccountZone.CloudKitContainer.Fields.name] as? String, let name = record[CloudKitAccountZone.CloudKitContainer.Fields.name] as? String,
let isAccount = record[CloudKitAccountZone.CloudKitContainer.Fields.isAccount] as? String, let isAccount = record[CloudKitAccountZone.CloudKitContainer.Fields.isAccount] as? String,
@ -98,16 +119,26 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
} }
if let folder = folder, let containerExternalID = folder.externalID, let unclaimedWebFeeds = unclaimedWebFeeds[containerExternalID] { if let folder = folder, let containerExternalID = folder.externalID, let unclaimedWebFeeds = unclaimedWebFeeds[containerExternalID] {
let group = DispatchGroup()
for unclaimedWebFeed in unclaimedWebFeeds { for unclaimedWebFeed in unclaimedWebFeeds {
var webFeed = account.existingWebFeed(withExternalID: unclaimedWebFeed.webFeedExternalID) group.enter()
if webFeed == nil { createWebFeedIfNecessary(url: unclaimedWebFeed.url, editedName: unclaimedWebFeed.editedName, webFeedExternalID: unclaimedWebFeed.webFeedExternalID) { webFeed in
webFeed = createWebFeed(url: unclaimedWebFeed.url, editedName: unclaimedWebFeed.editedName, webFeedExternalID: unclaimedWebFeed.webFeedExternalID)
}
if let webFeed = webFeed {
folder.addWebFeed(webFeed) folder.addWebFeed(webFeed)
group.leave()
} }
} }
group.notify(queue: DispatchQueue.main) {
self.unclaimedWebFeeds.removeValue(forKey: containerExternalID) self.unclaimedWebFeeds.removeValue(forKey: containerExternalID)
completion()
}
} else {
completion()
} }
} }
@ -144,8 +175,13 @@ private extension CloudKitAcountZoneDelegate {
} }
} }
func createWebFeed(url: URL, editedName: String?, webFeedExternalID: String) -> WebFeed? { func createWebFeedIfNecessary(url: URL, editedName: String?, webFeedExternalID: String, completion: @escaping (WebFeed) -> Void) {
guard let account = account else { return nil } guard let account = account else { return }
if let webFeed = account.existingWebFeed(withExternalID: webFeedExternalID) {
completion(webFeed)
return
}
let webFeed = account.createWebFeed(with: editedName, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil) let webFeed = account.createWebFeed(with: editedName, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
webFeed.editedName = editedName webFeed.editedName = editedName
@ -155,11 +191,12 @@ private extension CloudKitAcountZoneDelegate {
InitialFeedDownloader.download(url) { parsedFeed in InitialFeedDownloader.download(url) { parsedFeed in
self.refreshProgress?.completeTask() self.refreshProgress?.completeTask()
if let parsedFeed = parsedFeed { if let parsedFeed = parsedFeed {
account.update(webFeed, with: parsedFeed, {_ in }) account.update(webFeed, with: parsedFeed, { _ in
completion(webFeed)
})
} }
} }
return webFeed
} }
func addUnclaimedWebFeed(url: URL, editedName: String?, webFeedExternalID: String, containerExternalID: String) { func addUnclaimedWebFeed(url: URL, editedName: String?, webFeedExternalID: String, containerExternalID: String) {

View File

@ -37,14 +37,6 @@ class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate {
self.refreshProgress = refreshProgress self.refreshProgress = refreshProgress
} }
func cloudKitDidChange(record: CKRecord) {
}
func cloudKitDidDelete(recordKey: CloudKitRecordKey) {
// Article downloads clean up old articles and statuses
}
func cloudKitDidModify(changed: [CKRecord], deleted: [CloudKitRecordKey], completion: @escaping (Result<Void, Error>) -> Void) { func cloudKitDidModify(changed: [CKRecord], deleted: [CloudKitRecordKey], completion: @escaping (Result<Void, Error>) -> Void) {
database.selectPendingReadStatusArticleIDs() { result in database.selectPendingReadStatusArticleIDs() { result in

View File

@ -25,8 +25,6 @@ enum CloudKitZoneError: LocalizedError {
} }
protocol CloudKitZoneDelegate: class { protocol CloudKitZoneDelegate: class {
func cloudKitDidChange(record: CKRecord);
func cloudKitDidDelete(recordKey: CloudKitRecordKey)
func cloudKitDidModify(changed: [CKRecord], deleted: [CloudKitRecordKey], completion: @escaping (Result<Void, Error>) -> Void); func cloudKitDidModify(changed: [CKRecord], deleted: [CloudKitRecordKey], completion: @escaping (Result<Void, Error>) -> Void);
} }
@ -492,24 +490,13 @@ extension CloudKitZone {
} }
} }
op.recordChangedBlock = { [weak self] record in op.recordChangedBlock = { record in
guard let self = self else { return }
changedRecords.append(record) changedRecords.append(record)
DispatchQueue.main.async {
self.delegate?.cloudKitDidChange(record: record)
}
} }
op.recordWithIDWasDeletedBlock = { [weak self] recordID, recordType in op.recordWithIDWasDeletedBlock = { recordID, recordType in
guard let self = self else { return }
let recordKey = CloudKitRecordKey(recordType: recordType, recordID: recordID) let recordKey = CloudKitRecordKey(recordType: recordType, recordID: recordID)
deletedRecordKeys.append(recordKey) deletedRecordKeys.append(recordKey)
DispatchQueue.main.async {
self.delegate?.cloudKitDidDelete(recordKey: recordKey)
}
} }
op.recordZoneFetchCompletionBlock = { [weak self] zoneID ,token, _, _, error in op.recordZoneFetchCompletionBlock = { [weak self] zoneID ,token, _, _, error in