Skip to content

Commit 69e5064

Browse files
committed
Implemented PFObject.deleteAll through PFObjectBatchController.
1 parent 9f23094 commit 69e5064

File tree

6 files changed

+237
-82
lines changed

6 files changed

+237
-82
lines changed

Parse/Internal/Object/BatchController/PFObjectBatchController.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#import "PFDataProvider.h"
1313

1414
@class BFTask;
15+
@class PFObject;
1516

1617
NS_ASSUME_NONNULL_BEGIN
1718

@@ -33,11 +34,18 @@ NS_ASSUME_NONNULL_BEGIN
3334

3435
- (BFTask *)fetchObjectsAsync:(nullable NSArray *)objects withSessionToken:(nullable NSString *)sessionToken;
3536

37+
///--------------------------------------
38+
/// @name Delete
39+
///--------------------------------------
40+
41+
- (BFTask *)deleteObjectsAsync:(nullable NSArray *)objects withSessionToken:(nullable NSString *)sessionToken;
42+
3643
///--------------------------------------
3744
/// @name Utilities
3845
///--------------------------------------
3946

4047
+ (nullable NSArray *)uniqueObjectsArrayFromArray:(nullable NSArray *)objects omitObjectsWithData:(BOOL)omitFetched;
48+
+ (NSArray *)uniqueObjectsArrayFromArray:(NSArray *)objects usingFilter:(BOOL (^)(PFObject *object))filter;
4149

4250
@end
4351

Parse/Internal/Object/BatchController/PFObjectBatchController.m

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
#import "PFObjectBatchController.h"
1111

12+
#import <Bolts/Bolts.h>
13+
1214
#import "BFTask+Private.h"
1315
#import "PFAssert.h"
1416
#import "PFCommandResult.h"
@@ -19,6 +21,8 @@
1921
#import "PFObjectPrivate.h"
2022
#import "PFQueryPrivate.h"
2123
#import "PFRESTQueryCommand.h"
24+
#import "PFRESTObjectCommand.h"
25+
#import "PFRESTObjectBatchCommand.h"
2226

2327
@implementation PFObjectBatchController
2428

@@ -98,10 +102,86 @@ - (BFTask *)_processFetchResultAsync:(NSDictionary *)result forObjects:(NSArray
98102
}];
99103
}
100104

105+
///--------------------------------------
106+
#pragma mark - Delete
107+
///--------------------------------------
108+
109+
- (BFTask *)deleteObjectsAsync:(NSArray *)objects withSessionToken:(NSString *)sessionToken {
110+
if (objects.count == 0) {
111+
return [BFTask taskWithResult:objects];
112+
}
113+
114+
@weakify(self);
115+
return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{
116+
@strongify(self);
117+
NSArray *objectBatches = [PFInternalUtils arrayBySplittingArray:objects
118+
withMaximumComponentsPerSegment:PFRESTObjectBatchCommandSubcommandsLimit];
119+
NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:objectBatches.count];
120+
for (NSArray *batch in objectBatches) {
121+
PFRESTCommand *command = [self _deleteCommandForObjects:batch withSessionToken:sessionToken];
122+
BFTask *task = [[self.dataSource.commandRunner runCommandAsync:command
123+
withOptions:PFCommandRunningOptionRetryIfFailed] continueWithSuccessBlock:^id(BFTask *task) {
124+
PFCommandResult *result = task.result;
125+
return [self _processDeleteResultsAsync:[result result] forObjects:batch];
126+
}];
127+
[tasks addObject:task];
128+
}
129+
return [[BFTask taskForCompletionOfAllTasks:tasks] continueWithBlock:^id(BFTask *task) {
130+
NSError *taskError = task.error;
131+
if (taskError && [taskError.domain isEqualToString:BFTaskErrorDomain]) {
132+
NSArray *taskErrors = taskError.userInfo[@"errors"];
133+
NSMutableArray *errors = [NSMutableArray array];
134+
for (NSError *error in taskErrors) {
135+
if ([error.domain isEqualToString:BFTaskErrorDomain]) {
136+
[errors addObjectsFromArray:error.userInfo[@"errors"]];
137+
} else {
138+
[errors addObject:error];
139+
}
140+
}
141+
return [BFTask taskWithError:[NSError errorWithDomain:BFTaskErrorDomain
142+
code:kBFMultipleErrorsError
143+
userInfo:@{ @"errors" : errors }]];
144+
}
145+
return task;
146+
}];
147+
}] continueWithSuccessResult:objects];
148+
}
149+
150+
- (PFRESTCommand *)_deleteCommandForObjects:(NSArray *)objects withSessionToken:(NSString *)sessionToken {
151+
NSMutableArray *commands = [NSMutableArray arrayWithCapacity:objects.count];
152+
for (PFObject *object in objects) {
153+
[object checkDeleteParams];
154+
PFRESTCommand *deleteCommand = [PFRESTObjectCommand deleteObjectCommandForObjectState:object._state
155+
withSessionToken:sessionToken];
156+
[commands addObject:deleteCommand];
157+
}
158+
return [PFRESTObjectBatchCommand batchCommandWithCommands:commands sessionToken:sessionToken];
159+
}
160+
161+
- (BFTask *)_processDeleteResultsAsync:(NSArray *)results forObjects:(NSArray *)objects {
162+
NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:results.count];
163+
[results enumerateObjectsUsingBlock:^(NSDictionary *result, NSUInteger idx, BOOL *stop) {
164+
PFObject *object = objects[idx];
165+
NSDictionary *errorResult = result[@"error"];
166+
NSDictionary *successResult = result[@"success"];
167+
168+
id<PFObjectControlling> controller = [[object class] objectController];
169+
BFTask *task = [controller processDeleteResultAsync:successResult forObject:object];
170+
if (errorResult) {
171+
task = [task continueWithBlock:^id(BFTask *task) {
172+
return [BFTask taskWithError:[PFErrorUtilities errorFromResult:errorResult]];
173+
}];
174+
}
175+
[tasks addObject:task];
176+
}];
177+
return [BFTask taskForCompletionOfAllTasks:tasks];
178+
}
179+
101180
///--------------------------------------
102181
#pragma mark - Utilities
103182
///--------------------------------------
104183

184+
//TODO: (nlutsenko) Convert to use `uniqueObjectsArrayFromArray:usingFilter:`
105185
+ (NSArray *)uniqueObjectsArrayFromArray:(NSArray *)objects omitObjectsWithData:(BOOL)omitFetched {
106186
if (objects.count == 0) {
107187
return objects;
@@ -115,6 +195,7 @@ + (NSArray *)uniqueObjectsArrayFromArray:(NSArray *)objects omitObjectsWithData:
115195
continue;
116196
}
117197

198+
//TODO: (nlutsenko) Convert to using errors instead of assertions.
118199
PFParameterAssert([className isEqualToString:object.parseClassName],
119200
@"All object should be in the same class.");
120201
PFParameterAssert(object.objectId != nil,
@@ -126,4 +207,24 @@ + (NSArray *)uniqueObjectsArrayFromArray:(NSArray *)objects omitObjectsWithData:
126207
return [set allObjects];
127208
}
128209

210+
+ (NSArray *)uniqueObjectsArrayFromArray:(NSArray *)objects usingFilter:(BOOL (^)(PFObject *object))filter {
211+
if (objects.count == 0) {
212+
return objects;
213+
}
214+
215+
NSMutableDictionary *uniqueObjects = [NSMutableDictionary dictionary];
216+
for (PFObject *object in objects) {
217+
if (!filter(object)) {
218+
continue;
219+
}
220+
221+
// Use stringWithFormat: in case objectId or parseClassName are nil.
222+
NSString *objectIdentifier = [NSString stringWithFormat:@"%@%@", object.parseClassName, object.objectId];
223+
if (!uniqueObjects[objectIdentifier]) {
224+
uniqueObjects[objectIdentifier] = object;
225+
}
226+
}
227+
return [uniqueObjects allValues];
228+
}
229+
129230
@end

Parse/PFObject.m

Lines changed: 16 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -394,80 +394,6 @@ - (BOOL)canBeSerializedAfterSaving:(NSMutableArray *)saved withCurrentUser:(PFUs
394394
}
395395
}
396396

397-
// Delete all objects in the array.
398-
+ (BFTask *)deleteAllAsync:(NSArray *)objects withSessionToken:(NSString *)sessionToken {
399-
if ([objects count] == 0) {
400-
return [BFTask taskWithResult:@YES];
401-
}
402-
403-
return [[[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{
404-
return [PFObject _enqueue:^BFTask *(BFTask *toAwait) {
405-
NSMutableSet *uniqueObjects = [NSMutableSet set];
406-
NSMutableArray *commands = [NSMutableArray arrayWithCapacity:[objects count]];
407-
for (PFObject *object in objects) {
408-
@synchronized (object->lock) {
409-
//Just continue, there is no action to be taken here
410-
if (!object.objectId) {
411-
continue;
412-
}
413-
414-
NSString *uniqueCheck = [NSString stringWithFormat:@"%@-%@",
415-
object.parseClassName,
416-
[object objectId]];
417-
if (![uniqueObjects containsObject:uniqueCheck]) {
418-
[object checkDeleteParams];
419-
420-
[commands addObject:[object _currentDeleteCommandWithSessionToken:sessionToken]];
421-
[uniqueObjects addObject:uniqueCheck];
422-
}
423-
}
424-
}
425-
426-
// Batch requests have currently a limit of 50 packaged requests per single request
427-
// This splitting will split the overall array into segments of upto 50 requests
428-
// and execute them concurrently with a wrapper task for all of them.
429-
NSArray *commandBatches = [PFInternalUtils arrayBySplittingArray:commands
430-
withMaximumComponentsPerSegment:PFRESTObjectBatchCommandSubcommandsLimit];
431-
NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:[commandBatches count]];
432-
for (NSArray *commandBatch in commandBatches) {
433-
PFRESTCommand *command = [PFRESTObjectBatchCommand batchCommandWithCommands:commandBatch
434-
sessionToken:sessionToken];
435-
BFTask *task = [[[Parse _currentManager].commandRunner runCommandAsync:command withOptions:0]
436-
continueAsyncWithSuccessBlock:^id(BFTask *task) {
437-
NSArray *results = [task.result result];
438-
for (NSDictionary *result in results) {
439-
NSDictionary *errorResult = result[@"error"];
440-
if (errorResult) {
441-
NSError *error = [PFErrorUtilities errorFromResult:errorResult];
442-
return [BFTask taskWithError:error];
443-
}
444-
}
445-
446-
return task;
447-
}];
448-
[tasks addObject:task];
449-
}
450-
return [BFTask taskForCompletionOfAllTasks:tasks];
451-
} forObjects:objects];
452-
}] continueWithBlock:^id(BFTask *task) {
453-
if (!task.exception) {
454-
return task;
455-
}
456-
457-
// Return the first exception, instead of the aggregated one
458-
// for the sake of compatability with old versions
459-
460-
if ([task.exception.name isEqualToString:BFTaskMultipleExceptionsException]) {
461-
NSException *firstException = [task.exception.userInfo[@"exceptions"] firstObject];
462-
if (firstException) {
463-
return [BFTask taskWithException:firstException];
464-
}
465-
}
466-
467-
return task;
468-
}] continueWithSuccessResult:@YES];
469-
}
470-
471397
// This saves all of the objects and files reachable from the given object.
472398
// It does its work in multiple waves, saving as many as possible in each wave.
473399
// If there's ever an error, it just gives up, sets error, and returns NO;
@@ -2495,10 +2421,23 @@ + (BOOL)deleteAll:(NSArray *)objects error:(NSError **)error {
24952421
}
24962422

24972423
+ (BFTask *)deleteAllInBackground:(NSArray *)objects {
2498-
return [[[self currentUserController] getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) {
2424+
NSArray *deleteObjects = [objects copy]; // Snapshot the objects.
2425+
if (deleteObjects.count == 0) {
2426+
return [BFTask taskWithResult:objects];
2427+
}
2428+
return [[[[self currentUserController] getCurrentUserSessionTokenAsync] continueWithBlock:^id(BFTask *task) {
24992429
NSString *sessionToken = task.result;
2500-
return [PFObject deleteAllAsync:objects withSessionToken:sessionToken];
2501-
}];
2430+
2431+
NSArray *uniqueObjects = [PFObjectBatchController uniqueObjectsArrayFromArray:deleteObjects usingFilter:^BOOL(PFObject *object) {
2432+
return (object.objectId != nil);
2433+
}];
2434+
[uniqueObjects makeObjectsPerformSelector:@selector(checkDeleteParams)]; // TODO: (nlutsenko) Make it async?
2435+
return [self _enqueue:^BFTask *(BFTask *toAwait) {
2436+
return [toAwait continueAsyncWithBlock:^id(BFTask *task) {
2437+
return [[self objectBatchController] deleteObjectsAsync:uniqueObjects withSessionToken:sessionToken];
2438+
}];
2439+
} forObjects:uniqueObjects];
2440+
}] continueWithSuccessResult:@YES];
25022441
}
25032442

25042443
+ (void)deleteAllInBackground:(NSArray *)objects target:(id)target selector:(SEL)selector {

Tests/Other/OCMock/OCMock+Parse.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
#import <OCMock/OCMock.h>
1111

12+
@class PFRESTCommand;
13+
1214
@interface OCMockObject (PFCommandRunning)
1315

14-
- (void)mockCommandResult:(id)result forCommandsPassingTest:(BOOL (^)(id obj))block;
16+
- (void)mockCommandResult:(id)result forCommandsPassingTest:(BOOL (^)(PFRESTCommand *command))block;
1517

1618
@end

Tests/Other/OCMock/OCMock+Parse.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
@implementation OCMockObject (PFCOmmandRunning)
1818

19-
- (void)mockCommandResult:(id)result forCommandsPassingTest:(BOOL (^)(id obj))block {
19+
- (void)mockCommandResult:(id)result forCommandsPassingTest:(BOOL (^)(PFRESTCommand *command))block {
2020
PFCommandResult *commandResult = [PFCommandResult commandResultWithResult:result
2121
resultString:nil
2222
httpResponse:nil];

0 commit comments

Comments
 (0)