Update sync code to handle async selectForProcessing.

This commit is contained in:
Brent Simmons 2019-12-16 12:49:07 -08:00
parent 7b7346d010
commit 3981312d6f
4 changed files with 147 additions and 106 deletions

View File

@ -195,24 +195,34 @@ final class FeedWranglerAccountDelegate: AccountDelegate {
} }
} }
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) { func sendArticleStatus(for account: Account, completion: @escaping VoidResultCompletionBlock) {
os_log(.debug, log: log, "Sending article status...") os_log(.debug, log: log, "Sending article status...")
let syncStatuses = database.selectForProcessing() database.selectForProcessing { result in
let articleStatuses = Dictionary(grouping: syncStatuses, by: { $0.articleID })
let group = DispatchGroup() func processStatuses(_ syncStatuses: [SyncStatus]) {
let articleStatuses = Dictionary(grouping: syncStatuses, by: { $0.articleID })
articleStatuses.forEach { articleID, statuses in let group = DispatchGroup()
group.enter()
caller.updateArticleStatus(articleID, statuses) { articleStatuses.forEach { articleID, statuses in
group.leave() group.enter()
self.caller.updateArticleStatus(articleID, statuses) {
group.leave()
}
}
group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done sending article statuses.")
completion(.success(()))
}
}
switch result {
case .success(let syncStatuses):
processStatuses(syncStatuses)
case .failure(let databaseError):
completion(.failure(databaseError))
} }
}
group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done sending article statuses.")
completion(.success(()))
} }
} }

View File

@ -109,57 +109,67 @@ final class FeedbinAccountDelegate: AccountDelegate {
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) { func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
os_log(.debug, log: log, "Sending article statuses...") os_log(.debug, log: log, "Sending article statuses...")
let syncStatuses = database.selectForProcessing()
let createUnreadStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.read && $0.flag == false }
let deleteUnreadStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.read && $0.flag == true }
let createStarredStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.starred && $0.flag == true }
let deleteStarredStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.starred && $0.flag == false }
let group = DispatchGroup() database.selectForProcessing { result in
var errorOccurred = false
group.enter() func processStatuses(_ syncStatuses: [SyncStatus]) {
sendArticleStatuses(createUnreadStatuses, apiCall: caller.createUnreadEntries) { result in let createUnreadStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.read && $0.flag == false }
group.leave() let deleteUnreadStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.read && $0.flag == true }
if case .failure = result { let createStarredStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.starred && $0.flag == true }
errorOccurred = true let deleteStarredStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.starred && $0.flag == false }
let group = DispatchGroup()
var errorOccurred = false
group.enter()
self.sendArticleStatuses(createUnreadStatuses, apiCall: self.caller.createUnreadEntries) { result in
group.leave()
if case .failure = result {
errorOccurred = true
}
}
group.enter()
self.sendArticleStatuses(deleteUnreadStatuses, apiCall: self.caller.deleteUnreadEntries) { result in
group.leave()
if case .failure = result {
errorOccurred = true
}
}
group.enter()
self.sendArticleStatuses(createStarredStatuses, apiCall: self.caller.createStarredEntries) { result in
group.leave()
if case .failure = result {
errorOccurred = true
}
}
group.enter()
self.sendArticleStatuses(deleteStarredStatuses, apiCall: self.caller.deleteStarredEntries) { result in
group.leave()
if case .failure = result {
errorOccurred = true
}
}
group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done sending article statuses.")
if errorOccurred {
completion(.failure(FeedbinAccountDelegateError.unknown))
} else {
completion(.success(()))
}
}
}
switch result {
case .success(let syncStatuses):
processStatuses(syncStatuses)
case .failure(let databaseError):
completion(.failure(databaseError))
} }
} }
group.enter()
sendArticleStatuses(deleteUnreadStatuses, apiCall: caller.deleteUnreadEntries) { result in
group.leave()
if case .failure = result {
errorOccurred = true
}
}
group.enter()
sendArticleStatuses(createStarredStatuses, apiCall: caller.createStarredEntries) { result in
group.leave()
if case .failure = result {
errorOccurred = true
}
}
group.enter()
sendArticleStatuses(deleteStarredStatuses, apiCall: caller.deleteStarredEntries) { result in
group.leave()
if case .failure = result {
errorOccurred = true
}
}
group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done sending article statuses.")
if errorOccurred {
completion(.failure(FeedbinAccountDelegateError.unknown))
} else {
completion(.success(()))
}
}
} }
func refreshArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) { func refreshArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
@ -531,7 +541,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
} }
} }
return account.update(articles, statusKey: statusKey, flag: flag) return account.update(articles, statusKey: statusKey, flag: flag)
} }
func accountDidInitialize(_ account: Account) { func accountDidInitialize(_ account: Account) {

View File

@ -30,24 +30,36 @@ final class FeedlySendArticleStatusesOperation: FeedlyOperation {
} }
os_log(.debug, log: log, "Sending article statuses...") os_log(.debug, log: log, "Sending article statuses...")
let pending = database.selectForProcessing() database.selectForProcessing { result in
switch result {
case .success(let syncStatuses):
self.processStatuses(syncStatuses)
case .failure:
self.didFinish()
}
}
}
}
private extension FeedlySendArticleStatusesOperation {
func processStatuses(_ pending: [SyncStatus]) {
let statuses: [(status: ArticleStatus.Key, flag: Bool, action: FeedlyMarkAction)] = [ let statuses: [(status: ArticleStatus.Key, flag: Bool, action: FeedlyMarkAction)] = [
(.read, false, .unread), (.read, false, .unread),
(.read, true, .read), (.read, true, .read),
(.starred, true, .saved), (.starred, true, .saved),
(.starred, false, .unsaved), (.starred, false, .unsaved),
] ]
let group = DispatchGroup() let group = DispatchGroup()
for pairing in statuses { for pairing in statuses {
let articleIds = pending.filter { $0.key == pairing.status && $0.flag == pairing.flag } let articleIds = pending.filter { $0.key == pairing.status && $0.flag == pairing.flag }
guard !articleIds.isEmpty else { guard !articleIds.isEmpty else {
continue continue
} }
let ids = Set(articleIds.map { $0.articleID }) let ids = Set(articleIds.map { $0.articleID })
let database = self.database let database = self.database
group.enter() group.enter()
@ -55,17 +67,17 @@ final class FeedlySendArticleStatusesOperation: FeedlyOperation {
assert(Thread.isMainThread) assert(Thread.isMainThread)
switch result { switch result {
case .success: case .success:
database.deleteSelectedForProcessing(Array(ids)) { database.deleteSelectedForProcessing(Array(ids)) { _ in
group.leave() group.leave()
} }
case .failure: case .failure:
database.resetSelectedForProcessing(Array(ids)) { database.resetSelectedForProcessing(Array(ids)) { _ in
group.leave() group.leave()
} }
} }
} }
} }
group.notify(queue: DispatchQueue.main) { group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done sending article statuses.") os_log(.debug, log: self.log, "Done sending article statuses.")
self.didFinish() self.didFinish()

View File

@ -113,42 +113,51 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
} }
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) { func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
os_log(.debug, log: log, "Sending article statuses...") os_log(.debug, log: log, "Sending article statuses...")
let syncStatuses = database.selectForProcessing()
let createUnreadStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.read && $0.flag == false }
let deleteUnreadStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.read && $0.flag == true }
let createStarredStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.starred && $0.flag == true }
let deleteStarredStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.starred && $0.flag == false }
let group = DispatchGroup() database.selectForProcessing { result in
group.enter() func processStatuses(_ syncStatuses: [SyncStatus]) {
sendArticleStatuses(createUnreadStatuses, apiCall: caller.createUnreadEntries) { let createUnreadStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.read && $0.flag == false }
group.leave() let deleteUnreadStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.read && $0.flag == true }
let createStarredStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.starred && $0.flag == true }
let deleteStarredStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.starred && $0.flag == false }
let group = DispatchGroup()
group.enter()
self.sendArticleStatuses(createUnreadStatuses, apiCall: self.caller.createUnreadEntries) {
group.leave()
}
group.enter()
self.sendArticleStatuses(deleteUnreadStatuses, apiCall: self.caller.deleteUnreadEntries) {
group.leave()
}
group.enter()
self.sendArticleStatuses(createStarredStatuses, apiCall: self.caller.createStarredEntries) {
group.leave()
}
group.enter()
self.sendArticleStatuses(deleteStarredStatuses, apiCall: self.caller.deleteStarredEntries) {
group.leave()
}
group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done sending article statuses.")
completion(.success(()))
}
}
switch result {
case .success(let syncStatuses):
processStatuses(syncStatuses)
case .failure(let databaseError):
completion(.failure(databaseError))
}
} }
group.enter()
sendArticleStatuses(deleteUnreadStatuses, apiCall: caller.deleteUnreadEntries) {
group.leave()
}
group.enter()
sendArticleStatuses(createStarredStatuses, apiCall: caller.createStarredEntries) {
group.leave()
}
group.enter()
sendArticleStatuses(deleteStarredStatuses, apiCall: caller.deleteStarredEntries) {
group.leave()
}
group.notify(queue: DispatchQueue.main) {
os_log(.debug, log: self.log, "Done sending article statuses.")
completion(.success(()))
}
} }
func refreshArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) { func refreshArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {