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 (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to