Skip to content

Instantly share code, notes, and snippets.

@rjstelling
Last active March 8, 2024 18:21

Revisions

  1. rjstelling revised this gist Mar 8, 2024. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion UniformTypeIdentifierExtensions.swift
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    import Foundation

    // Typesafe UTI, systme APIs are all stringly typed 🙄
    // Typesafe UTI, system APIs are all stringly typed 🙄
    public struct UTI: CustomDebugStringConvertible, CustomStringConvertible {

    public enum Error: Swift.Error {
  2. rjstelling created this gist Apr 1, 2020.
    64 changes: 64 additions & 0 deletions UniformTypeIdentifierExtensions.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,64 @@
    import Foundation

    // Typesafe UTI, systme 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)
    }
    }