diff --git a/WordPress/Classes/Services/MediaCoordinator.swift b/WordPress/Classes/Services/MediaCoordinator.swift index 51fb39421019..6dd41a8c1142 100644 --- a/WordPress/Classes/Services/MediaCoordinator.swift +++ b/WordPress/Classes/Services/MediaCoordinator.swift @@ -5,11 +5,7 @@ import WordPressFlux /// items, independently of a specific view controller. It should be accessed /// via the `shared` singleton. /// -class MediaCoordinator: NSObject, Uploader { - func resume() { - } - - +class MediaCoordinator: NSObject { @objc static let shared = MediaCoordinator() private(set) var backgroundContext: NSManagedObjectContext = { @@ -606,6 +602,20 @@ extension MediaCoordinator: MediaProgressCoordinatorDelegate { } } +extension MediaCoordinator: Uploader { + func resume() { + let service = MediaService(managedObjectContext: mainContext) + + service.getFailedMedia { [weak self] media in + guard let self = self else { + return + } + + media.forEach() { self.retryMedia($0) } + } + } +} + extension Media { var uploadID: String { return objectID.uriRepresentation().absoluteString diff --git a/WordPress/Classes/Services/MediaService.h b/WordPress/Classes/Services/MediaService.h index 7bdccb53987c..c993e8d103fd 100644 --- a/WordPress/Classes/Services/MediaService.h +++ b/WordPress/Classes/Services/MediaService.h @@ -37,6 +37,12 @@ typedef NS_ERROR_ENUM(MediaServiceErrorDomain, MediaServiceError) { thumbnailCallback:(nullable void (^)(Media * __nonnull media, NSURL * __nonnull thumbnailURL))thumbnailCallback completion:(nullable void (^)(Media * __nullable media, NSError * __nullable error))completion; +/** + Get all Media that failed to upload. + + @param result a block that will be invoked to return the requested media. + */ +- (void)getFailedMedia:(nonnull void (^)( NSArray* _Nonnull media))result; /** Get the Media object from the server using the blog and the mediaID as the identifier of the resource diff --git a/WordPress/Classes/Services/MediaService.m b/WordPress/Classes/Services/MediaService.m index f2103300cacd..ee6c19d9ced1 100644 --- a/WordPress/Classes/Services/MediaService.m +++ b/WordPress/Classes/Services/MediaService.m @@ -509,6 +509,24 @@ - (void)deleteMedia:(nonnull NSArray *)mediaObjects #pragma mark - Getting media +- (void)getFailedMedia:(void (^)( NSArray* media))result { + [self.managedObjectContext performBlock:^{ + NSString *entityName = NSStringFromClass([Media class]); + NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName]; + + request.predicate = [NSPredicate predicateWithFormat:@"remoteStatusNumber == %d", MediaRemoteStatusFailed]; + + NSError *error = nil; + NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error]; + + if (!results) { + result(@[]); + } else { + result(results); + } + }]; +} + - (void) getMediaWithID:(NSNumber *) mediaID inBlog:(Blog *) blog success:(void (^)(Media *media))success failure:(void (^)(NSError *error))failure diff --git a/WordPress/Classes/Services/PostCoordinator.swift b/WordPress/Classes/Services/PostCoordinator.swift index d555b727f9ea..fb5785da8b00 100644 --- a/WordPress/Classes/Services/PostCoordinator.swift +++ b/WordPress/Classes/Services/PostCoordinator.swift @@ -248,9 +248,14 @@ class PostCoordinator: NSObject { extension PostCoordinator: Uploader { func resume() { - // Resume the upload of all posts that are not synched. - // - // 1. Query posts with status == .failed - // 2. Call retrySave() for each post + let service = PostService(managedObjectContext: mainContext) + + service.getFailedPosts { [weak self] posts in + guard let self = self else { + return + } + + posts.forEach() { self.retrySave(of: $0 ) } + } } } diff --git a/WordPress/Classes/Services/PostService.h b/WordPress/Classes/Services/PostService.h index 7ae2ad6fad02..f9b20b36fed9 100644 --- a/WordPress/Classes/Services/PostService.h +++ b/WordPress/Classes/Services/PostService.h @@ -43,6 +43,13 @@ extern const NSUInteger PostServiceDefaultNumberToSync; success:(void (^)(AbstractPost *post))success failure:(void (^)(NSError *))failure; +/** + Get all posts that failed to upload. + + @param result a block that will be invoked to return the requested posts. + */ +- (void)getFailedPosts:(nonnull void (^)( NSArray* _Nonnull posts))result; + /** Sync an initial batch of posts from the specified blog. Please note that success and/or failure are called in the context of the diff --git a/WordPress/Classes/Services/PostService.m b/WordPress/Classes/Services/PostService.m index 664a9b9031ee..8a12ef61575a 100644 --- a/WordPress/Classes/Services/PostService.m +++ b/WordPress/Classes/Services/PostService.m @@ -65,6 +65,26 @@ - (Page *)createDraftPageForBlog:(Blog *)blog { return page; } + +- (void)getFailedPosts:(void (^)( NSArray* posts))result { + [self.managedObjectContext performBlock:^{ + NSString *entityName = NSStringFromClass([AbstractPost class]); + NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName]; + + request.predicate = [NSPredicate predicateWithFormat:@"remoteStatusNumber == %d", AbstractPostRemoteStatusFailed]; + + NSError *error = nil; + NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error]; + + if (!results) { + result(@[]); + } else { + result(results); + } + }]; +} + + - (void)getPostWithID:(NSNumber *)postID forBlog:(Blog *)blog success:(void (^)(AbstractPost *post))success diff --git a/WordPress/Classes/Uploads/UploadsManager.swift b/WordPress/Classes/Uploads/UploadsManager.swift index 646ca2c6865e..84f0bf612dd3 100644 --- a/WordPress/Classes/Uploads/UploadsManager.swift +++ b/WordPress/Classes/Uploads/UploadsManager.swift @@ -1,24 +1,11 @@ import Foundation +import Reachability /// Takes care of coordinating all uploaders used by the app. /// -@objc class UploadsManager: NSObject { private let uploaders: [Uploader] - private lazy var reachabilityObserver: NSObjectProtocol = { - return NotificationCenter.default.addObserver(forName: .reachabilityChanged, object: nil, queue: nil) { [weak self] notification in - - guard let self = self else { - return - } - - let internetIsReachable = notification.userInfo?[Foundation.Notification.reachabilityKey] as? Bool ?? false - - if internetIsReachable { - self.resume() - } - } - }() + private let reachability: Reachability = Reachability.forInternetConnection() // MARK: Initialization & Finalization @@ -31,17 +18,24 @@ class UploadsManager: NSObject { self.uploaders = uploaders super.init() + + setupReachableBlock() } - deinit { - NotificationCenter.default.removeObserver(reachabilityObserver) + // MARK: Reachability + + private func setupReachableBlock() { + reachability.reachableBlock = { _ in + self.resume() + } + + reachability.startNotifier() } // MARK: Interacting with Uploads /// Resumes all uploads handled by the uploaders. /// - @objc func resume() { for uploader in uploaders { uploader.resume()