Search code examples
ioscocoa-touchios-app-extension

How to invoke action extensions?


From the iOS SDK docs, an Action extension might help users edit an image in a document that they’re viewing in a text editor. However I've been trying to google for examples how to do this and can only find articles on how to create app extensions and not how to use them in an app.

Suppose that I'm writing a word processing application for iOS. Picture the user having an embedded image in the app and wants to edit the image (e.g. apply a photo effect). How can the application provides the image to whatever image editing applications that the user has installed in the system, let it do it's thing, and then takes in the result?

I'd imagine the interaction style is pretty similar to LinkBack on the Mac. Except that the image editor is an app extension and displayed as a modal dialog (as per the SDK guide).

However I couldn't find any code example that shows:

  • How to provide input data (e.g. image) to the action extension.
  • How to invoke the action extension.
  • How to get back the output data from the action extension (including whatever additional metadata or editing information that the extension generates).
  • How to display the output data in a recognizable format (e.g. if I gave out a JPEG, I'd expect another JPEG will be given by the action extension as a result).

Solution

  • In the mean time I've figured out how to do this. It revolves primarily around UIActivityViewController and providing a callback block to the controller to receive the results from the action extension.

    The example below works with Skitch as the test action extension.

    // get the source image
    UIImage* image = ...;
    NSArray* activityItems = @[image];
    UIActivityViewController* activityCtrl = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
    activityCtrl.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
        [returnedItems enumerateObjectsUsingBlock:^(NSExtensionItem*  _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop1) {
            [item.attachments enumerateObjectsUsingBlock:^(NSItemProvider*  _Nonnull attachment, NSUInteger idx, BOOL * _Nonnull stop2) {
                if ([attachment hasItemConformingToTypeIdentifier:(__bridge id)kUTTypeImage]) {
                    [attachment loadItemForTypeIdentifier:(__bridge id)kUTTypeImage options:nil completionHandler:^(UIImage* returnedImage, NSError * _Null_unspecified error) {
                        // returnedImage is the result of the action extension
                    }];
                    *stop1 = YES;
                    *stop2 = YES;
                }
            }];
        }];
    };
    
    // assume that `self` is a UIViewController
    [self presentViewController:activityCtrl animated:YES completion:nil];
    

    Unfortunately action extension providers are a bit rare, hence I couldn't test out how it actually interacts with other action extensions that can produce images.