skip to Main Content

I’m using the PhotosPicker library to select either an image or a video and put it into data:

@State private var selectedImageData: Data? = nil

and I need to find out what exactly was selected.

All the answers I’ve found was about how to retrieve the meme type from a file using the path, but I don’t get the file path using PhotosPicker.

I’ve found this somewhere:

extension Data {
    private static let mimeTypeSignatures: [UInt8 : String] = [
        0xFF : "image/jpeg",
        0x89 : "image/png",
        0x47 : "image/gif",
        0x49 : "image/tiff",
        0x4D : "image/tiff",
        0x25 : "application/pdf",
        0xD0 : "application/vnd",
        0x46 : "text/plain",
        ]

    var mimeType: String {
        var c: UInt8 = 0
        copyBytes(to: &c, count: 1)
        return Data.mimeTypeSignatures[c] ?? "application/octet-stream"
    }
}

and it works but it’s incomplete, I also need mp4, mov and just as many as possible

2

Answers


  1. A quick research on Google with "mime types hexadecimal signatures" would give you this or this, for example…


    EDIT

    The array of signatures in your code reads the first byte of data of the file to determine its type.

    • So first, according to the links I provided, is that to use just one byte might not be enough to determine the type of data (there can be multiple types with same first byte). You should first modify your code so a signature can be several bytes long.
    • Then, you should use the links I provided, where, even if in different language, you get a list of first bytes linked to the related format, to fill your array of signatures and according types.

    Here is the example code of what I am saying :

    extension Data
    {
        private static let mimeTypeSignatures: [([UInt8], String)] = [
            ([0x49, 0x49, 0x2A, 0x00], "image/tiff"),
            ([0x4D, 0x4D, 0x00, 0x2A], "image/tiff"),
            ([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A], "image/png"),
            //...
        ]
    
        var mimeType: String
        {
            let tenFirstBytes = Array(subdata(in: 0..<10))
            return Data.mimeTypeSignatures.filter
                       { signature in
                           tenFirstBytes.starts(with: signature.0)
                       }
                       .first?.1 ?? "application/octet-stream"
        }
    }
    

    It converts the 10 first bytes of the data to an array, then checks for each MIME type if the array starts with the MIME type bytes. (You will need to take more than ten bytes if MIME signatures are more than 10 bytes long).

    Login or Signup to reply.
  2. You can use the Signature re-using the code here

    After copy-pasting the logic and adding the cases for tiff, vnd or text/plain, you will end up with this logic:

    enum ImageFormat {
        case gif, jpg, png, vnd, tiff, unknown
    }
    if let data = someData {
        switch data.format() {
        case .jpg, .png, .gif:
            print ("Formats supported by Swift. Yihaaa")
        case .tiff:
            print ("Do some fancy decoding")
        case .pdf:
            print ("Read as a file")
        case .unknown:
            print ("Treat this data differently")
        }
    }
    

    If you do the comparison using a String, make sure to decode using . isoLatin1. If you compare the bytes using the keys inside your mimeTypeSignatures, then no need to. I only personally found it more readable to use Strings. It was helpful for decoding and to add tests.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search