Now I would like to have a factory that implement this protocol for multiple types such as UILabel, UIImageView, ect ...
The first problem would be that the current syntax does not allow it
Definitely feels wrong and should not be used as such, at least in my opinion.
A good solution would be to use a different syntax for protocol with associated type, a syntax similar to the one for generics.
We would create the protocol like this :
However, my understanding is that this would require the oft-requested, oft-rejected feature of generic protocols, which the core team has explicitly ruled out for the foreseeable future.
My (imperfect) understanding is that the fact that a type can only adopt a single type for an associated type is the distinguishing feature of associated types. That is, with associated types, itβs possible to ask βWhat is the single, unique element type of this Collection?β whereas with generic protocols, one can only ask βWhat are the (possibly many) element types of this Collection?β I believe the stdlib makes heavy use of this unique conformance guarantee.
(Stdlib authors and experts who understand this better than I do, please correct me here if Iβm off the mark.)
Your protocol isn't really correct - ActualFactory isn't really a factory, but a repository of factories. Each type should have its own conformer. The simplest way to do this today is with a generic type:
struct Factory<T> {
private let _create: ()-> T
func create() -> T { return _create() }
init(_ creator: @escaping ()->T) { self._create = creator }
}
enum Factories {
static let viewFactory = Factory { return UIView() }
static let labelFactory = Factory { return UILabel() }
// ...
}
let aView = Factories.viewFactory.create()
So that's the simple case.
If you wanted to support multiple kinds of factories, you could, for example rename Factory<T> -> SimpleFactory<T>, add a ComplexFactory<T>, and create a common protocol for them:
The only "problem" here is that everybody can see which kind of Factory you return (SimpleFactory or ComplexFactory, respectively). Ideally you'd just like to tell them that you return Any<Factory where Object == UIImage> or something opaque like that, but that would require generalised existentials, which are on the roadmap sometime.
So for now you need to leak the information about which concrete type you use, or create an AnyFactory<T> wrapper to hide the implementation detail. But it's all possible.
AFAIK, we are discouraged from overloading methods based on returned value (i.e. the <T> parameter being only in the return value). With createObject, you'd be getting exactly that...
Generic protocols are the feature that would allow us implementing one protocol multiple times:
Excerp from the Generics Manifesto:
One of the most commonly requested features is the ability to parameterize protocols themselves. For example, a protocol that indicates that the Self type can be constructed from some specified type T:
Implicit in this feature is the ability for a given type to conform to the protocol in two different ways. A Real type might be constructible from both Float and Double, e.g.,
struct Real { ... }
extension Real : ConstructibleFrom<Float> {
init(_ value: Float) { ... }
}
extension Real : ConstructibleFrom<Double> {
init(_ value: Double) { ... }
}
It is literally the first thing under "Unlikely": "Features in this category have been requested at various times, but they don't fit well with Swift's generics system because they cause some part of the model to become overly complicated, have unacceptable implementation limitations, or overlap significantly with existing features."
I believe the way we model this is by exposing "views" of the underlying data. For example, String has multiple conformances to Collection, exposed as "views" (as Characters, unicode scalars, UTF8 code-units, etc). Each of them is a wrapper around the same underlying data.
The way to model your example in Swift would be to flip the protocol - instead of Real being constructible from a Float, it is Float which is expressible as a Real. Again, I would point to the standard library's LosslessStringConvertible as an example. This approach is also cleaner from an encapsulation perspective and makes it easier for other types to conform to the protocol; rather than writing an initialiser on Real, and build knowledge of Float or Double in to Real, the knowledge of those types stays within them, and they simply need to know how to express their own data as a Real.
It also allows you to write default implementations of var realValue: Real { get } in protocol extensions, which produce a Real by inspecting other properties. You couldn't write a default designated initialiser - it would have to be a convenience initialiser.