How do I make a file that identifies as a custom UTI type? - objective-c

Right now I have an application that generates an XML file. We'll call it someFile.myapp
When my app saves the file and look at it using mdls it has a kMDItemContentType of "dyn.234kdfsjk23jk24234kjfd"
How can I get the UTI type of the file to be a custom value like com.mycompany.myapp?

UTIs are linked to extensions via a declaration in your application's Info.plist file. Add a section like so:
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
</array>
<key>UTTypeDescription</key>
<string>My Special File Type</string>
<key>UTTypeIdentifier</key>
<string>org.myapp.someutiidentifier</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>myapp</string>
</array>
</dict>
</dict>
</array>

Dream up a unique extension for your file, and use an exported UTI declaration to associate the extension with your UTI.

Files are mapped to UTIs by Launch Services, which is theoretically able to use many different kinds of information about the file to identify its type, but in practice will disregard everything other than the file extension. Consequently, if you want your file's type to be identifiable, you need to come up with a unique extension and map it to the relevant type in your app's Info.plist. If your files need to share an extension that has already been claimed by another app, then you're SOL.
As far as I can see -- and I say this as a longtime Mac fan -- this is the one aspect of Mac OS X where it is really apt to say: "Welcome to 1985!"

Related

OSX related file creation

I'm trying to build an application that easily converts from one file format to another. The idea is you drag the source file onto the dock tile and the output file is created alongside the source file (in the same directory.)
After reading the documentation, I have everything setup correctly -- I think... but it doesn't work.
My Info.plist contains the following:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
my_src_type
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
[...]
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
my_dest_type
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>NSIsRelatedItemType</key>
<true/>
[...]
</dict>
</array>
If I'm reading the documentation correctly, this should allow my app to accept files of my_src_type via drag and drop and output files of my_dest_type in the same directory as the input file, provided of course that I let the sandbox know about it.
To that end, I have a SimpleFilePresenter that looks like this:
#interface SimpleFilePresenter : NSObject<NSFilePresenter>
#property (atomic, strong) NSURL *presentedItemURL;
#property (atomic, strong) NSURL *primaryPresentedItemURL;
#end
#implementation SimpleFilePresenter
- (NSOperationQueue *)presentedItemOperationQueue {
return [NSOperationQueue mainQueue];
}
#end
As soon as I use addFilePresenter: to request permission to create my output file, I get the following error in the Console.
2013-04-26 6:33:52.741 PM my_app[27639]: NSFileSandboxingRequestRelatedItemExtension: an error was received from pboxd instead of a token. Domain: NSPOSIXErrorDomain, code: 1
2013-04-26 6:33:52.741 PM my_app[27639]: +[NSFileCoordinator addFilePresenter:] could not get a sandbox extension. primaryPresentedItemURL: file://[...]/file.my_src_type, presentedItemURL: file://[...]/file.my_dest_type
Turns out addFilePresenter: is not synchronous or instantaneous.
All I had to do was call [NSFileCoordinator filePresenters] after addFilePresenter: which seems to have the effect of blocking until all file presenters are ready to go.
Also, I'm using Qt, so I was pleasantly surprised that this all works without needing to get specially created NSURL objects from the more scary looking NSFileCoordinator methods.
Another cause for this error might be an incorrect or missing UTI definition in the project info file.
A UTI must be defined to the document type and it should be identical to UTI defined imported/exported UTIs section (if it's not a built-in UTI).

NSPerformService "Tweet" with an Image (Twitter.app)

Is there anyway to NSPerformService(#"Tweet", [NSPasteboard generalPasteboard]); with an image? I know that simply text is possible, but i don't know about the image.If yes, example code would be nice.
Thank you.
PS: for those who don't know, this is only possible if the user has the Twitter.app installed.
The types that you can send to a service depends upon the types that the registered application has declared in its Info.plist. For Twitter.app, this looks like:
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Tweet</string>
</dict>
<key>NSSendTypes</key>
<array>
<string>NSStringPboardType</string>
</array>
Unfortunately, this means that you will have to transform the image into a string before it will be accepted by the service.

Creating a QuickLook plug-in for text files in Xcode 4.1

Background
I'm learning Objective-C and Cocoa, and I thought creating simple programs to answer my needs would be a nice thing. I already have a solid .NET C# background, some Python knowledge, and a little of C.
One of my "simple first app" I tried to do is a simple QuickLook plug-in for Arduino's sketch files. I thought it would be a simple task to accomplish since these files are plain text C-like scripts, the only "different" thing is they have a .pde extension.
I uploaded the project on GitHub at ArduinoQuickLook as a reference (the first commit contains a vanilla Xcode 4.1 QuickLook plugin-project).
What I found
Looking around the net I found these resources:
QLStephen
QLColorCode
Introduction to Quick Look Programming Guide
What are my problems
Both of them use GeneratePreviewForURL.m and GenerateThumbnailForURL.m files, but when I created the project in Xcode 4.1 it created GeneratePreviewForURL.c and GenerateThumbnailForURL.c (note .c instead of .m).
Both QLStephen and QLColorCode use #import <Foundation/Foundation.h> in their GeneratePreviewForURL.m and GenerateThumbnailForURL.m files, but if I try to #import it it yields to many errors. (Maybe because my files are .c instead of .m?)
It's not clear to me how I declare which files my plug-in will handle, I understood I need to change ArduinoQuickLook/ArduinoQuickLook-Info.plist (row 14) but what I have to write there? Something like cc.arduino.pde?
This tutorial on creating a Quicklook plugin explains things nicely, but to summarise:
Point 1 and 2 are related - for some strange reason the Quicklook plugin template only contains .c files - as such, importing the Obj-C Foundation.h header causes errors.
You should just be able to rename the files from .c to .m and it will work as expected.
It's not clear to me how I declare which files my plug-in will handle
You need to do two things - one is say which UTI (Uniform Type Identifier) your plugin handles (e.g cc.arduino.pde), by changing the line you mention:
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>QLGenerator</string>
<key>LSItemContentTypes</key>
<array>
<string>cc.arduino.pde</string>
</array>
</dict>
</array>
...but you also have to describe that UTI (mostly so it can map the file-extension to that UTI)
There are two slightly different ways to declare UTI's, [as "Declaring New Uniform Type Identifiers"] describes:
Your UTI declarations must be either imported or exported:
An exported UTI declaration means that the type is available for use by all other parties. For example, an application that uses a proprietary document format should declare it as an exported UTI.
An imported UTI declaration is used to declare a type that the bundle does not own, but would like to see available on the system. For example, say a video-editing program creates files using a proprietary format whose UTI is declared in its application bundle. If you are writing an application or plugin that can read such files, you must make sure that the system knows about the proprietary UTI, even if the actual video-editing application is not available. To do so, your application should redeclare the UTI in its own bundle but mark it as an imported declaration.
For a Quicklook plugin, you probably want an "imported" UTI declaration, in which you would add something like this to your Info.plist:
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>cc.arduino.pde</string>
<key>UTTypeReferenceURL</key>
<string>http://www.example.com</string>
<key>UTTypeDescription</key>
<string>Arduino PDE file</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.c-source</string>
<string>public.text</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>pde</string>
</array>
</dict>
</dict>
</array>
Here is the source for a project that provides QuickLook for Processing and Arduino .pde files: https://github.com/kroko/ProcessingQL I know this question is a year old, but this project might be helpful for anyone else attempting this.

How to define a Uniform Type Identifier in a plist file?

My application uses the following NSApplicationDelegate function.
- (void)application:(NSApplication*)sender openFiles:(NSArray*)filenames;
I want to use this function to enable the user to drag and drop image files onto the application icon in the dock.
How do I have to define certain file types in my plist file to restrict them to be images? I found out the structure has to look something like this.
// plist file contents taken from Preview.app
[...]
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeIconFile</key>
<string>jpeg.icns</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSIsAppleDefaultForType</key>
<true/>
<key>LSItemContentTypes</key>
<array>
<string>public.jpeg</string>
</array>
<key>NSDocumentClass</key>
<string>PVDocument</string>
</dict>
</array>
I added it to the plist file but it does not work. A popup window shows the following error message.
The document "test.jpg" could not be opened. MyApp cannot open files
in the "JPEG image" format.
Further, I read in the documentation that there is public.image which would be what I want to define.
Meanwhile, I found out that the plist file only contains the key CFBundleDocumentTypes if I create a Cocoa Application with the option "Create document-based application.". Can you please clarify what dependencies exist for the option?
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>png</string>
<string>jpg</string>
... add as many types as you need
</array>
... other keys
</dict>
</array>
Update: The CFBundleDocumentTypes key is deprecated in Mac OS X v10.5. The new key LSItemContentTypes should be used instead. The items are UTI strings:
<key>LSItemContentTypes</key>
<array>
<string>public.png</string>
</array>
If your document types are common types, you could use UTI's
About UTI's

Registering an icon for my application's document type

I'm trying to register an icon for my app's document type. After reading Declaring New Uniform Type Identifiers and looking at /Developer/Examples/Sketch I came up with something like this in my Info.plist:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>com.mycompany.myextension</string>
</array>
<key>NSDocumentClass</key>
<string>NSString</string>
</dict>
</array>
...
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeDescription</key>
<string>Blah blah blah</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIconFile</key>
<string>My-file-icon.icns</string>
<key>UTTypeIdentifier</key>
<string>com.mycompany.myextension</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>myextension</string>
</array>
</dict>
</dict>
</array>
Now, everything is fine and dandy, i.e. my program is opened when I click on a file with my extension, etc. However, the document icon is not registered with the OS, i.e. I see an ugly blank icon instead of my beautiful My-file-icon.icns. I suspect that I'm missing something in the plist above, any ideas?
Try putting the icon name in the CFBundleTypeIconFile key in the CFBundleDocumentTypes array, not in the UTExportedTypeDeclarations array.
And of course make sure that "My-file-icon.icns" is in your target's Copy Bundle Resources build phase and is being copied into Contents/Resources in your app's bundle.
I've been trying to do this in Xcode 11 and it's been a challenge. There are lots of answers here on SO to questions like this but I couldn't find one that had everything I needed to get this working. I'll try to do that here.
Technical Q&A QA1587 Although this is a bit old I think it has everything required but it's a bit vague in some spots like when it says Provide an icon for the document.
I thought I could make it work with an icon in Assets.xcassets but it didn't work for me until I provided an icns file. I took the 1024x1024 png app icon and converted it to an icns here at cloudconvert. Then I added that icns to my project. I don't think it matters where you put it. I put it just below the top level. It's circled in the image below.
Follow the arrows in the image and you end up at the Info tab. I tapped the Add button to create a Document Type and filled it in as shown. When you tap the Add button in the icon section it will ask you for an icns file. Once I had mine in the project it appeared in the window and I could select it.
Same for Configured UTIs.
That was it. In Files, Messages, or Mail the icon appears.
Your UTI declaration in the Info.plist appears to be correct, however I noticed an other issue. If you application is a document-based application you need to replace the NSString in following entry with your NSDocument subclass:
<key>NSDocumentClass</key>
<string>NSString</string>
For example it's "SKTDocument" in Sketch:
<key>NSDocumentClass</key>
<string>SKTDocument</string>
Edit:
Please also make sure to use your own reverse domain name for your exported UTIs. This ensures that UTIs are unique. For example its com.mindnode.MindNode.MindNodeDocument in my case.
I also had a similar problem, turns out I needed to rebuild the launch services database as per what i have documented here....
https://stackoverflow.com/a/12466968/1571635