Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Factory of generic types in Swift (Protocols and generics)

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
    }
}
like image 646
OscarVGG Avatar asked Jul 29 '15 20:07

OscarVGG


People also ask

What is generic type in Swift?

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.

What are generic protocols?

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.

What is the difference between any and generic in Swift?

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.


1 Answers

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.

like image 149
salman140 Avatar answered Oct 10 '22 11:10

salman140