This repository demonstrates how the UIDocumentBrowserViewController component is broken by a race condition issue caused by multiple iOS apps trying to register different Uniform Type Identifiers (aka. UTI) against the same filename extension.
This issue affects ALL iOS apps that adpot UIDocumentBrowserViewController
since iOS 11 and, when it happens, the whole app is malfunctioning because it can NOT open its own documents. It must be fixed, and can only be fixed, by Apple.
-
Clone this repository.
-
Open the "InnocentApp" project (a minimal Document-based App) using Xcode, select a testing device (either a simulator or a real device, iPad recommended), build and run the app.
-
When the "InnocentApp" is launched, tap the "+" button to create a few empty documents, e.g.
New Document.innocent
. -
Try tapping these files, and make sure that they can be opened in a simple document view.
-
Return to Xcode, and open the "MalicousApp" project, build and run it on the same device chosen above.
-
When the "MaliciousApp" is installed and launched, you don't even need to perform any interaction with this app, just simply return to the Home Screen and re-open the "InnocentApp" (or you can do so using the App Switcher).
-
Here the "MAGIC" happens: All
.innocent
documents in the document browser can NO LONGER be opened inside the "InnocentApp"! You can NOT even rename or delete them! And the most weird thing is that you CAN still create new.innocent
documents, but you can NO LONGER re-open them! Now the app becomes totally useless since no documents created by the app can be opened from within the app. -
Now return to the Home Screen, delete the "MaliciousApp", and return to the "InnocentApp". You should be able to open/edit/rename/delete those documents again.
The "InnocentApp" declares an "Exported UTI" named me.frankshaka.file.innocent
and associate it with the filename extension innocent
, according to this developer guide from Apple about Declaring New UTIs.
The "MaliciousApp" also declares an "Exported UTI" to be associated with this filename extension, but using a different identifier me.frankshaka.file.malicious
.
When an app is installed (or updated), the operating system reads the app's UTI declarations and automatically registers them in the File Metadata Core Service. This behavior is the same on both iOS and macOS.
Obviously, one kind of filename extensions can only be associated with one UTI across the system. So, when the "MalicousApp" is installed, all .innocent
files across the system will hence be regarded as documents of type me.frankshaka.file.malicious
.
What's actually wrong is that UIDocumentBrowserViewController
thinks that the "InnocentApp" is not permitted to open .innocent
files just because the app is not associated with the malicious UTI. This does NOT make sense. The document browser is just a normal component of the app from the user's point of view, rather than a system widget like a Share Sheet or a Document Picker, and thus it should behave the same way and obey the same rules as other components of the same app, no matter how it works.
This issue is disgusting because, as the developer of the "InnocentApp", you've done nothing wrong. You precisely followed the Apple's documentation and tutorials, and even created the app from the Xcode template "Document-based App". But one day, you suddenly receive thousands of emails from angry users, or even thousands of 1-star comments to your app in the App Store, reporting that your app does not work any more, just because they installed another app developed by a third-party developer!
Sure you can ask your users to delete that third-party app. But in a real world, big companies can make small mistakes. That third-party app could happen to be Twitter, Facebook, Youtube, Instagram, Dropbox, Spotify, or even a popular game. It doesn't have to be a "Document-based App", all it needs is to carelessly include several lines of code in their Info.plist
file and submit the update to the App Store, and ... "Boooom!" You're done.
If you've also encountered this issue, there's rarely anything you can do to quickly recover from the consequence. Here's my advice.
- Ask the third-party developer to update their app by either removing that UTI declaration or using the same declaration as yours. If you can't contact them directly, you can try asking Apple to mediate between you and the third-party developer. And, WAIT for the updated version of that app to be submitted, promoted, and finally downloaded and installed by all of the users affected.
- Submit a bug report to Apple and, again, WAIT for Apple to fix this bug.
If you're just interested in this issue, it's welcome for you to
- Star this repository or watch for updates;
- Leave comments in Issues, and share your thoughts on this.
MIT