I’m building a document-based iOS app for which I want to add sharing and collaboration features. Where possible, I try to use native iOS frameworks and features. When it comes to files, this means using iCloud Drive. This made me investigate how to build sharing and collaboration features on top of iCloud Drive.

iCloud is traditionally a single-user concept, but Apple has expanded it to various sharing capabilities in recent years. This shows best in iWork where you can share documents with specific people, grant them different levels of access, and collaborate with them in real-time. Similarly, in the Files app on iOS you can share any document from your iCloud Drive using a public URL or with selected people.

What’s available

These are great features, so the question is: How can you do the same thing in your own app?

When building a document-based app on iOS, the recommended way is to use UIDocument. Thanks to the way it works, it’s pretty straight-forward to pick up live changes to the document while the app is running. This is useful when the document is used by a single user, say, using the same app on multiple devices or when modifying the document in a different app on the same device. And, of course, this is what we’d need to show live changes as other people modify a shared document. UIDocument passes on the changes, you can update the UI accordingly, and you have live collaborative editing.1

However, UIDocument only covers live changes to an already shared filed, but how do you share the file in the first place?

Users can share files of any document-based app that is storing its files in iCloud Drive, without the developer of that app having to do anything: Just navigate to the files in the iCloud Drive section of the Files app, tap the “Share” action and then select “Add People” (or “Show People”, if it’s already shared). That works, but going through the Files app is rather cumbersome and most users won’t find this by themselves. You get the same features in the app, if the app is making use of UIDocumentBrowserViewController or UIDocumentPickerViewController2 — both are basically small embeddable variants of the Files app. Lastly, developers can expose the same features anywhere in their app by using UIActivityViewController. Provide the UIDocument.fileURL as one of the activityItems during initialisation, and the “Add People” action shows up for iCloud Drive documents.

This is good, but also a rather shallow integration, which doesn’t allow for many (or any) customisation. You can’t add a button just for sharing, and there are a few other stumbling blocks.

Stumbling blocks

Q: When I share a file from my app through the Files app, it shows with the Files app’s icon and name. What should I do?

A: That’s expected behaviour, as any app can share any documents, so the sharing message will show the app name and icon of the app that you share from — it doesn’t use those of the app creating the file. So, you should expose sharing from your app, using UIActivityViewController, UIDocumentBrowserViewController or UIDocumentPickerViewController.

Q: When I share a file through my app, the recipient just gets a default QuickLook preview, which might just show the text content if the file format is text based. When pressing the action button, my app is somewhere down the list and there’s no indication that it might be the owning app of that file type. Is that any way around this?

A: … I haven’t yet found a way around this.3 I’d love to hear if there is one. At the least, I’m hoping for improvements in iOS 13.

Q: Can I detect if a file is shared?

A: Yes, iOS 11 introduced a number of “ubiquitous item” (i.e., iCloud-related) properties on URLResourceValue, including whether an item is shared, who is the owner and what permissions you have.

Unfortunately, it does not go as far as providing access to who the item is shared with, how many people it is shared with, or what the sharing URL is.

As a side note, by calling FileManager.getFileProviderServicesForItem() for a file in iCloud Drive, you’ll get a NSFileProviderService with the name com.apple.iWorkCollaboration. This could be the gateway for getting more information using XPC calls and I went down a rabbit hole exporing this further. However, you’d have to know what communication protocol this is using which I have not figured out.

Q: What happens if multiple people edit the file at the same time?

There’s not much handholding here, you’ll get a document status indicating that the document is in conflict and you will need to provide conflict resolution.

What’s missing

  • Be able to customise message in the “Add People” activity, or also provide link to download app.4rdar://47406314
  • Recipient should be able to open app directly with that file rather than opening Files app, either preferring the app that the file was shared from, or preferring the app who claims ownership of that file type. → rdar://47406335
  • A way to directly present the “Add People” / “Show People” view controller, rather than having to rely on UIActivityViewController.5rdar://47406293
  • Detect with whom or how many people a document is shared with (to show, e.g., “Shared with 5 people”). → rdar://47406322

Changelog:

  • 21 June: Added references to URLResourceValue.
  1. We’ll ignore the very much non-trivial challenge of how to deal with potential clashes of simulatenous edits. 

  2. Perhaps somewhat surprisingly at first, you can share another app’s files from your app’s browser. Though, then again, this is just a plain iCloud Drive feature, so it might not be too surprising. However, when you do share it, the shared file gets the icon of the app you share it from, so if you share an iWork file through your app (or Files), what the recipient sees is the file name but with your app’s name and icon. 

  3. A QuickLook extension might help with the preview part, but that only works for custom file types. 

  4. A simple way to do this would be if the “Add People” activity would pass on other objects that were passed to it, such as a string. However, this is not the case and they are ignored. 

  5. I’m not very familiar with AppKit, but it seems to support this