Reputation: 2483
I have multiple classes that have code that calls a common network class to make a GET api call. Below is an example of one:
public typealias Api1Result = (Result<Api1Model>) -> Void
private var path = "the/path/api1"
public enum Api1ServiceError: String, Error {
case error = "Sorry, the api1 service returned something different than expected"
}
extension Api1Model {
public static func getApi1(networkClient: NetworkClient = networkClient, completion: @escaping Api1Result) {
networkClient.getPath(path) { result in
switch result {
case .success(let data):
do {
let api1Model = try JSONDecoder().decode(Api1Model.self, from: data)
completion(.success(api1Model))
} catch {
completion(.failure(Api1ServiceError.error))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
Here is the Result enum if interested:
public enum Result<Value> {
case success(Value)
case failure(Error)
}
There are several other model classes, and the only difference is the actual model class being decoded (Api1Model
in this case), as well as the completion typealias (Api1Result
). It does the exact same thing across several others, just makes the call to the networkClient.getPath()
method, checks for success/failure, and calls the completion closure.
Curious if there are any protocol experts out there who could assist in simplifying this and refactoring so I don't have the same boiler-plate code across multiple classes?
Upvotes: 1
Views: 228
Reputation: 285180
Use a protocol extension (untested)
protocol ApiModel {
associatedtype ApiType : Decodable = Self
static var path : String { get }
static func getApi1(networkClient: NetworkClient, completion: @escaping (Result<ApiType>) -> Void)
}
extension ApiModel where Self : Decodable {
static func getApi1(networkClient: NetworkClient, completion: @escaping (Result<ApiType>) -> Void) {
networkClient.getPath(path) { result in
switch result {
case .success(let data):
do {
let api1Model = try JSONDecoder().decode(ApiType.self, from: data)
completion(.success(api1Model))
} catch {
completion(.failure(Api1ServiceError.error))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
Make all your classes conform to ApiModel
and add the static path
property. The type alias is going to be inferred.
Upvotes: 2