Generic return type with Codable returning error Generic parameter 'T' could not be inferred even though Xcode correctly shows return type on output - swift

I have looked at a variety of related questions on here, but none of the answers appear to work for me right now.
For starters, I have the following function set up with a generic return type that must conform to Decodable.
func readFile<T: Decodable>(url: URL) -> T? {
do {
let data = try Data(contentsOf: url)
return try PropertyListDecoder().decode(T.self,
from: data) as T
} catch {
return nil
}
}
I have tried a variety of calls against it, but thus far Xcode keeps returning the error code
Cannot explicitly specialize a generic function
After some experimentation, I have arrived at the following:
if let file: CodableStruct = readFile(url: url) as? CodableStruct {
// File is reported as the correct type here
// but I still get "Generic parameter 'T' could not be inferred"
}
My goal is to store a variety of different objects that conform to Codable, and then be able to retrieve them with the above function.

Just remove the conditional downcast as? CodableStruct
if let file: CodableStruct = readFile(url: url) {
and – not related to the error – remove also the redundant bridge cast as T.
Consider to make the function throw
func readFile<T: Decodable>(url: URL) throws -> T {
let data = try Data(contentsOf: url)
return try PropertyListDecoder().decode(T.self, from: data)
}

Forward the errors along. Don't hide them with an optional.
func readFile<Decodable: Swift.Decodable>(url: URL) throws -> Decodable {
try PropertyListDecoder().decode(
Decodable.self,
from: Data(contentsOf: url)
)
}
let file: CodableStruct = try readFile(url: url)

Turns out, the compiler was confused by the try command in my readFile function.
I had to add ( ) to help it understand the statement.
return (try PropertyListDecoder().decode(T.self,
from: data)) as T
However, vadian was correct in that I could remove the downcast from the call.
To the comments about throwing, I do handle the error, but I cut down the code for purpose of making the question shorter. Thank you for addressing that though!

Related

Inferring the type of the return of a function from one of the inputs to that function

In our app we have an enum defined that covers all the back end endpoints that can be hit by the app...
enum Route {
case todo(TodoRoute)
case event(EventRoute)
case userDetails
enum TodoRoute {
case create
case delete(Todo)
}
case EventRoute {
case list
case edit(Event)
}
}
These get translated into the individual endpoints and parameters and so on.
So we have a couple of functions on our ApiClient like this that eventually make the network call...
public func request<A: Decodable>(
_ route: Route,
as: A.Type
) async throws -> A {
let (data, _) = try await self.request(route)
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return try decoder.decode(A.self, from: data)
} catch {
throw error
}
}
public func request<A: Decodable>(
_ route: Route
) async throws -> A {
let (data, _) = try await self.request(route)
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return try decoder.decode(A.self, from: data)
} catch {
throw error
}
}
As you can see, these decode the returned data into a particular generic type.
So at the call site it looks like one of these...
// 1.
let result = try await apiClient.request(.userDetails, as: UserDetails.self)
// 2.
let result: EventList = try await apiClient.request(.event(.list))
These work but I'm trying to find a way to embed the type to be decoded into the function call itself.
Each endpoint we call will only return one type of JSON so there is a 1:1 mapping between our Route cases and the type returned by the function. So rather than having to explicitly define both the route AND the type in the function it should be possible to only provide the route and have the type inferred. Something like this...
let result = try await apiClient.request(.event(.list))
And have the type of result inferred from the Route passed into the function.
Perhaps this just isn't possible?
I was thinking of having a function on the route like route.resourceType or something? So the function can infer what T is from that? Or something?
Hmm... as I type I'm thinking that isn't possible?
Is it possible to make such and type inference work?
Just to make it clear this is answered.
As #RobNapier pointed out. Inferring a type based on a value is not something that is a feature within Swift. Although it does exist in other languages. It’s not one to wait for in Swift.
https://en.wikipedia.org/wiki/Dependent_type

Swift Generics Generic parameter could not be inferred

I'm trying to create a protocol that has a function with generic parameters.
protocol APIRequest {
static func fetchData<T: Codable>(completion: #escaping(T?, NetworkError?) -> Void)
}
then I have a struct that conforms the protocol
static func fetchData<Ztar: Codable>(completion: #escaping (Ztar?, NetworkError?) -> Void) {
let url = URLConstructor.url(scheme: "https", host: "swapi.dev" , path: "/api")
guard let url = url else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else {
completion(nil, NetworkError.badResponse)
return
}
do {
let decoder = JSONDecoder()
let object = try decoder.decode(Ztar.self, from: data)
completion(object, nil)
} catch {
print(error)
}
}
task.resume()
}
but I'm not sure if that implementation of the type of the generic is correct because in my ViewController I'm receiving the error Generic parameter 'Ztar' could not be inferred
NetworkManager.fetchData { star, error in
}
Can someone explain What I'm doing wrong?
It's not possible to implement this correctly. What type is Ztar? How would the compiler know? You've written "fetchData will fetch some kind of data, out of all the infinitely possible kinds of data in the universe, and you, the compiler, should decode it." That's not possible.
Instead, the way this would generally be written is:
fetchData<T: Codable>(ofType: T.Type, completion: #escaping (Result<T, NetworkError>) -> Void)
And then you would need to pass the type you expect as the first parameter:
fetchData(ofType: Record.self) { resultRecord in ... }
If you don't know what type Ztar is, how can the compiler?
(Note that you should almost never use (T?, Error?) as a type. That says "maybe T, maybe Error, maybe neither, maybe both." You almost always mean Result<T, Error> instead. That says "either T or Error.")

Initialize a placeholder empty variable of generic type

I'm new to Swift.
My ultimate goal is to return the value level (which contains decoded JSON) but I am struggling with returning data from my function.
This is my code:
func decode<T: Decodable>(_ type: T.Type, from file: String) -> T {
let decoder = JSONDecoder()
let urlString = "https://drive.google.com/u/0/uc?id=1WMjoHM2iaKjfx9wZhE_6jmKlcMi_OzGT&export=download"
var level = type;
self.loadJson(fromURLString: urlString) { (result) in
switch result {
case .success(let data):
level = try! decoder.decode(T.self, from: data)
case .failure(let error):
print(error)
}
}
return level
}
Current problem
The current problem is when I try to assign a value to level I get the following error Cannot assign value of type 'T' to type 'T.Type'
What I've tried
My assumption is the error is happening because initializing a variable with T.type is trying to initialize with the name of the Class instead of the Class itself. I tried to call type.self with no luck.
I also tried to get creative to not have to initialize an empty variable. I tried returning decoder.decode(T.self, from: data) but I was getting a weird Unexpected non-void return value in void function error. I have no idea what that is all about.
Is there an easy way to initialize an empty variable to return data later when given T.Type

How to make a function that returns a decodable type in Swift?

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.
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.
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 .
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

enums with Associated Values + generics + protocol with associatedtype

I'm trying to make my API Service as generic as possible:
API Service Class
class ApiService {
func send<T>(request: RestRequest) -> T {
return request.parse()
}
}
So that the compiler can infer the response type from the request categories .auth and .data:
let apiService = ApiService()
// String
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")))
// Int
let intResponse = apiService.send(request: .data(.content(id: "123")))
I tried to come up with a solution using generics and a protocol with associated type to handle the parsing in a clean way. However I'm having trouble associating the request cases with the different response types in a way that it's simple and type-safe:
protocol Parseable {
associatedtype ResponseType
func parse() -> ResponseType
}
Endpoints
enum RestRequest {
case auth(_ request: AuthRequest)
case data(_ request: DataRequest)
// COMPILER ERROR HERE: Generic parameter 'T' is not used in function signature
func parse<T: Parseable>() -> T.ResponseType {
switch self {
case .auth(let request): return (request as T).parse()
case .data(let request): return (request as T).parse()
}
}
enum AuthRequest: Parseable {
case login(email: String, password: String)
case signupWithFacebook(token: String)
typealias ResponseType = String
func parse() -> ResponseType {
return "String!!!"
}
}
enum DataRequest: Parseable {
case content(id: String?)
case package(id: String?)
typealias ResponseType = Int
func parse() -> ResponseType {
return 16
}
}
}
How is T not used in function signature even though I'm using T.ResponseType as function return?
Is there a better still clean way to achieve this?
I'm trying to make my API Service as generic as possible:
First, and most importantly, this should never be a goal. Instead, you should start with use cases, and make sure that your API Service meets them. "As generic as possible" doesn't mean anything, and only will get you into type nightmares as you add "generic features" to things, which is not the same thing as being generally useful to many use cases. What callers require this flexibility? Start with the callers, and the protocols will follow.
func send<T>(request: RestRequest) -> T
Next, this is a very bad signature. You don't want type inference on return types. It's a nightmare to manage. Instead, the standard way to do this in Swift is:
func send<ResultType>(request: RestRequest, returning: ResultType.type) -> ResultType
By passing the expected result type as a parameter, you get rid of the type inference headaches. The headache looks like this:
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")))
How is the compiler to know that stringResponse is supposed to be a String? Nothing here says "String." So instead you have to do this:
let stringResponse: String = ...
And that's very ugly Swift. Instead you probably want (but not really):
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")),
returning: String.self)
"But not really" because there's no way to implement this well. How can send know how to translate "whatever response I get" into "an unknown type that happens to be called String?" What would that do?
protocol Parseable {
associatedtype ResponseType
func parse() -> ResponseType
}
This PAT (protocol w/ associated type) doesn't really make sense. It says something is parseable if an instance of it can return a ResponseType. But that would be a parser not "something that can be parsed."
For something that can be parsed, you want an init that can take some input and create itself. The best for that is Codable usually, but you could make your own, such as:
protocol Parseable {
init(parsing data: Data) throws
}
But I'd lean towards Codable, or just passing the parsing function (see below).
enum RestRequest {}
This is probably a bad use of enum, especially if what you're looking for is general usability. Every new RestRequest will require updating parse, which is the wrong place for this kind of code. Enums make it easy to add new "things that all instances implement" but hard to add "new kinds of instances." Structs (+ protocols) are the opposite. They make it easy to add new kinds of the protocol, but hard to add new protocol requirements. Requests, especially in a generic system, are the latter kind. You want to add new requests all the time. Enums make that hard.
Is there a better still clean way to achieve this?
It depends on what "this" is. What does your calling code look like? Where does your current system create code duplication that you want to eliminate? What are your use cases? There is no such thing as "as generic as possible." There are just systems that can adapt to use cases along axes they were prepared to handle. Different configuration axes lead to different kinds of polymorphism, and have different trade-offs.
What do you want your calling code to look like?
Just to provide an example of what this might look like, though, it'd be something like this.
final class ApiService {
let urlSession: URLSession
init(urlSession: URLSession = .shared) {
self.urlSession = urlSession
}
func send<Response: Decodable>(request: URLRequest,
returning: Response.Type,
completion: #escaping (Response?) -> Void) {
urlSession.dataTask(with: request) { (data, response, error) in
if let error = error {
// Log your error
completion(nil)
return
}
if let data = data {
let result = try? JSONDecoder().decode(Response.self, from: data)
// Probably check for nil here and log an error
completion(result)
return
}
// Probably log an error
completion(nil)
}
}
}
This is very generic, and can apply to numerous kinds of use cases (though this particular form is very primitive). You may find it doesn't apply to all your use cases, so you'd begin to expand on it. For example, maybe you don't like using Decodable here. You want a more generic parser. That's fine, make the parser configurable:
func send<Response>(request: URLRequest,
returning: Response.Type,
parsedBy: #escaping (Data) -> Response?,
completion: #escaping (Response?) -> Void) {
urlSession.dataTask(with: request) { (data, response, error) in
if let error = error {
// Log your error
completion(nil)
return
}
if let data = data {
let result = parsedBy(data)
// Probably check for nil here and log an error
completion(result)
return
}
// Probably log an error
completion(nil)
}
}
Maybe you want both approaches. That's fine, build one on top of the other:
func send<Response: Decodable>(request: URLRequest,
returning: Response.Type,
completion: #escaping (Response?) -> Void) {
send(request: request,
returning: returning,
parsedBy: { try? JSONDecoder().decode(Response.self, from: $0) },
completion: completion)
}
If you're looking for even more on this topic, you may be interested in "Beyond Crusty" which includes a worked-out example of tying together parsers of the kind you're discussing. It's a bit dated, and Swift protocols are more powerful now, but the basic message is unchanged and the foundation of things like parsedBy in this example.