Setting the title of AirDrop shares under iOS 7

In iOS 7, Apple introduced AirDrop, a way of sharing files and links between nearby devices using a combination of Bluetooth and Wi-Fi. They also updated UIActivityViewController to allow sharing over AirDrop from your own native application as well.

If you’re sharing images, or simple web links, you’ll get the behavior for free just by compiling against the new iOS SDK, but if you want to share deep links into your native application you have to do a little more work. When we added AirDrop to version 3.0 of the Eventbrite iPhone app we ran into a problem. We wanted to be able to share events from one phone to another using our custom URI scheme. It’s very easy for the app to select an appropriate URL to share based on the sharing method, so that Twitter, for instance, will get an HTTP URL but sharing via AirDrop sends the custom Eventbrite URL. Unfortunately, that ends up not looking so pretty:

share_url

The trick to making it look better is to save the URL we want to share into a file on the local device, and set the filename to the name of the event. Then we can send the file via AirDrop and the remote device will display the filename rather than the raw url.

On the sending side, our custom UIActivityItemSource has a method like this:

- (id)activityViewController:(UIActivityViewController *)activityViewController
         itemForActivityType:(NSString *)activityType
{
    // If the user wants to share the URL through twitter, email, etc, we'll
    // just send the URL we were passed at construction time. This should be a
    // http[s] URL so that normal apps can open it.
    if (![activityType isEqualToString:UIActivityTypeAirDrop]) {
        // let's assume this has been set earlier.
        return self.url;
    }

    // For AirDrop, we'll write the URL as a string to a file on disk, name
    // the file after the title we want to share as, then return the path
    // to that file on disk. The receiving device will see the filename
    // in the Airdrop accept dialog, rather than the raw URL.

    // Use a dedicated folder so cleanup is easy.
    NSURL *cache = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
                                                          inDomain:NSUserDomainMask
                                                 appropriateForURL:nil
                                                            create:YES
                                                             error:nil];
    NSURL *scratchFolder = [cache URLByAppendingPathComponent:@"airdrop_scratch"];
    [[NSFileManager defaultManager] removeItemAtURL:scratchFolder
                                              error:nil];
    [[NSFileManager defaultManager] createDirectoryAtURL:scratchFolder
                             withIntermediateDirectories:YES
                                              attributes:@{}
                                                   error:nil];

    // You can't put '/' in a filename. Replace it with a unicode character
    // that looks quite a lot like a /.
    NSString *safeFilename = [self.subject stringByReplacingOccurrencesOfString:@"/"
                                                                     withString:@"\u2215"];

    // The file on disk has to end with a custom file extension that we have defined.
    // Check "Document Types" and "Exported UTIs" in the project settings to see
    // where this file extension is defined.
    NSURL *tempPath = [scratchFolder URLByAppendingPathComponent:
                       [NSString stringWithFormat:@"%@.Eventbrite", safeFilename]];

    // Write the URL into the file, and return the file to be shared.
    NSData *data = [self.url.absoluteString dataUsingEncoding:NSUTF8StringEncoding];
    [data writeToURL:tempPath atomically:YES];
    return tempPath;
}

When the receiving device launches, it opens the file, extracts the URL, and then continues as if it had been sent that URL directly.

In our UIApplicationDelegate:

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    if ([url isFileURL]) {
        // We've been given a file, not a network URL. For our purposes,
        // that means it's an Airdrop receipt. We'll extract the original
        // URL from the file and continue as if we'd been passed it normally.
        NSData *fileData = [NSData dataWithContentsOfURL:url];
        [[NSFileManager defaultManager] removeItemAtPath:[url path] error:nil];
        NSString *urlString = [[NSString alloc] initWithData:fileData
                                                    encoding:NSUTF8StringEncoding];
        url = [NSURL URLWithString:urlString];
    }

    [self openCustomURL:url];
    return YES;
}

Now when we send the object, it looks like this:

share_file

Regrettably, the file extension seems to be required, and it needs to be unique to the sending device. You must also define the extension in your project settings. In the “Info” pane of the build target settings you must add an entry in both “Exported UTIs” (so that the file that AirDrop sends has the proper type information on it) and “Document Types” (so that the receiving device knows that it should offer to open this file):

project_settings

Finally, if the target device doesn’t have your application installed, it will offer to take the user to the App Store and search for applications registered to handle this document type. Because the identifier should be specific to your app, the user will be able to install it easily.

share_install

I’ve put together a simple example project that does all of this. If you install it on two AirDrop-capable devices (the iOS simulator won’t be enough – you need two real iOS devices with vaguely recent Wi-Fi hardware) you’ll be able to share arbitrary links back and forth. You’ll notice that if you share the link via Twitter or Facebook the link is sent normally, but if you send it via AirDrop we wrap it with the above technique so that the target device will open it in the native app. Of course, it’s not on the App Store, so if the target device doesn’t have the app, the offer to install it won’t help.

Download the demo project.