@@ -305,52 +305,56 @@ final class NotificationSyncMediator: NotificationSyncMediatorProtocol {
305
305
/// Deletes the note with the given ID from Core Data.
306
306
///
307
307
func deleteNote( noteID: String ) {
308
- Self . operationQueue. addOperation ( AsyncBlockOperation { [ contextManager] done in
308
+ Self . operationQueue. addOperation ( AsyncBlockOperation { [ contextManager] operationCompletion in
309
309
contextManager. performAndSave ( { context in
310
310
let predicate = NSPredicate ( format: " (notificationId == %@) " , noteID)
311
311
312
312
for orphan in context. allObjects ( ofType: Notification . self, matching: predicate) {
313
313
context. deleteObject ( orphan)
314
314
}
315
- } , completion: done , on: . main)
315
+ } , completion: operationCompletion , on: . main)
316
316
} )
317
317
}
318
318
319
319
/// Invalidates the local cache for the notification with the specified ID.
320
320
///
321
- func invalidateCacheForNotification( _ noteID: String ) {
322
- invalidateCacheForNotifications ( [ noteID] )
321
+ func invalidateCacheForNotification( _ noteID: String ,
322
+ completion: ( ( ) -> Void ) ? = nil ) {
323
+ invalidateCacheForNotifications ( [ noteID] , completion: completion)
323
324
}
324
325
325
326
/// Invalidates the local cache for all the notifications with specified ID's in the array.
326
327
///
327
- func invalidateCacheForNotifications( _ noteIDs: [ String ] ) {
328
- Self . operationQueue. addOperation ( AsyncBlockOperation { [ contextManager] done in
328
+ func invalidateCacheForNotifications( _ noteIDs: [ String ] ,
329
+ completion: ( ( ) -> Void ) ? = nil ) {
330
+ Self . operationQueue. addOperation ( AsyncBlockOperation { [ contextManager] operationCompletion in
329
331
contextManager. performAndSave ( { context in
330
332
let predicate = NSPredicate ( format: " (notificationId IN %@) " , noteIDs)
331
333
let notifications = context. allObjects ( ofType: Notification . self, matching: predicate)
332
334
333
335
notifications. forEach { $0. notificationHash = nil }
334
- } , completion: done, on: . main)
336
+ } , completion: {
337
+ completion ? ( )
338
+ operationCompletion ( )
339
+ } , on: . main)
335
340
} )
336
341
}
337
342
338
343
func toggleLikeForPostNotification( isLike: Bool ,
339
344
postID: UInt ,
340
345
siteID: UInt ,
341
346
completion: @escaping ( Result < Bool , Swift . Error > ) -> Void ) {
347
+ let success = { [ weak self] ( ) -> Void in
348
+ self ? . updatePostLikeStatusLocally ( isLike: isLike, postID: postID, siteID: siteID, completion: completion)
349
+ }
342
350
if isLike {
343
- readerRemoteService. likePost ( postID, forSite: siteID) {
344
- completion ( . success( isLike) )
345
- } failure: { error in
351
+ readerRemoteService. likePost ( postID, forSite: siteID, success: success, failure: { error in
346
352
completion ( . failure( error ?? ServiceError . unknown) )
347
- }
353
+ } )
348
354
} else {
349
- readerRemoteService. unlikePost ( postID, forSite: siteID) {
350
- completion ( . success( isLike) )
351
- } failure: { error in
355
+ readerRemoteService. unlikePost ( postID, forSite: siteID, success: success, failure: { error in
352
356
completion ( . failure( error ?? ServiceError . unknown) )
353
- }
357
+ } )
354
358
}
355
359
}
356
360
@@ -359,16 +363,15 @@ final class NotificationSyncMediator: NotificationSyncMediatorProtocol {
359
363
siteID: UInt ,
360
364
completion: @escaping ( Result < Bool , Swift . Error > ) -> Void ) {
361
365
let commentService = commentRemoteFactory. restRemote ( siteID: NSNumber ( value: siteID) , api: restAPI)
366
+ let success = { [ weak self] ( ) -> Void in
367
+ self ? . updateCommentLikeStatusLocally ( isLike: isLike, commentID: commentID, siteID: siteID, completion: completion)
368
+ }
362
369
if isLike {
363
- commentService. likeComment ( withID: NSNumber ( value: commentID) ) {
364
- completion ( . success( isLike) )
365
- } failure: { error in
370
+ commentService. likeComment ( withID: NSNumber ( value: commentID) , success: success) { error in
366
371
completion ( . failure( error ?? ServiceError . unknown) )
367
372
}
368
373
} else {
369
- commentService. unlikeComment ( withID: NSNumber ( value: commentID) ) {
370
- completion ( . success( isLike) )
371
- } failure: { error in
374
+ commentService. unlikeComment ( withID: NSNumber ( value: commentID) , success: success) { error in
372
375
completion ( . failure( error ?? ServiceError . unknown) )
373
376
}
374
377
}
@@ -386,7 +389,7 @@ private extension NotificationSyncMediator {
386
389
/// - completion: Callback to be executed on completion
387
390
///
388
391
func determineUpdatedNotes( with remoteHashes: [ RemoteNotification ] , completion: @escaping ( ( [ String ] ) -> Void ) ) {
389
- Self . operationQueue. addOperation ( AsyncBlockOperation { [ contextManager] done in
392
+ Self . operationQueue. addOperation ( AsyncBlockOperation { [ contextManager] operationCompletion in
390
393
contextManager. performAndSave ( { context in
391
394
let remoteIds = remoteHashes. map { $0. notificationId }
392
395
let predicate = NSPredicate ( format: " (notificationId IN %@) " , remoteIds)
@@ -404,7 +407,7 @@ private extension NotificationSyncMediator {
404
407
. map { $0. notificationId }
405
408
} , completion: { outdatedIds in
406
409
completion ( outdatedIds)
407
- done ( )
410
+ operationCompletion ( )
408
411
} , on: . main)
409
412
} )
410
413
}
@@ -417,7 +420,7 @@ private extension NotificationSyncMediator {
417
420
/// - completion: Callback to be executed on completion
418
421
///
419
422
func updateLocalNotes( with remoteNotes: [ RemoteNotification ] , completion: ( ( ) -> Void ) ? = nil ) {
420
- Self . operationQueue. addOperation ( AsyncBlockOperation { [ contextManager] done in
423
+ Self . operationQueue. addOperation ( AsyncBlockOperation { [ contextManager] operationCompletion in
421
424
contextManager. performAndSave ( { context in
422
425
for remoteNote in remoteNotes {
423
426
let predicate = NSPredicate ( format: " (notificationId == %@) " , remoteNote. notificationId)
@@ -426,7 +429,7 @@ private extension NotificationSyncMediator {
426
429
localNote. update ( with: remoteNote)
427
430
}
428
431
} , completion: {
429
- done ( )
432
+ operationCompletion ( )
430
433
DispatchQueue . main. async {
431
434
completion ? ( )
432
435
}
@@ -440,7 +443,7 @@ private extension NotificationSyncMediator {
440
443
/// - Parameter remoteHashes: Collection of remoteNotifications.
441
444
///
442
445
func deleteLocalMissingNotes( from remoteHashes: [ RemoteNotification ] , completion: @escaping ( ( ) -> Void ) ) {
443
- Self . operationQueue. addOperation ( AsyncBlockOperation { [ contextManager] done in
446
+ Self . operationQueue. addOperation ( AsyncBlockOperation { [ contextManager] operationCompletion in
444
447
contextManager. performAndSave ( { context in
445
448
let remoteIds = remoteHashes. map { $0. notificationId }
446
449
let predicate = NSPredicate ( format: " NOT (notificationId IN %@) " , remoteIds)
@@ -449,7 +452,7 @@ private extension NotificationSyncMediator {
449
452
context. deleteObject ( orphan)
450
453
}
451
454
} , completion: {
452
- done ( )
455
+ operationCompletion ( )
453
456
DispatchQueue . main. async {
454
457
completion ( )
455
458
}
@@ -495,11 +498,74 @@ private extension NotificationSyncMediator {
495
498
let notificationCenter = NotificationCenter . default
496
499
notificationCenter. post ( name: Foundation . Notification. Name ( rawValue: NotificationSyncMediatorDidUpdateNotifications) , object: nil )
497
500
}
501
+
502
+ /// Attempts to fetch a `Comment` object matching the comment and site IDs from the local cache
503
+ /// If found, the like status is updated. If not found, nothing happens
504
+ /// - Parameters:
505
+ /// - isLike: Indicates whether this is a like or unlike
506
+ /// - commentID: Comment identifier used to fetch the comment
507
+ /// - siteID: Site identifier used to fetch the comment
508
+ /// - completion: Callback block which is called when the local comment is updated.
509
+ func updateCommentLikeStatusLocally( isLike: Bool ,
510
+ commentID: UInt ,
511
+ siteID: UInt ,
512
+ completion: @escaping ( Result < Bool , Swift . Error > ) -> Void ) {
513
+ contextManager. performAndSave ( { context in
514
+ do {
515
+ let fetchRequest = NSFetchRequest < Comment > ( entityName: Comment . entityName ( ) )
516
+ fetchRequest. fetchLimit = 1
517
+ let commentIDPredicate = NSPredicate ( format: " \( #keyPath( Comment . commentID) ) == %d " , commentID)
518
+ let siteIDPredicate = NSPredicate ( format: " blog.blogID = %@ " , NSNumber ( value: siteID) )
519
+ fetchRequest. predicate = NSCompoundPredicate ( andPredicateWithSubpredicates: [ commentIDPredicate, siteIDPredicate] )
520
+ if let comment = try context. fetch ( fetchRequest) . first {
521
+ comment. isLiked = isLike
522
+ comment. likeCount = comment. likeCount + ( comment. isLiked ? 1 : - 1 )
523
+ }
524
+ }
525
+ catch {
526
+ completion ( . failure( ServiceError . localPersistenceError) )
527
+ }
528
+ } , completion: {
529
+ completion ( . success( isLike) )
530
+ } , on: . main)
531
+ }
532
+
533
+ /// Attempts to fetch a `ReaderPost` object matching the post and site IDs from the local cache
534
+ /// If found, the like status is updated. If not found, nothing happens
535
+ /// - Parameters:
536
+ /// - isLike: Indicates whether this is a like or unlike
537
+ /// - postID: Post identifier used to fetch the post
538
+ /// - siteID: Site identifier used to fetch the post
539
+ /// - completion: Callback block which is called when the local post is updated.
540
+ func updatePostLikeStatusLocally( isLike: Bool ,
541
+ postID: UInt ,
542
+ siteID: UInt ,
543
+ completion: @escaping ( Result < Bool , Swift . Error > ) -> Void ) {
544
+ contextManager. performAndSave ( { context in
545
+ do {
546
+ let fetchRequest = NSFetchRequest < ReaderPost > ( entityName: ReaderPost . entityName ( ) )
547
+ fetchRequest. fetchLimit = 1
548
+ let commentIDPredicate = NSPredicate ( format: " \( #keyPath( ReaderPost . postID) ) == %d " , postID)
549
+ let siteIDPredicate = NSPredicate ( format: " \( #keyPath( ReaderPost . siteID) ) = %@ " , NSNumber ( value: siteID) )
550
+ fetchRequest. predicate = NSCompoundPredicate ( andPredicateWithSubpredicates: [ commentIDPredicate, siteIDPredicate] )
551
+ if let post = try context. fetch ( fetchRequest) . first {
552
+ post. isLiked = isLike
553
+ post. likeCount = NSNumber ( value: post. likeCount. intValue + ( post. isLiked ? 1 : - 1 ) )
554
+ }
555
+ }
556
+ catch {
557
+ completion ( . failure( ServiceError . localPersistenceError) )
558
+ }
559
+ } , completion: {
560
+ completion ( . success( isLike) )
561
+ } , on: . main)
562
+ }
498
563
}
499
564
500
565
extension NotificationSyncMediator {
501
566
502
567
enum ServiceError : Error {
503
568
case unknown
569
+ case localPersistenceError
504
570
}
505
571
}
0 commit comments