There is another solution that was not mentioned in this question that uses the type erasure method. To get an abstract interface for a common protocol, create a class or structure that wraps an object or structure that conforms to the protocol. The wrapper class, usually called "Any {protocol name]," itself conforms to the protocol and implements its functions by forwarding all calls to the internal object. Try the example below on the playground:
import Foundation public protocol Printer { typealias T func print(val:T) } struct AnyPrinter<U>: Printer { typealias T = U private let _print: U -> () init<Base: Printer where Base.T == U>(base : Base) { _print = base.print } func print(val: T) { _print(val) } } struct NSLogger<U>: Printer { typealias T = U func print(val: T) { NSLog("\(val)") } } let nsLogger = NSLogger<Int>() let printer = AnyPrinter(base: nsLogger) printer.print(5)
The printer type is known as AnyPrinter<Int> and can be used to abstract any possible implementation of the printer protocol. While AnyPrinter is not abstractly abstract, its implementation simply comes down to the actual implementation type and can be used to decouple implementation types from types using them.
It should be noted that AnyPrinter does not need to explicitly save the base instance. Actually, we cannot, since we cannot declare AnyPrinter have the Printer<T> property. Instead, we get a pointer to the _print function on the underlying print function. A call to base.print , without calling it, returns a function in which the base is specified as its own variable, and therefore is saved for future calls.
Another thing to keep in mind is that this solution is essentially another layer of dynamic sending, which means a slight decrease in performance. In addition, the type erase instance requires additional memory on top of the main instance. For these reasons, erasing a type is not a free abstraction.
Obviously, there is some work to configure type erasure, but it can be very useful if an abstraction of the general protocol is required. This template is found in a fast standard library with types such as AnySequence . Further reading: http://robnapier.net/erasure
BONUS:
If you decide you want to implement the same printer implementation everywhere, you can provide a convenience initializer for AnyPrinter that introduces this type.
extension AnyPrinter { convenience init() { let nsLogger = NSLogger<T>() self.init(base: nsLogger) } } let printer = AnyPrinter<Int>() printer.print(10)
This can be a simple and harsh way of expressing dependencies for the protocols that you use in your application.