3

I tried using QLThumbnailGenerator in Simulator/Device for iOS and iPadOS, but it does not work. I'am able to only obtain a standard empty thumbnail but not the rich icon from my files from documents directory.

Some progress with files in sandbox but nothing useful.

Do you make it work? Maybe something with the permission is wrong...but what? From my app I am able to list files, and read (open) them.

@IBAction func generateDidSelect(_ sender: Any) {  

    let docDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last  
    print("DOC: \(docDir)")  

    let absPath = URL(fileURLWithPath: docDir ?? "").appendingPathComponent("flowers.png").absoluteString  
    //[[NSBundle mainBundle]URLForResource:@"flowers" withExtension:@"png"];  
    let fileURL = URL(fileURLWithPath: absPath)  
    fileURL.startAccessingSecurityScopedResource()  

    let size = CGSize(width: 200, height: 200)  
    let scale = UIScreen.main.scale  

    let isFile = fileURL.isFile()  
    let exists = FileManager.default.fileExists(atPath: fileURL.path)  
    print("isFILE? \(isFile ? "YES" : "NO") exists? \(exists ? "YES" : "NO") \nFILE: \(fileURL)")  

    let request = QLThumbnailGenerationRequest(fileAtURL: fileURL, size: size, scale: scale, representationTypes: QLThumbnailGenerationRequestRepresentationTypeAll)  
    //request.iconMode = YES;  

    QLThumbnailGenerator.shared().generateRepresentations(for: request, updateHandler: { thumbnail, type, error i  

        DispatchQueue.main.async(execute: {  

            print(String(format: "*** TYPE: %ld ****", Int(type)))  
            let uiimage = thumbnail?.uiImage  
            let cgImage = thumbnail?.cgImage  

            if let uiimage = uiimage {  
                print("uiimage: \(uiimage)")  
            }  
            if let cgImage = cgImage {  
                print("cgImage: \(cgImage)")  
           }  

            if uiimage != nil {  
                self.thumbnailImageView.image = uiimage  
            }  

            if error != nil {  
                if let error = error {  
                    print("ERROR: \(error)")  
                }  
                //self.thumbnailImageView.image = [UIImage imageWithContentsOfFile:fileURL.path]; // test read, works  
            }  
        })  
    })  

}  

Then I tried with an image into the bundle.

Getting the file url with:

Bundle.main.url(forResource: "flowers", withExtension: "png")  

and it magically works! ...but no with fileURLWithPath method.

But, accessing the same identical file uploaded via iTunes into the Documents directory of the app i get a read error.

2019-10-01 12:41:27.167091+0200 test_thumb_obj[618:57118] DOC: /var/mobile/Containers/Data/Application/BE4A5950-5D24-4620-A1FE-B837222E8B64/Documents  
2019-10-01 12:41:27.196739+0200 test_thumb_obj[618:57118] isFILE? YES exists? YES   
FILE: file:///var/mobile/Containers/Data/Application/BE4A5950-5D24-4620-A1FE-B837222E8B64/Documents/flowers.png  
2019-10-01 12:41:27.233546+0200 test_thumb_obj[618:57118] *** TYPE: 0 ****  
2019-10-01 12:41:27.233788+0200 test_thumb_obj[618:57118] uiimage:   
2019-10-01 12:41:27.233858+0200 test_thumb_obj[618:57118] cgImage:  (DP)  
  < (kCGColorSpaceDeviceRGB)>  
  width = 493, height = 640, bpc = 8, bpp = 32, row bytes = 1984   
  kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Little  | kCGImagePixelFormatPacked   
  is mask? No, has masking color? No, has soft mask? No, has matte? No, should interpolate? Yes  
2019-10-01 12:41:27.234761+0200 test_thumb_obj[618:57118] *** TYPE: 1 ****  
2019-10-01 12:41:27.234836+0200 test_thumb_obj[618:57118] uiimage: (null)  
2019-10-01 12:41:27.234865+0200 test_thumb_obj[618:57118] cgImage: (null)  
2019-10-01 12:41:27.234943+0200 test_thumb_obj[618:57118] ERROR: Error Domain=QLThumbnailErrorDomain Code=2 "No cached thumbnail"  
2019-10-01 12:41:27.262228+0200 test_thumb_obj[618:57118] *** TYPE: 2 ****  
2019-10-01 12:41:27.262317+0200 test_thumb_obj[618:57118] uiimage: (null)  
2019-10-01 12:41:27.262349+0200 test_thumb_obj[618:57118] cgImage: (null)  
2019-10-01 12:41:27.262452+0200 test_thumb_obj[618:57118] ERROR: Error Domain=QLThumbnailErrorDomain Code=0 "Could not generate a thum  
bnail" UserInfo={NSUnderlyingError=0x281676940 {Error Domain=NSCocoaErrorDomain Code=256 "The file couldn’t be opened."}}  

The only image a get i TYPE = 0, the white empty one.

And, as before, on simulator nothing good....

Error Domain=NSPOSIXErrorDomain Code=22 "couldn't issue sandbox extension com.apple.app-sandbox.read for...  

Some test i missed to make it working?

Fabiosoft
  • 1,141
  • 14
  • 32

3 Answers3

2

I had the same issue. I resolved it by changing representationTypes from .all to .thumbnail

let request = QLThumbnailGenerator
            .Request(fileAt: fileURL, size: size, scale: scale,
                     representationTypes: .thumbnail)

Or if you like, you can try to create a recursion. After 20-30 calls of generateRepresentations the error "No cached thumbnail" stopped appearing and the image was available.

func thumbnail(for fileURL: URL, size: CGSize, scale: CGFloat) {
    QLThumbnailGenerator.shared.generateRepresentations(for: request) { (thumbnail, type, error) in

        let uiimage = thumbnail?.uiImage
        if uiimage == nil {
             thumbnail(fileURL, size, scale)
        }
        else {
             //image is available
        }
    }
}
  • I don't think recursion is suitable, but by now i'am satisfied of dummy thumbnail on simulator, and device real icon. – Fabiosoft Apr 21 '21 at 08:53
0

Solved on iOS13.2 beta. On device ok, still read issues on simulator

Fabiosoft
  • 1,141
  • 14
  • 32
0

This worked for me, I create this method:

import QuickLook

...

func generateThumbnailFromQuickLook(url: URL,
                                    type: QLThumbnailGenerator.Request.RepresentationTypes,
                                    completionHandler: @escaping (Bool, UIImage?) -> Void) {
    let size = CGSize(width: 300, height: 250)
    let scale = UIScreen.main.scale
    
    let request = QLThumbnailGenerator.Request(fileAt: url,
                                               size: size,
                                               scale: scale,
                                               representationTypes: type)
    
    let generator = QLThumbnailGenerator.shared
    
    generator.generateBestRepresentation(for: request) { thumbnail, error in
        if let error = error {
            print(error)
        }
        
        completionHandler(thumbnail != nil, thumbnail?.uiImage)
    }
}

Then I check if the representation type thumbnail fail. If it fails, I try to get the representation type all:

let url: URL = <url file>

generateThumbnailFromQuickLook(url: url, type: .thumbnail) { success, image in
    if success == false {
        generateThumbnailFromQuickLook(url: url, type: .all) { success, image in
            // use the thumbnail here
        }
    } else if let image = image {
        // use the thumbnail here
    }
}

pableiros
  • 14,932
  • 12
  • 99
  • 105