Search code examples
swiftcore-graphicsnsdata

NSData from CGImageRef in Swift


I am having difficulty figuring out how to get an NSData representation of an image from a CGImageRef. All of the answers I've found make use of UIImage or NSImage, but my application is cross-platform, so I want to use only Core Graphics. Objective-C answers state simply that CFData is toll-free bridged to NSData and simply cast it, but Swift will not allow this. The closest I've got is:

var image: CGImageRef? = nil

//...
if let dataProvider: CGDataProviderRef = CGDataProviderCreateWithURL(url) {
    image = CGImageCreateWithPNGDataProvider(dataProvider, nil, false, CGColorRenderingIntent.RenderingIntentDefault) 
    // works fine
    //...
    if let data = CGDataProviderCopyData(CGImageGetDataProvider(image)) as? NSData {
        // Do something with data, if only it ever got here!
    }
}

but the cast doesn't ever succeed...


Solution

  • CGDataProviderCopyData() returns the optional CFData?, and that cannot be cast to the non-optional NSData. But you can convert/bridge it to NSData? and use that in the optional binding:

    if let data = CGDataProviderCopyData(CGImageGetDataProvider(image)) as NSData? {
        // Do something with data ...
    
    }
    

    Here is a simpler example demonstrating the same issue with CFString and NSString:

    let cfstr : CFString? = "Hello world"
    if let nsstr = cfstr as? NSString {
        print("foo") // not printed
    }
    if let nsstr = cfstr as NSString? {
        print("bar") // printed
    }
    

    But I admit that my explanation is not fully satisfying, because a similar optional cast works in other cases:

    class MyClass { }
    class MySubclass : MyClass { }
    
    let mc : MyClass? = MySubclass()
    if let msc = mc as? MySubclass {
        print("yes") // printed
    }
    

    So this must be related to the toll-free bridging between CoreFoundation and Foundation types.