Skip to content

Instantly share code, notes, and snippets.

@rjstelling
Last active March 8, 2024 18:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rjstelling/0e3298a42c2a2bca2f75f193578521c3 to your computer and use it in GitHub Desktop.
Save rjstelling/0e3298a42c2a2bca2f75f193578521c3 to your computer and use it in GitHub Desktop.
A couple of extensions to make working with UTI in Swift less painful.
import Foundation
// Typesafe UTI, system APIs are all stringly typed 🙄
public struct UTI: CustomDebugStringConvertible, CustomStringConvertible {
public enum Error: Swift.Error {
case invalidCharachters
}
private let rawUTI: String
public var debugDescription: String { "UTI -> \(rawUTI)" }
public var description: String {
guard let desc = UTTypeCopyDescription(rawUTI as CFString)?.takeUnretainedValue() else { return "Unknown UTI" }
return desc as String
}
init(_ uti: String) throws {
try self.init(withString: uti)
}
init(_ uti: CFString) throws {
try self.init(uti as String)
}
private init(withString utiStr: String) throws {
let regex = try NSRegularExpression(pattern: "^[\\x{80}-\\x{FFFF}0-9aA-zZ.-]*$", options: [])
let range = NSRange(utiStr.startIndex..<utiStr.endIndex, in: utiStr)
if regex.firstMatch(in: utiStr, options: [], range: range) != nil {
rawUTI = utiStr
}
else {
throw Error.invalidCharachters
}
}
}
// Get a UTI from a URL (this uses only the file extention not the OSType identifier)
extension URL {
public enum UTIError: Error {
case notLocalFile
}
public func uniformTypeIdentifier() throws -> UTI {
guard self.isFileURL else { throw UTIError.notLocalFile }
let extention = self.pathExtension
// This will only be nil if kUTTagClassFilenameExtension is unreconised,
// as this is a system level constant we will have other issues!
let conformTo = self.hasDirectoryPath ? kUTTypeDirectory : nil
let unmanagedUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
extention as CFString,
conformTo)!
let uti = unmanagedUTI.takeUnretainedValue() as String
return try UTI(uti)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment