I would like to create a generic protocol for a data fetching service like this:
protocol FetchDataDelegate: AnyObject {
associatedtype ResultData
func didStartFetchingData()
func didFinishFetchingData(with data: ResultData)
func didFinishFetchingData(with error: Error)
}
class Controller: UIViewController, FetchDataDelegate {
func didStartFetchingData() {
//...
}
func didFinishFetchingData(with data: ResultData) {
//...
}
func didFinishFetchingData(with error: Error) {
//...
}
}
class NetworkManager {
weak var delegate: FetchDataDelegate?
//...
}
The Controller class would have a reference to a NetworkManager instance, and through this, I would like to start the network operations. When the operation is ended I would like to call the appropriate delegate function to pass the result back to the controller. But with this setup I got the following error: Protocol 'FetchDataDelegate' can only be used as a generic constraint because it has Self or associated type requirements The question is what should I do to use this generic protocol as a variable type? Or if is not possible what would be the correct way? Thanks!
While this is closely related to the question David Smith linked (and you should read that as well), it's worth answering separately because it's a different concrete use case, and so we can talk about it.
First, imagine you could store this variable. What would you do with it? What function in
NetworkManager
could calldelegate.didFinishFetchingData
? How would you generate theResultData
when you don't know what it is?The point is that this isn't what PATs (protocols with associated types) are for. It's not their goal. Their goal is to help you add extensions to other types, or to restrict which kinds of types can be passed to generic algorithms. For those purposes, they're incredibly powerful. But what you want are generics, not protocols.
Instead of creating delegates, you should use generic functions to handle the result of a specific call, rather than trying to nail down each view controller to a specific result type (which isn't very flexible anyway). For example, in the simplest, and least flexible way that still gives you progress reporting:
This structure is the basic approach to this entire class of problem. It can be made much, much more flexible depending on your specific needs.
(If your
didStartFetchingData
method is very important, in ways thatProgress
doesn't solve, leave a comment and I'll show how to implement that kind of thing. It's not difficult, but this answer is pretty long already.)