I am having a hard time trying to implement Dependency Inversion. Looking around, find a fantastic article Swift Type Erasure. I am not sure how to can I get advantage in my situation. Here is what I am trying to achieve.
Protocol for Networking
protocol Networkable {
associatedtype Response: Decodable
func request(handler: @escaping((Result<Response, Error>) -> ()))
}
Concrete Implementation of Networkable
protocol
final class Networker<Response: Decodable>: Networkable {
private let session: URLSession
private let url: URL
init(session: URLSession, url: URL) {
self.session = session
self.url = url
}
func request(handler: @escaping ((Result<Response, Error>) -> ())) {
session.dataTask(with: url) { data, response, error in
handler(.success(try! JSONDecoder().decode(Response.self, from: data!)))
}
}
}
A ViewModel
that is dependent on Networkable
protocol
class ViewModel {
let networker: Networkable
// Error-> Protocol 'Networkable' can only be used as a generic constraint because it has Self or associated type requirements
init(netwrorker: Networkable) {
self.networker = netwrorker
}
func execute() {
networker.request { _ in
print("Responsed")
}
}
}
Usage:
class View {
let viewModel: ViewModel
init() {
let networker = Networker<ResponseModel>(session: .shared, url: URL(string: "SOME URL")!)
self.viewModel = ViewModel(netwrorker: networker)
}
func load() {
viewModel.execute()
}
}
struct ResponseModel: Decodable {}
Questions:
- Is this a correct way to implement dependency-inversion?
- How can I achieve Generic behavior on Network class and still open for testing?
CodePudding user response:
Your ViewModel
class is ill-formed:
class ViewModel<T: Decodable> {
let networker: Networker<T>
…
}
Otherwise you might create a type-erased AnyNetworkable
concrete type, which you could use as in:
class ViewModel<T: Decodable> {
let networker: AnyNetworkable<T>
…
}
Or you could go with an even more generic approach as in:
class ViewModel<R, T: Networkable> where T.Response == R {
let networker: T
}
Obviously the same reflects on your View
type, which you also need to make either generic or specialised over a specific concrete type of ViewModel
.