Reputation: 1549
So I have this enum that I use for the few url requests I use in my app :
enum Netwrok {
case popular
case topRated
case latest
// ...
static let baseUrl = "http://..."
func path() -> String {
switch self {
case .popular: return "/popular"
// ...
}
}
}
And I would like to add a function that returns the Decodable Type of model the network stack should decode the data with.
So I thought something like that would do the job :
func returnType<T>() -> T.Type where T : Decodable {
switch self {
case .popular:
return Popular.self
// ...
}
}
But I can't make it work, it says :
Cannot convert return expression of type 'Popular.Type' to return type 'T.Type'
Asking me to force cast in T.Type
.
How can I make a function that returns the decodable so that type can be handled but the JSONDecoder's decode function ?
Thanks.
Upvotes: 1
Views: 2187
Reputation: 299355
What you're asking is straightforward, but it probably isn't what you want. What you're asking to do is to return a type. There's nothing generic about that.
func returnType<T>() -> T.Type where T : Decodable {
This syntax defines a type parameter, T
, that is passed by the caller. It's not defined by your function. That means the caller may pass any type that is Decodable and your function will return it. For example, the caller can set T
to be Int (since that's Decodable), and you will return Int.Type
. That's easy to implement (return T.self
), but not what you mean.
What you mean is that the function returns some type that is Decodable that the function knows, but the caller doesn't:
func returnType() -> Decodable.Type { ... }
This will work fine, and do exactly what you are asking for, but it suggests you're probably building this network stack incorrectly and will have headaches later.
The reason this approach is likely to be a problem is that you probably want to write a line of code like this:
let result = JSONDecoder().decode(networkType.returnType(), from: data)
That's going to break, because Decodable.Type
is not itself a Decodable type. (You you decode Int, but you can't decode the type of Int.) Say it did work. What type would result
be? What could you do with it? The only thing you'd know about it is that it's Decodable (and you've already decoded it).
You likely want something more like Vasu Chand's implementation, or the similar approach discussed in my blog series.
Upvotes: 3
Reputation: 357
You can use escaping closure for your returning result of an API Call.
Assuming you are hitting a get request . A simple working example for passing Codable model for get request api.
class func GETRequest<ResponseType :Decodable>(url : URL,responseType : ResponseType.Type ,completion: @escaping (ResponseType? ,Error? ) -> Void){
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else{
completion(nil,error)
return
}
let decoder = JSONDecoder()
do{
let responseData = try decoder.decode(ResponseType.self, from: data)
completion(responseData, nil)
}
catch let error{
completion(nil, error)
}
}
task.resume()
}
How to call this network function.
Network.GETRequest(url: url, responseType: Model.self) { (model, error) in
completion(model,error)
}
Model class contains
struct Model : Codable{
}
You can pass any response model for any get
request to network class .
Similarly you can build api network for post request where request body is simply Codable model .
Upvotes: 2
Reputation: 100503
For sorry you can't as according to your need the supply for the first parameter here
JSONDecoder().decode(AdecodableType.self,from:data)
need to be inferred right when you write the code so it can't be Any 1 from a collection of types that conform to Decodable
Upvotes: 0