Better Uniform Type Identifiers with Xcode 12

If you’ve been doing any kind of file uploads, downloads, previews, and such in past versions of Xcode and needed to deal with MIME types or file extensions, you’ve probably had to deal with Uniform Type Identifers. Using CoreServices and some of its methods such as UTTypeCreatePreferredIdentifierForTag and UTTypeCopyPreferredTagWithClass could avoid some hard-coding of identifiers and such; however these methods are more difficult to work with as they involve unmanaged CFString classes as well as iteration through tag dictionaries in some cases.

Enter the New UTType Class

With Xcode 12 and iOS 14 (macOS 11, watchOS 7, or tvOS 14 as well), we now have an awesome new Swift-based UTType struct that abstracts a lot of the complexity out and makes life simpler when dealing with file extensions and MIME types for documents from Microsoft Office, Adobe Acrobat, or even your own custom file types. By providing properties such as preferredMimeType, preferredFilenameExtension, and identifier, developers will now be able to craft code more cleanly in Swift with less effort.

For example to get the key components for an Adobe Acrobat PDF document, we can just use import UniformTypeIdentifiers and do this.

import UniformTypeIdentifiers

let pdf: UTType = .pdf

print("MIME type: \(pdf.preferredMIMEType!)")   //application/pdf
print("File Extension: \(pdf.preferredFilenameExtension!)") //pdf
print("Identifier: \(pdf.identifier)")  //com.adobe.pdf

You can also infer a full UTType object from a file extension or MIME type with the static types(tag:tagClass:conformingTo:) method as follows.

import UniformTypeIdentifiers

let word: UTType = .word

print("MIME type: \(word.preferredMIMEType!)")   //application/vnd.openxmlformats-officedocument.wordprocessingml.document
print("File Extension: \(word.preferredFilenameExtension!)") //docx
print("Identifier: \(word.identifier)")  //org.openxmlformats.wordprocessingml.document

extension UTType {
    // Word documents are not an existing property on UTType
    static var word: UTType {
        // Look up the type from the file extension
        UTType.types(tag: "docx", tagClass: .filenameExtension, conformingTo: nil).first!
    }
}

Using UTType with Existing Functions

Obviously it will take Apple a while to spread this new class out through all the existing areas in the app but you can still start leveraging UTType in your existing code today. Many of the existing functions that accept strings can just be altered to use the new types instead of raw, hard-coded strings or the existing UTI’s that were in CoreServices in UTCoreTypes.h. For example, when donating a user activity for Spotlight, it’s necessary to spin up a CSSearchableItemAttributeSet to append thumbnails or properties such as keywords. Previously this would have resulted in some code looking like this with kUTTypeItem in this case.

let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String)

We can now replace that with this in iOS 14 code and possibly remove a reference to CoreServices.

let attributes = CSSearchableItemAttributeSet(itemContentType: UTType.item.identifier)

Fortunately some existing methods are already being updated. Where you might have done something like this in the past to initialize a UIDocumentPickerViewController for PDF and Word documents,

// Word not available in UTCoreTypes
let documentTypes = [kUTTypePDF as String, "org.openxmlformats.wordprocessingml.document"]
        
// Initialize document picker with the allowed types
let documentPickerViewController = UIDocumentPickerViewController(documentTypes: documentTypes, in: .import)

You can now do this with UTType with some type safety and less hard-coding of strings using this overload of init on UIDocumentPickerViewController.

//PDF is a known type but word needs to be looked up
var documentTypes: [UTType] = [.pdf, .word]
let documentPickerViewController = UIDocumentPickerViewController(forOpeningContentTypes: documentTypes)

extension UTType {
    //Word documents are not an existing property on UTType
    static var word: UTType {
        UTType.types(tag: "docx", tagClass: .filenameExtension, conformingTo: nil).first!
    }
}

What To Do Next?

So after you fix up your code and get rid of some of those old references to UTTypeCore, you might also want to check out defining your own Uniform Type Identifier, especially if your app has a custom document format that other apps can leverage.

It’s great to see Apple modernizing some of the core functionality and introducing Swift compatibility with code that’s been baked in for years. Changes like this make it easier for us as developers to move forward and write stable, maintainable code.

Get the Source

Download sample code with some of the above examples from Github.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: