I'm trying to create a factory of generic types that implement a protocol. The problem is that in the make
method of the adapter factory I get the following error: Protocol 'Adapter' can only be used as a generic constraint because it has Self or associated type requirements
.
Here is an example of what I'm doing now:
protocol Adapter {
typealias T
static func method1(parameter: T)
}
final class AdapterFactory<T>: NSObject {
static func make(name: String = "") -> Adapter.Type {
switch name {
case "Adapter1":
return ConcreteAdapter1<T>.self
default:
return ConcreteAdapter2<T>.self
}
}
}
final class ConcreteAdapter1<T>: NSObject, Adapter {
static func method1(parameter: T) {
// bla, bla, bla
}
}
Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner.
Making a Protocol Generic. There are two ways to create a generic protocol - either by defining an abstract associatedtype or the use of Self (with a capital S). The use of Self or associatedtype is what we like to call "associated types". This is because they are only associated with the protocol they are defined in.
So if we use Generics then we can write flexible functions, structures, classes, and protocols without compromising Swift's type safety. But if we use the Any type, then we are kind of our own boss, we can do almost anything we want.
You can use a pattern used in swift standard library(see Sequence and AnySequence) i.e using something like AnyAdapter that implements the Adapter protocol by delegating every method call to the underlying implementation(a concrete implementation of Adapter protocol like ConcreteAdapter1) or by using closures.
Your factory would then return AnyAdapter instead of Adapter. It may seems unnatural at first but using AnyAdapter as the type gives the same advantage as using the protocol (obviously it is a workaround) since AnyAdapter is not a concrete implementation by itself instead it delegates the implementation to a concrete implementation.
Here's the code
protocol Adapter {
typealias Element
func method1(parameter: Element)
func method2(parameter : Element)
}
struct AnyAdapter<Element> : Adapter {
private let _method1 : (Element) -> ()
private let _method2 : (Element) -> ()
init<A:Adapter where A.Element == Element>(_ base:A) {
_method1 = { base.method1($0) }
_method2 = { base.method2($0) }
}
func method1(parameter: Element) {
_method1(parameter)
}
func method2(parameter: Element) {
_method2(parameter)
}
}
final class ConcreteAdapter1<T>: NSObject, Adapter {
func method1(parameter: T) {
print("Concrete Adapter 1 method 1")
}
func method2(parameter: T) {
print("Concrete Adapter 1 method 2")
}
}
final class ConcreteAdapter2<T> : Adapter {
func method1(parameter: T) {
print("Concrete adapter 2 method 1")
}
func method2(parameter: T) {
print("Concrete Adapter 2 method 2")
}
}
final class AdapterFactory<T>: NSObject {
static func make(name: String = "") -> AnyAdapter<String> {
switch name {
case "Adapter1":
let concreteAdapter1 = ConcreteAdapter1<String>()
return AnyAdapter(concreteAdapter1)
default:
let concreteAdapter2 = ConcreteAdapter2<String>()
return AnyAdapter(concreteAdapter2)
}
}
}
I am not using a static method in protocol to make things simpler since statics don't operate well with generic types.
To be honest this is a shortcoming in the language and i would like it work like this thing to be simplified like in java or C#.
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With