That's going to change soon once SE-327 is fully implemented. The presence of convenience will become a warning with a fix-it that simply deletes the keyword, as it's not needed for actors because of the lack of inheritance.
Since this is pretty fresh on my mind, the compiler does make a few internal distinctions between designated and convenience initializers, so it's not purely for readers. In particular, it's an ABI break to add or remove convenience for a class, and that's published in the *.swiftinterface file for a compiled module. The compiler relies on that to know how to work with the initializer.
First, in the Swift language itself, when a programmer writes a designated initializer, they're responsible for initializing all of the stored properties of self before making any other use of it. That contract is checked by the compiler in the same manner as if you had declared a local variable without an initializing expression. Convenience (or delegating) inits differ in that they must delegate to a designated initializer of the same class (as the diagram depicts) before any uses of self or returning.
In terms of implementation details, the two kinds of initializers in Swift have some differences. Designated initializers have two entry-points, whereas convenience inits only have one (which does all of the things that the designated init does in one function). Why? I believe it has to do with a combination of memory allocation responsibilities, the need for dynamic type metadata to do the allocation (in some cases), and hold-overs from how Objective-C chains initialization of a subclass to a superclass.
The first entry-point of a designated initializer performs allocation by accepting dynamic type information as a parameter (as passed-in by the caller), which can be used to describe the type's size and layout in order to allocate a correctly sized instance, etc. That first entry-point gets published in the class's vtable, so that entry-point is what you're really overriding in a class. From what I've seen in the compiler, that type parameter is definitely used by Obj-C classes, as all of the Swift classes I've tested knows its type statically and uses that, ignoring the dynamic type argument.
Now, the second entry-point of a designated initializer is where the body of the designated initializer, as it appears in the original source, is actually emitted. But, instead of accepting type metadata as a parameter, this entry-point accepts an instance of that class itself for it to initialize. So it's kind of like this:
// MyClass._allocating_init(designated:)
sil_func MyClass_allocating_init_designated(..., type: TypeMetadata) {
let self = // ... do memory allocation, etc ...
MyClass_init_designated(..., self)
}
// MyClass.init(designated:)
sil_func MyClass_init_designated(..., self: MyClass) {
/// do initialization
}
// MyClass._allocating_init(convenience:)
sil_func MyClass_init_convenience(..., type: TypeMetadata) {
/// delegate to an init
/// do any further initialization
}
That is really the key difference between designated and convenience. The first point where you enter a class's initializer determines the type metadata for the instance to be allocated (as passed-in by the caller, to support generics, etc). After that, the initialization chaining for super-classes goes through these secondary entry-points, so that the super-classes only need to initialize the parts of the instance it knows about, knowing nothing about the underlying instance that was passed in.
To make it a bit more visual, it's sort of like this:
/// Superclass
+---------------------------------------------------+
| Designated init |
| [ allocating entry ] --> [ initializing entry ] |
| ^
+----------------------------------------|----------+
|
| super.init(...)
// Subclass |
+--------------------------------------- | ---------+
| Designated init | |
| [ allocating entry ] --> [ initializing entry ] |
+---------------------------------------------------+
Why not always emit two entrypoints to eliminate the distinction? I don't know. This is probably tied to some history or other design trade-offs that I'm not aware of.
Also, in theory it shouldn't need to be an ABI break to add/remove convenience for a final class, but that does appear to be the case for a reason I haven't investigated yet.