Show UIAlertView during UIActivity:activityViewController

362 Views Asked by At

I have a set of UIActivities where I prepare my data into a given format and then attach it to an email the user can send. I'm using a subclass of UIActivity and I'm doing all the work in -(void)activityViewController:

- (UIViewController *)activityViewController
{
    [self.alert show];

    NSString *filename = [NSString stringWithFormat:@"%@.gpx", self.activity.title];
    __block MFMailComposeViewController *mailComposeVC = [[MFMailComposeViewController alloc] init];
    mailComposeVC.mailComposeDelegate = self;
    [mailComposeVC setSubject:[NSString stringWithFormat:@"GPX export for %@ activity", self.activity.title]];
    [mailComposeVC setMessageBody:@"Generated with Slopes" isHTML:NO];

    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        CBCFileExporter *exporter = [[CBCFileExporter alloc] init];
        NSData *exportContents = [exporter exportActivity:self.activity inFileFormat:CBCFileExportTypeGPX error:nil];
        [mailComposeVC addAttachmentData:exportContents mimeType:@"application/gpx+xml" fileName:filename];
    });

    [self.alert dismissWithClickedButtonIndex:0 animated:YES];

    return mailComposeVC;
}

The specific issue I'm running into is that the UIAlertView doesn't actually show until the dispatch_sync completes. I realize the dispatch_sync might(?) be blocking the main thread as it waits, but the problem is I need to wait until the attachment is generated before returning from that method call (MFMailComposeViewController docs say you can't add attachment once the view is presented).

How can I get an alertview to show while a non-trivial task the main thread has to wait for completion has to run?

2

There are 2 best solutions below

0
On

Ick.

For what it's worth, I had to give up (after 4 hours of fighting with all kinds of blocks, performOnThread, etc) on using the activityViewController method to directly return a UI and instead switch to the performActivity method. PerformActivity is supposed to be for UI-less activities, but it's the only async-compatable one.

I have to set my main ViewController (the one showing the activity sheet) as a delegate to the UIActivities, then call my delegate back with the message VC once the export is ready:

- (void)performActivity
{
    __block UIAlertView *alert = [[UIAlertView alloc] init];
    alert.title = @"Generating Export";
    [alert show];

    //get rid of the activity sheet now - can't present the mail modal if this is active
    [self activityDidFinish:YES];

    __block CBCGPXEmailActivity *weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        CBCFileExporter *exporter = [[CBCFileExporter alloc] init];
        NSString *filename = [NSString stringWithFormat:@"%@.gpx", weakSelf.activity.title];
        NSData *exportContents = [exporter exportActivity:weakSelf.activity inFileFormat:CBCFileExportTypeGPX error:nil];

        //dispatch after to make sure there was time to remove the action sheet
        double delayInSeconds = 0.1;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            MFMailComposeViewController *mailComposeVC = [[MFMailComposeViewController alloc] init];
            [mailComposeVC setSubject:[NSString stringWithFormat:@"GPX export for %@ activity", weakSelf.activity.title]];
            [mailComposeVC setMessageBody:@"Generated with Slopes" isHTML:NO];
            [mailComposeVC addAttachmentData:exportContents mimeType:@"application/gpx+xml" fileName:filename];

            [weakSelf.delegate showMailViewController:mailComposeVC];
            [alert dismissWithClickedButtonIndex:0 animated:YES];
        });
    });
}
0
On

Given that the mail view controller specifically disallows adding an attachment to the mail compose view controller once it has been presented, what you probably need to do here is create and present an "interstitial" view controller with an indeterminate progress indicator, start the export process in the background, and then when that process is complete, create and fully populate the mail compose view controller with the attachment, then present it.

That requirement that it be fully populated before being presented means that there won't be a simple "do this in the background and call me back" approach possible.