Convenience init extension for CG/CF types

Why can't I write a convenience init extension for CG/CF types ? Is it about ARC (releases) ?

Error: Convenience initializers are not supported in extensions of CF types

1 Like

Although it doesn't really answer the question, this might provide some insight: SR-1801

Let's wait for someone from core (or someone who knows) to elaborate on this.

No, that's pretty much the answer.

Specifically, convenience initializers can be inherited, and your new one can't be in a safe way. The feature we need here is what we call "factory initializers" inside the compiler. (It's not out of the question, either! Just still needs some design.)

A convenience initializer always has to return the Self type, but you can't guarantee that with the "initializers" that get imported from C. Those only promise to return one particular type.

Does this mean CGPath, or rather Core Graphics, is a C framework bridged to Swift?

Oh, it really is! (Core Foundation) CFArray.c

Ah, the latter. Frameworks built on Core Foundation are C-compatible (though they do use Objective-C in their implementation).

2 Likes

What's the state of this these days (factory initializers)? I just ran into this with CGImage, wherein I tried to make a convenience initializer for creating a CGImage from PNG data.

I don't really understand why I can make a function to do the work but not an initializer, but putting that aside, would it have been possible to allow convenience initializers in extensions of final classes? That would remove the issue of inheriting the initializer. Does it make sense to allow subclassing CF types?

1 Like

Isn't this the case for Objective-C initializers as well (as in, they can return types other than the type they belong to)?

In fact, Objective-C framework authors (including Apple) routinely take advantage of this fact by calling it The Class Cluster Pattern, where the base class is exposed to the client code, the subclasses are hidden, and the base class's initializer makes a decision which subclass to initialize and return based on the initializer's parameters.

I understand that for any type whose instance layout is more than a mere pointer will no work with this approach, because the compiler needs to know the layout of a value in order to represent it in memory (or have it wrapped in an existential, which loses the type). But all CF-style imported C types are effectively reference types whose instance layout is exactly a mere pointer, so in practice, they're handled exactly like an Objective-C class.

Please correct me if I got any of this wrong.

Will a standalone function work for you?

func CGImage(pngData: Data) -> CGImage? {
    guard let provider = CGDataProvider(data: pngData as CFData) else {
        return nil
    }
    return CGImage(pngDataProviderSource: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)
}
3 Likes

Yeah, I suppose so. It didn't occur to me I could name it like that. Just seems like a contrivance to not be able to just make an initializer.