Protocols don't provide any implementation, but that doesn't mean the only use they have is for conformance on concrete types. You can (and should) use protocols as types. Protocols are first class citizens, as are functions, classes, structures, and enums, which means you can use them anywhere you'd be able to use any other type:
- As a return type—parameter of a function
- As a member—variable or constant
- Within arrays, dictionaries, or other container types
You should already be very comfortable with using protocols as types. Delegation leverages this widely:
protocol RunnerDelegate: class {
func didStart(runner: Runner)
func didStop(runner: Runner)
}
class Runner {
weak var delegate: RunnerDelegate?
func start() {
// Start the runner
delegate?.didStart(runner: self)
}
func stop() {
delegate?.didStop(runner: self)
}
}
In the previous example, we define a delegation protocol, RunnerDelegate, which helps convey to another object, delegate, actions that were taken on Runner.
One interesting feature of protocols as types is the ability to compose these protocols. Instead of creating protocol hierarchies, Swift makes it easy to introduce compounded protocols:
typealias HashAndEquatable = Hashable & Equatable
As typealias is a proper type as well, you can use these composed protocols in your objects.
typealias RunnerDelegateView = NSView & RunnerDelegate
The previous code is a valid typealias that would require the variable to be both NSView and RunnerDelegate. You may use this pattern if you need a specific conformance from a concrete class.