2

I'm trying to create an image from the contents of (all subviews) an NSImageView and save it to disk on a Mac. Right now the step of writing it to disk is failing. As I step through the code in the debugger, I notice that imageData doesn't appear to be created properly. The Variables View shows imageData's value as some and when I look deeper the field backing.bytes is nil.

enter image description here

My guess is this line:

let imageData: Data! = rep!.representation(using: NSBitmapImageRep.FileType.png, properties: [:])

is failing. Here is the full code I am using:

class ExportableImageView: NSImageView {

    func saveToDisk() {
        let rep: NSBitmapImageRep! = self.bitmapImageRepForCachingDisplay(in: self.bounds)
        self.cacheDisplay(in: self.bounds, to: rep!)

        let imageData: Data! = rep!.representation(using: NSBitmapImageRep.FileType.png, properties: [:])
        let paths = NSSearchPathForDirectoriesInDomains(.desktopDirectory, .userDomainMask, true)
        let desktopPath = URL.init(string: paths[0])
        let savePath = desktopPath?.appendingPathComponent("test.png")
        do {
            try imageData!.write(to: savePath!, options: .atomic)
        }
        catch {
            print("save error")
        }
    }

    /* Other stuff */
}

Any ideas why this would be failing? Thanks.

Kevin_TA
  • 4,575
  • 13
  • 48
  • 77
  • 2
    Your app it is probably sandboxed. If you would like to be able to save anything out of your sandbox bundle location you would need to present NSSavePanel to let the user select the destination folder or disable sandbox in your app capabilities – Leo Dabus Feb 19 '18 at 02:28
  • My guess is this line:let savePath = desktopPath?.appendingPathComponent("test.png") – El Tomato Feb 19 '18 at 02:29
  • You can't just create a file anywhere you want. – El Tomato Feb 19 '18 at 02:29
  • Where does rep come from and why are you force-unwrapping it? – El Tomato Feb 19 '18 at 02:31
  • @ElTomato rep it is declared at the first line of the same method – Leo Dabus Feb 19 '18 at 02:33
  • You are right. Thanks. – El Tomato Feb 19 '18 at 02:35
  • 1
    "You should consider using the FileManager methods urls(for:in:) and url(for:in:appropriateFor:create:). which return URLs, which are the preferred format." – Willeke Feb 19 '18 at 12:50
  • @Willeke you were right. What turned out to work in the end was changing the way I was getting the desktop path. I ended up using `let desktopPath = try! fileManager.url(for: .desktopDirectory, in: .allDomainsMask, appropriateFor: nil, create: true)` – Kevin_TA Feb 19 '18 at 16:58

1 Answers1

1

Thanks to Willeke's suggestion, I only had to change the way in which I was getting the path to the desktop to

let desktopPath = try! fileManager.url(for: .desktopDirectory, in: .allDomainsMask, appropriateFor: nil, create: true)

Here is the final solution

func saveToDisk() {
    let rep: NSBitmapImageRep! = self.bitmapImageRepForCachingDisplay(in: self.bounds)
    self.cacheDisplay(in: self.bounds, to: rep!)
    let imageData: Data! = rep!.representation(using: NSBitmapImageRep.FileType.png, properties: [:])

    let fileManager = FileManager.default
    let desktopPath = try! fileManager.url(for: .desktopDirectory, in: .allDomainsMask, appropriateFor: nil, create: true)            
    let filePath = desktopPath.appendingPathComponent("test.png")
    do {
        try imageData.write(to: filePath, options: .atomic)
    }
    catch {
        print("save file error: \(error)")
    }
}
Kevin_TA
  • 4,575
  • 13
  • 48
  • 77