Sharing custom file types in iOS

If you are planning to share more than standard file types on iOS and make the system recognize the file and open it in your app you can use Document Types and Exported Type Identifiers.

For some time, Xcode is making a declaration of custom types easy. We can do it on the target's Info page.

Exported type identifier

Let's declare a new type identifier. Open Info page of your target:

Info page in Xcode

Expand Exported Type Identifiers and add a new entry:

Exported type identifier

Apple documentation clearly stays, that our new type has to conform to at least one public type. I'm using two:

  • public.data - Base physical type for byte streams (flat files, pasteboard data, and so on).
  • public.content - Base type for mixed content. For example, a PDF file contains both text and special formatting data.

Those are the vaguest types, perfect for our demo. Apple provides many more types, available here.

Document Type

Now we have to add our type. Expand Document Types and add entry:

New document type

This will ensure, that system will list our app when opening a new custom type.

The code

We will make the app create and open the new file. For simplicity, I'll use a plain text file with a custom extension.

Add plain file to project and give it name: fileToShare.shr. Fill the file with some text, it can be anything you want.

Sharing

I added a button and a label to my root view controller. I connected the label to an outlet and the button to an action. Now, we have to present a share sheet. Add code to action:

@IBAction func share(_ sender: UIButton) {
    let url = Bundle.main.url(forResource: "fileToShare", withExtension: "shr")!
    let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
    self.present(activityViewController, animated: true, completion: nil)
}

This will open an iOS sharing popup. Build and run the app. Press the share button. You should see something close to this:

Sharing sheet

It works! Our file has a type description and iOS knows, that we can open files with shr extensions in our app. This is what we wanted to achieve! I saved my file to the Files app, from there I'll check if opening works.

Opening file

If you are using a newer Xcode, like 12.x, the opening file has to be done in SceneDelegate. Add a new method to SceneDelegate:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    if let url = URLContexts.first?.url,
       url.scheme == "file" && url.pathExtension == "shr",
       let data = try? Data(contentsOf: url) {
        (window?.rootViewController as? ViewController)?.displayLabel.text = String(data: data, encoding: .utf8)
    }
}

The new method is called when the app is asked to open an URL. First, check if the context has the URL to open. Then code ensures that URL points to file with the proper extension. If yes, I'm doing not so brilliant, but good enough "hack". I'm accessing the window's root view controller's label to set its text to match file content. In production code, I would use a flow controller to handle such updates.

If you are using an older Xcode, file handling should be done in AppDelegate. In this case, add a new method to AppDelegate:

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    if url.scheme == "file" && url.pathExtension == "shr" {
        // open file here
        return true
    }
    return false
}

Build and run the app. If you saved the shr file in the Files app, go to Files and open the shr file in the ShareExample app. For me this is the effect:

Opened file

Content is displayed on the label! Yuppie, our code is working perfectly!

Conclusions

iOS provides an easy way to create and handle your own file types. This is super useful when creating documents app, provide an option to export user's data to file for backup or transfer between platforms.