I have a app a app that allows users to rapidly send photos to each other , but as we all know users dont always have a perfect internet connection so we decided to create a system that would temp store all the photos in a directory and info for each api request in a array of dictionaries. If a user takes 2 photos to send and the first one fails due to no connection and then a few minutes later the user takes a 3rd photo when they have a internet connection this is what happens (pseudo ) , but we get some repeats and weird things happening doing it this way if the queue starts to get backed up and this whole process is being triggered multiple times. So we did some research and dispatch_groups seems to be the answer, but we cant figure out how we can use the same dispatch group each time so that there are not multiple dispatch group queues all firing the same requests at the same time if a user takes 20 pictures really fast.
Another important part of this system is that it must MUST upload in the same order all the images were taken and should ideally avoid any duplication
-(void)upload:(NSString*)typeOfUpload{
[_resendBtn setHidden:YES];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *pendingRequests=[[NSMutableArray alloc] init];
NSString *pendingRequestsFrom= [NSString stringWithFormat:@"pendingRequestsForUid%@",[defaults objectForKey:@"uid"]];
NSLog(@"PENDINGREQUESTFROM:%@",pendingRequestsFrom);
if ([defaults objectForKey:pendingRequestsFrom]){
pendingRequests= [[defaults objectForKey:pendingRequestsFrom]mutableCopy];
}
NSMutableDictionary *requestDict=[[NSMutableDictionary alloc] init];
NSDate *now = [NSDate date];
int timestamp = [[NSDate date] timeIntervalSince1970];
[requestDict setObject:[NSString stringWithFormat:@"%d",timestamp] forKey:@"timestamp"];
if(_convertedVideoURL){
NSString*urlPath= [_convertedVideoURL path];
[requestDict setObject:urlPath forKey:@"videoURL"];
}
if([typeOfUpload isEqualToString:@"PHOTO"]){
// Get image data. Here you can use UIImagePNGRepresentation if you need transparency
NSData *imageData = UIImageJPEGRepresentation(_imgToSend, 8);
// Get image path in user's folder and store file with name image_CurrentTimestamp.jpg (see documentsPathForFileName below)
//Create temporary URL to record to
NSDate *now = [NSDate date];
NSTimeInterval nowEpochSeconds = [now timeIntervalSince1970];
NSString *intervalString = [NSString stringWithFormat:@"%f", nowEpochSeconds];
NSString *main_img_path = [[NSString alloc] initWithFormat:@"%@image%@.jpg", NSTemporaryDirectory(), intervalString];
// Write image data to user's folder
[imageData writeToFile:main_img_path atomically:YES];
[requestDict setObject:main_img_path forKey:@"imgToSendStored"];
}
[requestDict setObject:_selectedUserString forKey:@"recip_uid"];
[requestDict setObject:typeOfUpload forKey:@"MEDIA_TYPE"];
if([typeOfUpload isEqualToString:@"TEXT"]){
[requestDict setObject:_textMsgView.coverCaption.text forKey:@"body"];
}
NSLog(@"params being stored for later %@", requestDict);
[pendingRequests addObject:requestDict];
NSArray *newArray= [NSArray arrayWithArray:pendingRequests];
NSLog(@"TOTAL_PENDING_VIDS == %@, araay count == %d",newArray,[newArray count]);
[defaults setObject:newArray forKey:pendingRequestsFrom];
[defaults synchronize];
_imgToSend=nil;
_textToSend=nil;
_isTextDropDownDisplayed=NO;
[UIView animateWithDuration:.5 animations:^{
[_textMsgView setFrame:CGRectMake(0, -300, 320, 10)];
_textMsgView.coverCaption.text=@"";
//secondView.alpha = 1.0;
[self swippedAway];
}];
[self uploadStoredVidsFunction:@"UPLOAD"];
}
-(void)uploadStoredVidsFunction:(NSString*)typeOfResend
{
NSString *pendingRequestsFrom= [NSString stringWithFormat:@"pendingRequestsForUid%@",[defaults objectForKey:@"uid"]];
pendingRequests= [[defaults objectForKey:pendingRequestsFrom]mutableCopy];
NSLog(@"PENDING_REQUESTS%@",pendingRequests);
dispatch_group_t group = dispatch_group_create();
for (int i=0;i<[pendingRequests count]; i++) {
dispatch_group_enter(group);
MAKE AFNETWORKING REQUEST
success{
remove request from pending array
// start next request
dispatch_group_leave(group);
}
failure {
//STOP THE QUEUE from continuing to execute the rest of the requests in line/give user their options ( aka retry sending all/ delete all/save for later )
}
}
}
You could just spawn off a new thread that deals with all of this in an unterminating while
loop using an NSCondition
to ensure thread-safety.
// Somewhere in your initialization:
requestLock = [[NSCondition alloc] init];
[self performSelectorInBackground:@selector(processRequests)];
- (void)processRequests {
while (![[NSThread currentThread] isCancelled]) {
[requestLock lock];
if ([pendingRequests count] == 0 /* || delay time not yet reached */) {
[requestLock waitUntilDate:someTimeoutDate];
[requestLock unlock];
continue;
}
NSMutableArray *remainingRequests = [pendingRequests copy];
[pendingRequests removeAllObjects];
[requestLock unlock];
for (Request *request in requests) {
if (success) {
// Process the request and then..
[remainingRequests removeObject:request];
} else {
break;
}
}
[requestLock lock];
[pendingRequests insertObjects:remainingRequests atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [remainingRequests count])]];
[requestLock unlock];
}
}
- (void)addRequest:(Request *)request {
[requestLock lock];
[pendingRequests addObject:request];
[requestLock broadcast];
[requestLock unlock];
}