Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send and receive custom data with AirDrop

I've been struggling with this for ages now as I can't find any detailed examples.

In my app I have an array of custom data that I want to send to another user with the same app, via AirDrop.

The first step is sending the data:

@IBAction func share_Button_Click(sender: UIBarButtonItem)
{
    let dataToShare: NSData = getMyCustomNSData()

    let controller = UIActivityViewController(activityItems: [dataToShare], applicationActivities: nil)
    controller.excludedActivityTypes = [UIActivityTypePostToFacebook, UIActivityTypePostToTwitter, UIActivityTypePostToWeibo, UIActivityTypePrint, UIActivityTypeCopyToPasteboard, UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll, UIActivityTypePostToFlickr, UIActivityTypePostToTencentWeibo, UIActivityTypeMail, UIActivityTypeAddToReadingList, UIActivityTypeOpenInIBooks, UIActivityTypeMessage]

    self.presentViewController(controller, animated: true, completion: nil)
}

This converts my data to an NSData object, the user gets the AirDrop share option, and of the data goes to another phone. So far so good...

But how does the other user's app know how to receive it?

I've read about custom UTI types and have declared one, but to be honest I don't know what to put in the declaration. And how do you indicate to iOS that the data you are sending conforms to this particular UTI?

There are AirDrop examples here and there online, but they focus on sharing common types like images, and no one I have found has worked through sharing a custom data type in detail.

Can anyone help?

like image 633
Greg Avatar asked Sep 03 '16 17:09

Greg


2 Answers

The AirDrop sample code covers how to define your own file type/UTI and use it to send your custom data using AirDrop.

The main required parts are as follows.

  1. The following keys and values in your app's Info.plist

    <...>
    <key>CFBundleDocumentTypes</key>
    <array>
    <dict>
        <key>CFBundleTypeName</key>
        <string>AirDrop Profile File Type</string>
        <key>LSHandlerRank</key>
        <string>Default</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>com.apple.customProfileUTI.customprofile</string>
        </array>
    </dict>
    </array>
    <...>
    
  2. To support receiving your custom type
    In the app delegate, handle being launched with a fileURL by implementing

    Objective-C

    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
    

    Swift

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        // Check if your app can open the URL
        // If it can, do something with the url and options, then return true
        // otherwise return false
    }
    

    Make sure to remove/move the file to clean up in the inbox folder, where recieved documents go (inside your app's sandbox).

  3. To support sending the custom type
    The item you pass to UIActivityViewController should either be a fileURL to a file with the extension that you registered as your custom file type, or an object that conforms to the UIActivityItemSource protocol and returns something of NSData type in

    - (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
    

    and your actual NSData blob in

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

    Then in the following method you'll want to return the UTI of your custom type that you registered in your app's Info.plist.

    - (NSString *)activityViewController:(UIActivityViewController *)activityViewController dataTypeIdentifierForActivityType:(NSString *)activityType
    

The sample code has a great example of how to do all of this, and also how to make the entire UX better by including a properly sized and cropped preview photo.

like image 119
ccjensen Avatar answered Nov 15 '22 06:11

ccjensen


iOS13 (and above)

If the open url delegate method in AppDelegate is not called

func application(_ app: UIApplication, open url: URL, options [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { ...}

You should use the openURLContexts in SceneDelegate:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url else {return}    
}

Then you can do with 'url' what you need in your app.

like image 20
Adir Kol Avatar answered Nov 15 '22 07:11

Adir Kol