Generic Protocols

In Swift, why cannot we use generic protocols as normal protocols?

Generic protocol can only be used as a generic constraint because it has Self or associated type requirements.

Swift error

In Swift, the above is the error we encounter when we try to use a protocol with associated types as the type of a variable or input function directly. So what is the problem with using generic protocols as normal protocols?

Suppose that the NetworkAPIProviderInterface. It is a generic protocol that is conformed by the types that can make URLRequest form a RequestModel. As the RequestModel is unknown for the NetworkAPIProviderInterface, it must be defined as a generic type using the associatedtype keyword.

protocol NetworkAPIProviderInterface {
		
	associatedtype RequestModel
		
	func prepareURLRequest(request: RequestModel) throws ->   URLRequest
}

Now imagine there is an IncidentsProvider that uses an instance of a type that implements the NetworkAPIProviderInterface to make proper URLRequest.

class IncidentsProvider {	
	let networkAPIProvider: NetworkAPIProviderInterface? = nil
}

The Swift compiler raises the following error:

Protocol ‘NetworkAPIProviderInterface’ can only be used as a generic constraint because it has Self or associated type requirements.

What is the reason behind this error and how to solve that?
To understand the reason of the error, suppose we want to call the prepareURLRequest mehtod. We know that the input request of the prepareURLRequest must be of type NetworkAPIProviderInterface.ReqeustModel, but no one knows what it is exactly and, there is no means to define that here. That is the reason of the Xcode error.

The solution to the above problem is explained by the error message too, which is generic constraints. Generic types allow us to define generic constraints. By generic constraints we specify what the NetworkAPIProviderInterface.ReqeustModel is exactly. Generic constraints are declared by the where clause.

class IncidentsProvider<T> where T: NetworkAPIProviderInterface, T.RequestModel == String {
		
	let apiProvider: T
		
	init(apiProvider: T) {
		self.apiProvider = apiProvider
	}
		
	func getIncidents() throws -> String {
			
		try apiProvider.prepareURLRequest(request: "https://.exapmle.com/")
			
		//implementing other logics here
			
		return "result of network request"
	}
}

As a result, the only place to specify the associated types of a generic protocol is in the ‘where’ clause of the generic constraints. Therefore, we can only use generic protocols as generics constraints in generic types or generic functions.


Posted

in

by