Add send statuses to CloudKit.
This commit is contained in:
parent
9a1b7f5225
commit
44231937cd
|
@ -56,6 +56,8 @@
|
|||
5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */; };
|
||||
5170743C232AEDB500A461A3 /* OPMLFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5170743B232AEDB500A461A3 /* OPMLFile.swift */; };
|
||||
519E84A62433D49000D238B0 /* OPMLNormalizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84A52433D49000D238B0 /* OPMLNormalizer.swift */; };
|
||||
519E84A82434C5EF00D238B0 /* CloudKitArticlesZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */; };
|
||||
519E84AA2434C60400D238B0 /* CloudKitArticlesZoneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E84A92434C60400D238B0 /* CloudKitArticlesZoneDelegate.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 */; };
|
||||
|
@ -290,6 +292,8 @@
|
|||
5170743B232AEDB500A461A3 /* OPMLFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLFile.swift; sourceTree = "<group>"; };
|
||||
518B2EA52351306200400001 /* Account_project_test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_test.xcconfig; sourceTree = "<group>"; };
|
||||
519E84A52433D49000D238B0 /* OPMLNormalizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLNormalizer.swift; sourceTree = "<group>"; };
|
||||
519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitArticlesZone.swift; sourceTree = "<group>"; };
|
||||
519E84A92434C60400D238B0 /* CloudKitArticlesZoneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitArticlesZoneDelegate.swift; sourceTree = "<group>"; };
|
||||
51BB7B83233531BC008E8144 /* AccountBehaviors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountBehaviors.swift; sourceTree = "<group>"; };
|
||||
51BC8FCB237EC055004F8B56 /* Feed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = "<group>"; };
|
||||
51BFDECD238B508D00216323 /* ContainerIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerIdentifier.swift; sourceTree = "<group>"; };
|
||||
|
@ -522,6 +526,8 @@
|
|||
5103A9D82422546800410853 /* CloudKitAccountDelegate.swift */,
|
||||
51E4DB2F2426353D0091EB5B /* CloudKitAccountZone.swift */,
|
||||
512DD4CC2431098700C17B1F /* CloudKitAccountZoneDelegate.swift */,
|
||||
519E84A72434C5EF00D238B0 /* CloudKitArticlesZone.swift */,
|
||||
519E84A92434C60400D238B0 /* CloudKitArticlesZoneDelegate.swift */,
|
||||
51E4DB2D242633ED0091EB5B /* CloudKitZone.swift */,
|
||||
51C034DE242D65D20014DC71 /* CloudKitZoneResult.swift */,
|
||||
);
|
||||
|
@ -1085,6 +1091,7 @@
|
|||
9EA643D5239306AC0018A28C /* FeedlyFeedsSearchResponse.swift in Sources */,
|
||||
9EAEC60E2332FEC20085D7C9 /* FeedlyFeed.swift in Sources */,
|
||||
5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */,
|
||||
519E84AA2434C60400D238B0 /* CloudKitArticlesZoneDelegate.swift in Sources */,
|
||||
512DD4CB2431000600C17B1F /* CKRecord+Extensions.swift in Sources */,
|
||||
3B826DAF2385C81C00FC1ADB /* FeedWranglerGenericResult.swift in Sources */,
|
||||
9ECC9A85234DC16E009B5144 /* FeedlyAccountDelegateError.swift in Sources */,
|
||||
|
@ -1165,6 +1172,7 @@
|
|||
51BB7B84233531BC008E8144 /* AccountBehaviors.swift in Sources */,
|
||||
9E1773D923458D590056A5A8 /* FeedlyResourceId.swift in Sources */,
|
||||
9EE4CCFA234F106600FBAE4B /* FeedlyFeedContainerValidator.swift in Sources */,
|
||||
519E84A82434C5EF00D238B0 /* CloudKitArticlesZone.swift in Sources */,
|
||||
552032FC229D5D5A009559E0 /* ReaderAPIUnreadEntry.swift in Sources */,
|
||||
9EC688EA232B973C00A8D0A2 /* FeedlyAPICaller.swift in Sources */,
|
||||
9E1773D32345700F0056A5A8 /* FeedlyLink.swift in Sources */,
|
||||
|
|
|
@ -30,8 +30,9 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||
return CKContainer(identifier: "iCloud.\(orgID).NetNewsWire")
|
||||
}()
|
||||
|
||||
private lazy var zones = [accountZone]
|
||||
private lazy var zones: [CloudKitZone] = [accountZone, articlesZone]
|
||||
private let accountZone: CloudKitAccountZone
|
||||
private let articlesZone: CloudKitArticlesZone
|
||||
|
||||
private let refresher = LocalAccountRefresher()
|
||||
|
||||
|
@ -48,8 +49,11 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||
|
||||
init(dataFolder: String) {
|
||||
accountZone = CloudKitAccountZone(container: container)
|
||||
articlesZone = CloudKitArticlesZone(container: container)
|
||||
|
||||
let databaseFilePath = (dataFolder as NSString).appendingPathComponent("Sync.sqlite3")
|
||||
database = SyncDatabase(databaseFilePath: databaseFilePath)
|
||||
|
||||
accountZone.refreshProgress = refreshProgress
|
||||
}
|
||||
|
||||
|
@ -75,10 +79,29 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||
accountZone.fetchChangesInZone() { result in
|
||||
switch result {
|
||||
case .success:
|
||||
self.refresher.refreshFeeds(account.flattenedWebFeeds()) {
|
||||
BatchUpdate.shared.end()
|
||||
account.metadata.lastArticleFetchEndTime = Date()
|
||||
completion(.success(()))
|
||||
|
||||
self.sendArticleStatus(for: account) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
|
||||
self.refreshArticleStatus(for: account) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
|
||||
self.refresher.refreshFeeds(account.flattenedWebFeeds()) {
|
||||
BatchUpdate.shared.end()
|
||||
account.metadata.lastArticleFetchEndTime = Date()
|
||||
completion(.success(()))
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
case .failure(let error):
|
||||
BatchUpdate.shared.end()
|
||||
|
@ -88,11 +111,44 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||
}
|
||||
|
||||
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
|
||||
completion(.success(()))
|
||||
os_log(.debug, log: log, "Sending article statuses...")
|
||||
|
||||
database.selectForProcessing { result in
|
||||
|
||||
func processStatuses(_ syncStatuses: [SyncStatus]) {
|
||||
self.articlesZone.sendArticleStatus(syncStatuses) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
os_log(.debug, log: self.log, "Done sending article statuses.")
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .success(let syncStatuses):
|
||||
processStatuses(syncStatuses)
|
||||
case .failure(let databaseError):
|
||||
completion(.failure(databaseError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func refreshArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
|
||||
completion(.success(()))
|
||||
os_log(.debug, log: log, "Refreshing article statuses...")
|
||||
|
||||
articlesZone.fetchChangesInZone() { result in
|
||||
os_log(.debug, log: self.log, "Done refreshing article statuses.")
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
@ -353,6 +409,7 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||
|
||||
func accountDidInitialize(_ account: Account) {
|
||||
accountZone.delegate = CloudKitAcountZoneDelegate(account: account, refreshProgress: refreshProgress)
|
||||
articlesZone.delegate = CloudKitArticlesZoneDelegate(account: account)
|
||||
|
||||
if account.externalID == nil {
|
||||
accountZone.findOrCreateAccount() { result in
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// CloudKitArticlesZone.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Maurice Parker on 4/1/20.
|
||||
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
import RSWeb
|
||||
import CloudKit
|
||||
import SyncDatabase
|
||||
|
||||
final class CloudKitArticlesZone: CloudKitZone {
|
||||
|
||||
|
||||
static var zoneID: CKRecordZone.ID {
|
||||
return CKRecordZone.ID(zoneName: "Articles", ownerName: CKCurrentUserDefaultName)
|
||||
}
|
||||
|
||||
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
|
||||
|
||||
weak var container: CKContainer?
|
||||
weak var database: CKDatabase?
|
||||
weak var refreshProgress: DownloadProgress? = nil
|
||||
var delegate: CloudKitZoneDelegate? = nil
|
||||
|
||||
struct CloudKitArticleStatus {
|
||||
static let recordType = "ArticleStatus"
|
||||
struct Fields {
|
||||
static let read = "read"
|
||||
static let starred = "starred"
|
||||
static let userDeleted = "userDeleted"
|
||||
}
|
||||
}
|
||||
|
||||
init(container: CKContainer) {
|
||||
self.container = container
|
||||
self.database = container.privateCloudDatabase
|
||||
}
|
||||
|
||||
func sendArticleStatus(_ syncStatuses: [SyncStatus], completion: @escaping ((Result<Void, Error>) -> Void)) {
|
||||
var records = [String: CKRecord]()
|
||||
|
||||
for status in syncStatuses {
|
||||
|
||||
var record = records[status.articleID]
|
||||
if record == nil {
|
||||
let recordID = CKRecord.ID(recordName: status.articleID, zoneID: Self.zoneID)
|
||||
record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID)
|
||||
records[status.articleID] = record
|
||||
}
|
||||
|
||||
switch status.key {
|
||||
case .read:
|
||||
record![CloudKitArticleStatus.Fields.read] = status.flag ? "1" : "0"
|
||||
case .starred:
|
||||
record![CloudKitArticleStatus.Fields.starred] = status.flag ? "1" : "0"
|
||||
case .userDeleted:
|
||||
record![CloudKitArticleStatus.Fields.userDeleted] = status.flag ? "1" : "0"
|
||||
}
|
||||
}
|
||||
|
||||
modify(recordsToSave: Array(records.values), recordIDsToDelete: [], completion: completion)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// CloudKitArticlesZoneDelegate.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Maurice Parker on 4/1/20.
|
||||
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os.log
|
||||
import CloudKit
|
||||
|
||||
class CloudKitArticlesZoneDelegate: CloudKitZoneDelegate {
|
||||
|
||||
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
|
||||
|
||||
weak var account: Account?
|
||||
|
||||
init(account: Account) {
|
||||
self.account = account
|
||||
}
|
||||
|
||||
func cloudKitDidChange(record: CKRecord) {
|
||||
// switch record.recordType {
|
||||
// case CloudKitAccountZone.CloudKitWebFeed.recordType:
|
||||
// default:
|
||||
// assertionFailure("Unknown record type: \(record.recordType)")
|
||||
// }
|
||||
}
|
||||
|
||||
func cloudKitDidDelete(recordType: CKRecord.RecordType, recordID: CKRecord.ID) {
|
||||
// Article downloads clean up old articles and statuses
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue