1

I want to perform an upload task once a user has selected a share activity from UIActivityViewController, but before the share sheet is shown.

Specifically, I need the url of the uploaded image to use in the Activity.

I already have subclassed UIActivityItemProvider and figure I can do my uploading in the itemForActivityType method, however the uploading code is block based and I can't figure out how to make it wait for the block to finish. Is this even possible? It might be a simple coding error, it's been a long day.

I dont want to upload the image when the user presses the share button, as they might cancel the Activity View which means the uploaded image is sitting there not being used.

This is the code I currently have, but it returns nil before the image has uploaded and within the block it doesn't let me return nil for the errors:

- (id) activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{

    [self getShortUrlForUploadedImageWithCompletionHandler:^(NSString *shortUrl, NSError *error) {
        if (!error) {
            if ( [activityType isEqualToString:UIActivityTypeMail] ) {

                NSString *shareString = @"Email content here using shortUrl";

                return shareString;
            } else {
                return @"";
            }

        } else {

            return @"";
        }
    }];

    return nil;
}

-(void)getShortUrlForUploadedImageWithCompletionHandler:(NSString* (^)(NSString *shortUrl, NSError *error))completionHandler
{
    NSData *imageToUpload = UIImageJPEGRepresentation(_image, 75);
    AFHTTPClient *client= [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:kShareURL]];

    NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
                            @"image", @"action",
                            @"simple", @"format",
                            nil];
    NSMutableURLRequest *request = [client multipartFormRequestWithMethod:@"POST" path:nil parameters:params constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
        [formData appendPartWithFileData: imageToUpload name:@"image" fileName:@"temp.png" mimeType:@"image/jpeg"];
    }];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSString *response = [operation responseString];
        NSLog(@"response: %@",response);

        completionHandler(response, nil);

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

        if([operation.response statusCode] == 403){
            NSLog(@"Upload Failed");
            return;
        }
        NSLog(@"error: %@", [operation error]);

        completionHandler(nil, error);
    }];

    [operation start];
}

-------- EDIT

I really could do with some help on this. My current work around is to upload the image when the user click my share button, before the Activity selection. So they could cancel the share and i'm left with a redundant uploaded image, or they could select Twitter which doesn't need the uploaded image. I need to only upload the image if Email has been selected and I think the only place I can do this is in the Acticity Provider subclass.

Darren
  • 10,182
  • 20
  • 95
  • 162

1 Answers1

2

Instead of implementing - (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType, try overriding the UIActivityItemProvider's - (id)item. This method will be called from the NSOperation's main method which is on a background thread.

As for waiting until the networking completion block triggers, I'd recommend you look into using a dispatch_semaphore. Here is an example:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"doing some work");
    sleep(5);
    NSLog(@"done with work");
    dispatch_semaphore_signal(semaphore);
});
double delayInSeconds = 60.0;
dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
NSLog(@"waiting for background thread to finish");
dispatch_semaphore_wait(semaphore, waitTime);
NSLog(@"background thread finished, or took too long");

Make sure to only use this on a background thread though, otherwise you will block the main thread.

ccjensen
  • 4,578
  • 2
  • 23
  • 25