Another app's UTI can break your app
Another app's UTI can break your app
- Subject: Another app's UTI can break your app
- From: Seth Willits <email@hidden>
- Date: Tue, 10 Jun 2014 12:21:13 -0700
TLDR:
-----------------------
If your app has a document type that may use the same extension as another app, you should override typeForContentsOfURL:error: because otherwise the existence of that other application on a user's system can break your app.
Problem summary:
-----------------------
My app and Coda both open plain text .sql files. Coda exports a UTI for the .sql extension. So does my app. However (because Coda was registered with Launch Services first), my app will be prevented from opening ANY .sql file because the system and Cocoa think it's a com.panic.coda.structured-query-language-file file, and my app can't open files with that UTI.
The same would happen even if my app does not use a UTI but simply declares that it opens documents with an .sql extension.
This is a critical problem.
Investigatory work:
-----------------------
- [NSDocumentController typeForContentsOfURL:error:] is the first method being called to determine what class will open a file. The header file (but not the documentation) has some useful information:
"The default implementation of this method merely returns the URL's NSURLTypeIdentifierKey resource value.
You can override this to customize type determination for documents being opened."
What this says, is that Cocoa doesn't look at my Info.plist file to see what type *my application* would describe the file at the URL as. Instead, it by default asks the file system "what's the UTI for this URL" and Launch Services (because Coda registered its sql UTI long before my app) says: "It's a Coda file."
Cocoa *then* looks in my Info.plist file (essentially), and sees no conformity/usage of com.panic.coda.structured-query-language-file so it throws a fit, preventing my app from opening .sql files.
That's very unfortunate and frankly ridiculous because this means that the existence of another application on your system can break your app.
The solution however is easy.
Solution
-----------------------
By overriding typeForContentsOfURL:error: as the header suggests, we can cure the problem.
- (NSString *)typeForContentsOfURL:(NSURL *)url error:(NSError **)outError;
{
if ([url.pathExtension.lowercaseString isEqual:@"sql"]) {
return @"my.uti.type";
}
return [super typeForContentsOfURL:url error:outError];
}
It's also important to note that if your document type declares a UTI, you need to return that UTI from typeForContentsOfURL:error: even though the docs say it returns "name of the document type" (AKA the CFBundleTypeName). If you return the type name and not the UTI then the document architecture will get confused later and things will fail. However, if you don't declare a UTI type for the document, then return the CFBundleTypeName.
Conclusion
-----------------------
That's several hours of investigatory work. I went down many rabbit holes during that, but I'm pretty sure that I understand this and have boiled it down to the root cause and truth. If I'm wrong, please correct me. (I'd like to be wrong!)
--
Seth Willits
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden