Generic parameter 'T' could not be inferred when passing Codable struct - swift

Please go through the code first for better understanding.
func getType<T: Decodable>(urlString: String, header: HTTPHeaders, completion: #escaping (T?) -> Void){
}
let networkManager = DataManager()
networkManager.getType(urlString: kGetMyDayTaskDetails + strId, header: header) { (MyDayAndTaskDetails) in
}
Error:
Generic parameter 'T' could not be inferred
I am trying to pass the struct as parameter using Generics to use a common method for api calling. But not getting how to call correctly. Please guide.

Consider that the parameter in the closure is an instance, not a type. For example like
networkManager.getType(urlString: kGetMyDayTaskDetails + strId,
header: header) { details in
}
To specify the generic type annotate it and according to the signature it must be optional
networkManager.getType(urlString: kGetMyDayTaskDetails + strId,
header: header) { (details : MyDayAndTaskDetails?) in
}

Related

Why is this generic json function not working?

Looking to make a function to decode any Type that I pass in at the call site.. so of course it has to be generic over the type. So far my implementation looks like this:
func getAll<ResourceType>(_ type: ResourceType, using url: URL, completion: #escaping ([ResourceType] -> Void) where ResourceType: Codable {
//This works just fine
}
but when I try to call it:
getAll(Movie, using: URL(string: "movies.com")) { movies in
//This does not work
//Error says Type 'Movie.Type' cannot conform to 'Decodable`
//Error says Type 'Movie.Type' cannot conform to 'Encodable`
// This baffles me because in the func declaration it says *where RT: Codable*
}
type must be ResourceType.Type, the generic ResourceType can be both a single object and an array
func getAll<ResourceType>(_ type: ResourceType.Type, using url: URL, completion: #escaping (ResourceType) -> Void) where ResourceType: Codable {
and when calling the method type must be [Movie].self
getAll([Movie].self, using: URL(string: "movies.com")) { movies in
and a parentheses is missing in the signature of the method and the compiler with complain about an unwrapped URL

Swift: Assign an instance of a generic type to a class variable?

I am trying to use this https://github.com/bricklife/JSONRPCKit . It is a simple implementation of JSONRPC for Swift.
This is the example in the readme. Quite simple.
// Generating request JSON
let batchFactory = BatchFactory(version: "2.0", idGenerator: NumberIdGenerator())
let request = Subtract(minuend: 42, subtrahend: 23)
let batch = batchFactory.create(request)
batch.requestObject // ["jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1]
// Parsing response JSON
let responseObject: Any = ["jsonrpc": "2.0", "result": 19, "id": 1]
let response = try! batch.responses(from: responseObject)
I would like to keep track of batch, even if it is created in a function of a class. So that another function (when I get a reply from my server). Can access the specific request/batch/batchelement and execute whatever function it needs to.
I cannot create vars in my class using any of the types from JSONRPCKit.
I get compiler errors along the lines of:
Protocol 'Batch' can only be used as a generic constraint because it has Self or associated type requirements
Reference to generic type 'Batch1' requires arguments in <...>
Value of protocol type 'Request' cannot conform to 'Request'; only struct/enum/class types can conform to protocols
I tried using the generic in functions somehow to avoid my issues but that didn't help either.
func store_b<Batch: JSONRPCKit.Batch>(_ batch: Batch){
More info: batch is of type Batch1, like so:
public struct Batch1<Request: JSONRPCKit.Request>: Batch {
and Batch is a protocol
public protocol Batch {
Is there a simple way to keep track of my batch request and get responses/ how do I use these generics properly?
Break down the create method signature and the Batch1 type
public func create<Request: JSONRPCKit.Request>(_ request: Request) -> Batch1<Request>
public struct Batch1<Request: JSONRPCKit.Request>: Batch {
public typealias Responses = Request.Response
public typealias Results = Result<Request.Response, JSONRPCError>
}
create is a generic function that takes a single parameter of any type. The constraint <Request: JSONRPCKit.Request> specifies that the parameter's type must conform to the protocol JSONRPCKit.Request.
Batch1 is a generic struct that needs to define two internal types, both of which are associated with some arbitrary type. Again, <Request: JSONRPCKit.Request> specifies that that arbitrary type must conform to the protocol JSONRPCKit.Request.
The return type Batch1<Request> ties these two generics together, saying that the type used for the returned Batch1 struct will match the type of the request parameter.
When you call the create method, you must fill in the generic type with a concrete type, as in the example
let batch = batchFactory.create(Subtract(minuend: 42, subtrahend: 23))
Now the compiler can go through all of the definitions and create concrete implementations for that type:
public func create(_ request: Subtract) -> Batch1<Subtract>
public struct Batch1<Subtract>: Batch {
public typealias Responses = Int
public typealias Results = Result<Int, JSONRPCError>
}
This uses the fact that Subtract defines typealias Response = Int. Note that nothing is generic anymore; these are all concrete types. You would have no issue with trying to store a property of type Batch1<Subtract>.
This is why you can't easily store the batch in a property: Swift has no idea what types to put in it!
One way around this is to instead store a closure, this can wrap the generic batch such that the class doesn't have to know about it
// closure property
var responseProcessor: ((Any) -> Void)?
func createBatch<R: JSONRPCKit.Request>(request: R, processor: #escaping (R.Response) -> Void) {
let batch = batchFactory.create(request)
self.responseProcessor = { responseObject in
let response = try! batch.responses(from: responseObject)
processor(response)
}
}
// somewhere else when you get the responseObject
responseProcessor?(responseObject)
This method takes a specific closure that matches the generic type and wraps it in a closure that is no longer dependent on the generic. This way every batch can share the same closure property.

Swift Optional Generics Type required even though parameter is nil [duplicate]

This question already has answers here:
Default value for optional generic parameter in Swift function
(3 answers)
Closed 2 years ago.
how to fix this method declaration to let Payload be fully omitted when not needed, now it needs Generic Type to be provided even though it should use default nil value. I ended with duplicating this method with Payload part omitted to get rid of this error but looking for any hints so I can deduplicate my code again.
Thanks in advance
func prepareRequest<Payload: Encodable>(for url: URL, method: String = "GET", payload: Payload? = nil) -> AnyPublisher<URLRequest, Never> {
// ...
}
Payload has to be something. It doesn't matter in this case what it is, but it still has to be known. The fact that the value is nil doesn't mean that you won't use the type itself in some way inside of the implementation, so Swift needs to know what it is.
The easiest way is to assign it to something already Encodable like String, adding the following overload:
func prepareRequest(for url: URL, method: String = "GET") -> AnyPublisher<URLRequest, Never> {
return prepareRequest(for: url, method: method, payload: Optional<String>.none)
}
You have to do this with overloads; there's no "default type parameter" feature in Swift. But any time you run into a limitation of default parameters, you can deal with it as an overload. Default parameters are just easier-to-type overloads after all.
If you're like me, having to pick a random type like String is annoying. The correct type here is Never, but Never doesn't conform to Encodable. It should (Never should conform to everything), but we can fix that by hand:
extension Never: Encodable {
public func encode(to encoder: Encoder) throws {
fatalError()
}
}
func prepareRequest(for url: URL, method: String = "GET") -> AnyPublisher<URLRequest, Never> {
return prepareRequest(for: url, method: method, payload: Optional<Never>.none)
}
There's no really tangible advantage doing it this way than using String, but it makes me feel better.
Creating another overload is the way to do it. Swift doesn't have variadic generics yet. Until then, you'll need an overload for each number of placeholders. And zero is one of those numbers!
Typically, this means you'll be using a third (likely private) function for common functionality.
Note: it's impossible to use these default parameters, even though they compile!
private func common() { }
func ƒ() { common() }
ƒ()
func ƒ<T>(_: T? = nil) { common() }
ƒ( () )
func ƒ<T0, T1>(_: (T0, T1)? = nil) { common() }
ƒ(
( (), () )
)

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

Protocol conformance of system classes using more generic types

To mock objects in Swift for test, I generally follow a pattern of authoring a Protocol describing the behaviour of the object I'd like, and then using Cuckoo to generate mocks for it for test.
Usually, these protocols map directly onto existing types, and this works fine, until I need to make the existing type work with my new protocol types.
public typealias RequestCompletionHandler = (Request, Error?) -> Swift.Void
public protocol Request {
var results: [Any]? { get }
var completionHandler: RequestCompletionHandler? { get }
}
extension VNRequest: Request {}
Here, VNRequest already has a member called completionHandler that returns the following type:
public typealias VNRequestCompletionHandler = (VNRequest, Error?) -> Swift.Void
Technically, all of these types should match up, but obviously it's not a very easy scenario for the type solver to solve, so the compiler isn't too cheerful about it.
At first I thought I'd be able to refer to the original completionBlock implementation by doing the following:
extension VNRequest: Request {
public var completionHandler: RequestCompletionHandler? {
return (self as VNRequest).completionHandler
}
}
But it's not too happy about that either.
Any advice about how best to do this? I've thought about using a different name in the protocol (e.g: completionBlock_ or completionBlock$), which works, but it's a bit scrappy.
The problem occurs due to the fact that Swift is covariant in respect to closure return type, and contra-variant in respect to its arguments. Which means that (VNRequest, Error?) -> Void can't be used where (Request, Error?) -> Void is needed (the other way around is possible).
You can solve your problem by using an associated type in the Request protocol:
public protocol Request {
associatedtype RequestType = Self
var results: [Any]? { get }
var completionHandler: ((RequestType, Error?) -> Void)? { get }
}
The above protocol definition will make the VNRequest class compile, as the compiler will detect the match for RequestType.
The downside, though, is that protocols with associated types have some limitations regarding where they can be used, and also passing them as function arguments will require some where clauses to make them work.
Another alternative would be to use Self as a parameter to the completion handler:
public typealias RequestCompletionHandler<T> = (T, Error?) -> Swift.Void
public protocol Request {
var results: [Any]? { get }
var completionHandler: RequestCompletionHandler<Self>? { get }
}
This will also solve the conformance issue, but also comes with some constraints: VNRequest must be final, the functions using Request must be generic:
func send<R: Request>(_ request: R)