6

This code type checks and compiles but then crashes. How do I save a CGImage to Data so that I can read it in again later.

let cgi: CGImage? = ...        
var mData = Data()
let imageDest = CGImageDestinationCreateWithData(mData as! CFMutableData, 
                                                 kUTTypePNG, 1, nil)!
CGImageDestinationAddImage(imageDest, cgi!, nil)
CGImageDestinationFinalize(imageDest)

The last line crashes. Error in console is:

2018-01-17 19:25:43.656664-0500 HelloPencil[2799:3101092] -[_NSZeroData 
  appendBytes:length:]: unrecognized selector sent to instance 0x1c80029c0
2018-01-17 19:25:43.658420-0500 HelloPencil[2799:3101092] *** Terminating app 
  due to uncaught exception 'NSInvalidArgumentException', reason: 
  '-[_NSZeroData appendBytes:length:]: unrecognized selector 
  sent to instance 0x1c80029c0'

That cast from Data to CFMutableData was recommended by Xcode, but maybe it's wrong.

Rob N
  • 12,931
  • 12
  • 81
  • 149

1 Answers1

12

The problem is the way you are creating your mutable data. Data is not convertible to NSMutableData. Just change your forced casting from Data to CFMutableData to CFDataCreateMutable(nil, 0). Another option is to use NSMutableData which is toll-free bridged to CFMutableData. Try like this:

if let cgi = cgi, 
    let mutableData = CFDataCreateMutable(nil, 0),
    let destination = CGImageDestinationCreateWithData(mutableData, "public.png" as CFString, 1, nil) {
    CGImageDestinationAddImage(destination, cgi, nil)
    if CGImageDestinationFinalize(destination) {
        let data = mutableData as Data
        if let image = UIImage(data: data) {
            print(image.size)
        }
    } else {
        print("Error writing Image")
    }
}

edit/update: Xcode 11 • Swift 5.1

extension CGImage {
    var png: Data? {
        guard let mutableData = CFDataCreateMutable(nil, 0),
            let destination = CGImageDestinationCreateWithData(mutableData, "public.png" as CFString, 1, nil) else { return nil }
        CGImageDestinationAddImage(destination, self, nil)
        guard CGImageDestinationFinalize(destination) else { return nil }
        return mutableData as Data
    }
}
Leo Dabus
  • 216,610
  • 56
  • 458
  • 536
  • Thanks, that fixes it. I may report a bug about the cast. If a cast from A to B succeeds, I don't think it should die later because A wasn't really a B. – Rob N Jan 18 '18 at 14:13