Move CloudKitArticlesZone to CloudKitSync module.

This commit is contained in:
Brent Simmons 2024-04-20 15:19:10 -07:00
parent 60dd9ad82a
commit 55e3e20be1
4 changed files with 69 additions and 65 deletions

View File

@ -420,6 +420,7 @@ enum CloudKitAccountDelegateError: LocalizedError {
accountZone.delegate = CloudKitAcountZoneDelegate(account: account, refreshProgress: refreshProgress, articlesZone: articlesZone) accountZone.delegate = CloudKitAcountZoneDelegate(account: account, refreshProgress: refreshProgress, articlesZone: articlesZone)
articlesZone.delegate = CloudKitArticlesZoneDelegate(account: account, database: database, articlesZone: articlesZone) articlesZone.delegate = CloudKitArticlesZoneDelegate(account: account, database: database, articlesZone: articlesZone)
articlesZone.feedInfoDelegate = self
Task { Task {
try await database.resetAllSelectedForProcessing() try await database.resetAllSelectedForProcessing()
@ -748,3 +749,16 @@ extension CloudKitAccountDelegate: LocalAccountRefresherDelegate {
} }
} }
} }
extension CloudKitAccountDelegate: CloudKitFeedInfoDelegate {
@MainActor func feedExternalID(article: Article) -> String? {
article.feed?.externalID
}
@MainActor func feedURL(article: Article) -> String? {
article.feed?.url
}
}

View File

@ -16,8 +16,9 @@ import Articles
import ArticlesDatabase import ArticlesDatabase
import Database import Database
import CloudKitExtras import CloudKitExtras
import CloudKitSync
class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate { final class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit") private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")

View File

@ -9,8 +9,9 @@
import Foundation import Foundation
import os.log import os.log
import Core import Core
import CloudKitSync
class CloudKitReceiveStatusOperation: MainThreadOperation { final class CloudKitReceiveStatusOperation: MainThreadOperation {
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit") private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")

View File

@ -15,58 +15,64 @@ import CloudKit
import Articles import Articles
import SyncDatabase import SyncDatabase
import CloudKitExtras import CloudKitExtras
import CloudKitSync
final class CloudKitArticlesZone: CloudKitZone { public protocol CloudKitFeedInfoDelegate {
var zoneID: CKRecordZone.ID @MainActor func feedExternalID(article: Article) -> String?
@MainActor func feedURL(article: Article) -> String?
}
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit") public final class CloudKitArticlesZone: CloudKitZone {
weak var container: CKContainer? public var zoneID: CKRecordZone.ID
weak var database: CKDatabase?
var delegate: CloudKitZoneDelegate? = nil public var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
public weak var container: CKContainer?
public weak var database: CKDatabase?
public var delegate: CloudKitZoneDelegate? = nil
public var feedInfoDelegate: CloudKitFeedInfoDelegate? = nil
var compressionQueue = DispatchQueue(label: "Articles Zone Compression Queue") var compressionQueue = DispatchQueue(label: "Articles Zone Compression Queue")
struct CloudKitArticle { public struct CloudKitArticle {
static let recordType = "Article" public static let recordType = "Article"
struct Fields { public struct Fields {
static let articleStatus = "articleStatus" public static let articleStatus = "articleStatus"
static let feedURL = "webFeedURL" public static let feedURL = "webFeedURL"
static let uniqueID = "uniqueID" public static let uniqueID = "uniqueID"
static let title = "title" public static let title = "title"
static let contentHTML = "contentHTML" public static let contentHTML = "contentHTML"
static let contentHTMLData = "contentHTMLData" public static let contentHTMLData = "contentHTMLData"
static let contentText = "contentText" public static let contentText = "contentText"
static let contentTextData = "contentTextData" public static let contentTextData = "contentTextData"
static let url = "url" public static let url = "url"
static let externalURL = "externalURL" public static let externalURL = "externalURL"
static let summary = "summary" public static let summary = "summary"
static let imageURL = "imageURL" public static let imageURL = "imageURL"
static let datePublished = "datePublished" public static let datePublished = "datePublished"
static let dateModified = "dateModified" public static let dateModified = "dateModified"
static let parsedAuthors = "parsedAuthors" public static let parsedAuthors = "parsedAuthors"
} }
} }
struct CloudKitArticleStatus { public struct CloudKitArticleStatus {
static let recordType = "ArticleStatus" public static let recordType = "ArticleStatus"
struct Fields { public struct Fields {
static let feedExternalID = "webFeedExternalID" public static let feedExternalID = "webFeedExternalID"
static let read = "read" public static let read = "read"
static let starred = "starred" public static let starred = "starred"
} }
} }
@MainActor init(container: CKContainer) { @MainActor public init(container: CKContainer) {
self.container = container self.container = container
self.database = container.privateCloudDatabase self.database = container.privateCloudDatabase
self.zoneID = CKRecordZone.ID(zoneName: "Articles", ownerName: CKCurrentUserDefaultName) self.zoneID = CKRecordZone.ID(zoneName: "Articles", ownerName: CKCurrentUserDefaultName)
migrateChangeToken() migrateChangeToken()
} }
@MainActor func refreshArticles(completion: @escaping ((Result<Void, Error>) -> Void)) { @MainActor public func refreshArticles(completion: @escaping ((Result<Void, Error>) -> Void)) {
fetchChangesInZone() { result in fetchChangesInZone() { result in
switch result { switch result {
case .success: case .success:
@ -90,27 +96,7 @@ final class CloudKitArticlesZone: CloudKitZone {
} }
} }
@MainActor func saveNewArticles(_ articles: Set<Article>, completion: @escaping ((Result<Void, Error>) -> Void)) { public func deleteArticles(_ feedExternalID: String) async throws {
guard !articles.isEmpty else {
completion(.success(()))
return
}
var records = [CKRecord]()
let saveArticles = articles.filter { $0.status.read == false || $0.status.starred == true }
for saveArticle in saveArticles {
records.append(makeStatusRecord(saveArticle))
records.append(makeArticleRecord(saveArticle))
}
compressionQueue.async {
let compressedRecords = self.compressArticleRecords(records)
self.save(compressedRecords, completion: completion)
}
}
func deleteArticles(_ feedExternalID: String) async throws {
let predicate = NSPredicate(format: "webFeedExternalID = %@", feedExternalID) let predicate = NSPredicate(format: "webFeedExternalID = %@", feedExternalID)
let ckQuery = CKQuery(recordType: CloudKitArticleStatus.recordType, predicate: predicate) let ckQuery = CKQuery(recordType: CloudKitArticleStatus.recordType, predicate: predicate)
@ -118,7 +104,7 @@ final class CloudKitArticlesZone: CloudKitZone {
try await delete(ckQuery: ckQuery) try await delete(ckQuery: ckQuery)
} }
@MainActor func modifyArticles(_ statusUpdates: [CloudKitArticleStatusUpdate], completion: @escaping ((Result<Void, Error>) -> Void)) { @MainActor public func modifyArticles(_ statusUpdates: [CloudKitArticleStatusUpdate], completion: @escaping ((Result<Void, Error>) -> Void)) {
guard !statusUpdates.isEmpty else { guard !statusUpdates.isEmpty else {
completion(.success(())) completion(.success(()))
return return
@ -200,9 +186,11 @@ private extension CloudKitArticlesZone {
@MainActor func makeStatusRecord(_ article: Article) -> CKRecord { @MainActor func makeStatusRecord(_ article: Article) -> CKRecord {
let recordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: zoneID) let recordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: zoneID)
let record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID) let record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID)
if let feedExternalID = article.feed?.externalID {
if let feedExternalID = feedInfoDelegate?.feedExternalID(article: article) {
record[CloudKitArticleStatus.Fields.feedExternalID] = feedExternalID record[CloudKitArticleStatus.Fields.feedExternalID] = feedExternalID
} }
record[CloudKitArticleStatus.Fields.read] = article.status.read ? "1" : "0" record[CloudKitArticleStatus.Fields.read] = article.status.read ? "1" : "0"
record[CloudKitArticleStatus.Fields.starred] = article.status.starred ? "1" : "0" record[CloudKitArticleStatus.Fields.starred] = article.status.starred ? "1" : "0"
return record return record
@ -212,7 +200,7 @@ private extension CloudKitArticlesZone {
let recordID = CKRecord.ID(recordName: statusID(statusUpdate.articleID), zoneID: zoneID) let recordID = CKRecord.ID(recordName: statusID(statusUpdate.articleID), zoneID: zoneID)
let record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID) let record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID)
if let feedExternalID = statusUpdate.article?.feed?.externalID { if let article = statusUpdate.article, let feedExternalID = feedInfoDelegate?.feedExternalID(article: article) {
record[CloudKitArticleStatus.Fields.feedExternalID] = feedExternalID record[CloudKitArticleStatus.Fields.feedExternalID] = feedExternalID
} }
@ -228,7 +216,7 @@ private extension CloudKitArticlesZone {
let articleStatusRecordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: zoneID) let articleStatusRecordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: zoneID)
record[CloudKitArticle.Fields.articleStatus] = CKRecord.Reference(recordID: articleStatusRecordID, action: .deleteSelf) record[CloudKitArticle.Fields.articleStatus] = CKRecord.Reference(recordID: articleStatusRecordID, action: .deleteSelf)
record[CloudKitArticle.Fields.feedURL] = article.feed?.url record[CloudKitArticle.Fields.feedURL] = feedInfoDelegate?.feedURL(article: article)
record[CloudKitArticle.Fields.uniqueID] = article.uniqueID record[CloudKitArticle.Fields.uniqueID] = article.uniqueID
record[CloudKitArticle.Fields.title] = article.title record[CloudKitArticle.Fields.title] = article.title
record[CloudKitArticle.Fields.contentHTML] = article.contentHTML record[CloudKitArticle.Fields.contentHTML] = article.contentHTML