Swift Generics Generic parameter could not be inferred - swift

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

Related

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

Creating a property of strict generic type of "self"

I want to create a property on a class that uses the class type as a generic parameter, and I'm having difficulty working it out.
open class ResponseProcessor {
required public init() {
}
var success: ((_ responseProcessor: ResponseProcessor) -> Void)?
func process() {
success?(self)
}
}
class TestProcessor: ResponseProcessor {
var result: String?
override func process() {
result = "Some Result"
super.process()
}
}
open class Request<ResponseProcessorType: ResponseProcessor> {
var success: ((_ responseProcessor: ResponseProcessor) -> Void)?
func doRequest() {
let responseProcessor = ResponseProcessorType.init()
responseProcessor.success = success
responseProcessor.process()
}
}
class TestRequest: Request<TestProcessor> {
}
let testRequest = TestRequest()
testRequest.success = { (responseProcessor) in
// This line reports an error, but I want it to know what
// type the responseProcessor is.
print(responseProcessor.result)
}
testRequest.doRequest()
I want to be able to assign SubRequest to the .request variable, but I can't because of strict generic typing.
So I'd like to be able to say "the request property on a ResponseProcessor should be of type Request<WhateverThisClassIs>, but I can't work out how to express that, or declare it in a way that works.
It should work out that testProcessor.request is of type HTTPRequest<TestProcessor>, but obviously that isn't happening.
I'm not sure if this is going to answer your question or not, but maybe it will put you on a better road. To your stated question, the answer is there is no generic covariance in Swift. What you're trying to write is not possible. Generic covariance wouldn't actually fix your code, because you have a lot of other type problems here (your latest version is probably violating Liskov's Substitution Principle, which means it breaks the meaning of class inheritance). But I don't think you actually want what you're trying to write at all.
I suspect you're writing a pluggable and testable networking stack. That's really common. He's a fairly simple one; they can get much more powerful if you tear this apart a bit more.
First, the low-level networking stack itself should consume URLRequests and return Data. That's all. It should not try to deal with model types. This is where people always go off the rails. So a Request is an URLRequest and a completion handler:
struct Request {
let urlRequest: URLRequest
let completion: (Result<Data, Error>) -> Void
}
And a client consumes those.
final class NetworkClient {
func fetch(_ request: Request) {
URLSession.shared.dataTask(with: request.urlRequest) { (data, _, error) in
if let error = error { request.completion(.failure(error)) }
else if let data = data { request.completion(.success(data)) }
}.resume()
}
}
Now we generally don't want to talk to URLSession when we're testing. We want to throw back pre-canned data probably. So we make one of those.
final class TestClient {
enum ClientError: Error {
case underflow
}
var responses: [Result<Data, Error>]
init(responses: [Result<Data, Error>]) { self.responses = responses }
func fetch(_ request: Request) {
if let response = responses.first {
responses.removeFirst()
request.completion(response)
} else {
request.completion(.failure(ClientError.underflow))
}
}
}
I'm marking things final class because these are sensibly reference types, but I want to make it clear that I'm not using class inheritance anywhere here. (Feel free to leave "final" off in your own code; it's a bit pedantic and usually not needed.)
How are these two things alike? They share a protocol:
protocol Client {
func fetch(_ request: Request)
}
Great. Now I can do things like:
let client: Client = TestClient(responses: [])
No associated types means that Client is perfectly fine as a type.
But getting back Data is kind of ugly. We want a type, like User.
struct User: Codable, Equatable {
let id: Int
let name: String
}
How do we do that? We just need a way to construct a Request that fetches a Decodable:
extension Request {
init<Model: Decodable>(fetching: Model.Type,
from url: URL,
completion: #escaping (Result<Model, Error>) -> Void) {
self.urlRequest = URLRequest(url: url)
self.completion = { data in
completion(Result {
try JSONDecoder().decode(Model.self, from: data.get())})
}
}
}
Notice how Request still doesn't know anything about models? And Client doesn't know anything about models. There's just this Request initializer that takes a Model type and wraps it up in a way that can accept Data and spit back a Model.
You can take this approach miles further. You can write a Client that wraps a Client and modifies the request, adding headers for example.
struct AddHeaders: Client {
let base: Client
let headers: [String: String]
func fetch(_ request: Request) {
var urlRequest = request.urlRequest
for (key, value) in headers {
urlRequest.addValue(value, forHTTPHeaderField: key)
}
base.fetch(Request(urlRequest: urlRequest,
completion: request.completion))
}
}
let client = AddHeaders(base: NetworkClient(),
headers: ["Authorization": "Token ...."])
There are no subclasses here, no generic types, just one protocol (which has no associated types), and one generic method. But you can plug in a wide variety of back-ends, and compose together any operation that can be made to match one of a handful of transforms (Request -> Request, Request -> Data, Data -> Void).
I hope this matches some of what you're getting at with your question. Best of luck.

Can't infer generic type on static function with completion block

I have a static function that uses generics, but I can't get it to infer the generic type when it's called. The function:
static func getDocument<T: JSONDecodable>(_ document: String, fromCollection collection: FirebaseStorage.FirestoreCollections, completion: #escaping (_ decodedDoc: T?, _ error: Error?) -> ()) {
let docRef = firestore.collection(collection.rawValue).document(document)
docRef.getDocument { documentSnapshot, error in
guard error == nil,
let docData = documentSnapshot?.data(),
let decodedDoc = T(json: docData) else {
completion(nil, error)
return
}
completion(decodedDoc, nil)
}
}
Called using:
FirebaseClient.getDocument(
id,
fromCollection: FirebaseStorage.FirestoreCollections.users) { (profile, error) in
}
This gives the error: Generic parameter 'T' could not be inferred. How can I make the generic part of the function work?
FirebaseClient.getDocument(
id,
fromCollection: FirebaseStorage.FirestoreCollections.users) { (profile: ProfileType?, error) in
}
You'll need to let Swift know what type profile is where I've added ProfileType. That should do it!
Kane's answer is good, but a more flexible approach is to pass the type directly. For example, this makes it possible to have an optional completion handler, or to ignore the parameter with _ if you don't care about it. (That said, this approach is a little longer to type, so sometimes Kane's way is better.)
static func getDocument<T: JSONDecodable>(_ document: String,
ofType: T.Type,
completion: #escaping (_ decodedDoc: T?, _ error: Error?) -> ())
This makes everything explicit. You call it this way:
FirebaseClient.getDocument(id, ofType: ProfileType.self) { (profile, error) in ... }
Note that there's no need to use the ofType parameter for anything. It's just there to specialize the generic.
This is pretty close to how Decodable works, and is applicable to a lot of problems. But Kane's solution is also handy at times if it's more convenient.

Swift How to returning a tuple from a do catch where conditional binding must have optional type?

I am wanting to put a swift 3 do-catch inside a function rather than constantly writing it everywhere I need it; inside this function I wish to return a tuple with a boolean, and an optional error.
I am trying to return a tuple from the function and handle the result in my XCTest
However, I get an error saying:
Initializer for conditional binding must have Optional type, not '(Bool, Error?)' (aka '(Bool, Optional)')
My function is as follows;
public static func isValidPurchase(train: Train, player: Player) -> (Bool, Error?) {
do {
let result = try train.canBePurchased(by: player)
return (result, nil)
} catch let error {
return (false, error)
}
}
My canBePurchased code is a bit long, but it goes like this:
func canBePurchased(by player: Player) throws -> Bool {
if (!self.isUnlocked) {
throw ErrorCode.trainIsNotUnlocked(train: self)
}
// other if-statements and throws go here
}
And in my XCTest I call it as such:
if let result = TrainAPI.isValidPurchase(train: firstTrain, player: firstPlayer) as! (Bool, Error?) {
}
I've tried to force cast:
if let result: (Bool, Error?) ...
but this only demotes the compiler error to a warning.
The complier displays the error as noted above.
What am I doing wrong in terms of Initializer for conditional binding must have Optional type and how do I avoid it?
Thanks
The return type from isValidPurchase(train:player) is (Bool, Error?), which is not an optional (it is a tuple where the 2nd member happens to be an optional). Hence, there is no use for optional binding when capturing the return from a call to isValidPurchase(train:player). You simply assign the return value and study it's content (possible error etc) from there:
// e.g. using explicitly separate tuple members
let (result, error) = TrainAPI
.isValidPurchase(train: firstTrain, player: firstPlayer)
if let error = error { /* you have an error */ }
else { /* no error, proceed with 'result' */ }
Or, studying the return using a switch statement:
// result is a tuple of type (Bool, Error?)
let result = TrainAPI
.isValidPurchase(train: firstTrain, player: firstPlayer)
switch result {
case (_, let error?): print("An error occured!")
case (let result, _): print("Result = \(result)")
}
Just use optional casting instead of force casting. Using force casting result would have a non-optional value even if used without the if let statement.
if let result = TrainAPI.isValidPurchase(train: firstTrain, player: firstPlayer) as? (Bool, Error?) {
}

Confusing closures and completion handles

Im a new programmer and am very lost.
I am taking this online iOS dev course and I was configuring collection view cell.
However, closures and completion handles were used and it was never mentioned before.
import UIKit
class PersonCell: UICollectionViewCell {
#IBOutlet weak var img: UIImageView!
func configureCell(imgUrl: String) {
if let url = NSURL(string: imgUrl) {
downloadImg(url)
}
}
func downloadImg(url: NSURL) {
getDataFromURL(url) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else {return}
self.img.image = UIImage(data: data)
}
}
}
func getDataFromURL(url: NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError?) -> Void)) {
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
completion(data: data, response: response, error: error)
} .resume()
}
}
Can someone explain to me what the completion handler is doing after the "getDataFromURL" function. Also what are the closures doing? is "(data, response, error)" getting passed around? How does swift know that "data" is suppose to be NSData and etc in the "(data, response, error)"?
What does the closure after the "dataTaskWithURL" do (is it setting up the completion handler"?
Thank you!
These are good questions!
A closure is simply a collection (aka block) of lines of code that you can treat like a variable and execute like a function. You can refer to a closure with a variable name and you can pass a closure around as a parameter in function calls just like any other variable, eventually executing the code when appropriate. A closure can accept certain parameters to use in its code and it can include a return value.
Example:
This is a closure that accepts two strings as parameters and returns a string.
let closure: (String, String) -> String = { (a: String, b: String) -> String in
return a + b
}
Thus, the following will print "Hello Jack!":
print(closure("Hello ", "Jack!"))
A closure also has a variable type (just like "hello" is a String and 1 is an Int). The variable type is based on the parameters that the closure accepts and the value that the closure returns. Thus, since the closure above accepts two strings as parameters and returns a string, its variable type is (String, String) -> String. Note: when nothing is returned (i.e. the return type is Void), you can omit the return type (so (Int, String) -> Void is the same thing as (Int, String)).
A completion handler is a closure that you can pass to certain functions. When the function completes, it executes the closure (e.g. when a view finished animating onto the screen, when a file finished downloading, etc.).
Example:
"Done!" will be printed when the view controller is finished presenting.
let newClosure: () -> Void = { () -> Void in
print("Done!")
}
let someViewController = UIViewController(nibName: nil, bundle: nil)
self.presentViewController(someViewController, animated: true, completion: newClosure)
Let's focus on the getDataFromURL function you wrote first. It takes two parameters: a variable of type NSData and a closure of type (NSData?, NSURLResponse?, NSError?) -> Void. Thus, the closure (which is named completion) takes three parameters of types NSData?, NSURLResponse?, and NSError?, and returns nothing, because this is how you defined the closure in the function declaration.
You then call getDataFromURL. If you read the documentation, you'll see that the closure you pass to this function as the second parameter is executed when the load task is complete. The function declaration for dataTaskWithURL is what defines the variable types that the closure accepts and returns. Within this closure, you are then calling the closure you passed to the getDataFromURL function.
Within this latter closure (the one you define in downloadImg when you are calling getDataFromURL), you are checking to see if the data that you downloaded is not nil, and if not, you are then setting the data as an image in a UIImageView. The dispatch_async(dispatch_get_main_queue(), ...) call simply ensures that you are setting the new image on the main thread, as per Apple's specifications (you can read more about threads elsewhere).
make an typealias to understand this is easy :
typealias Handle = (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void
//the func should be
func getDataFromURL(url: NSURL, completion: Handle)
//when you call it. it needs an url and an Handle
getDataFromURL(url:NSURL, completion: Handle)
// so we pass the url and handle to it
getDataFromURL(url) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else {return}
self.img.image = UIImage(data: data)
}
}
//setp into the func
func getDataFromURL(url: NSURL, completion: Handle){
// call async net work by pass url
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
// now data / response / error we have and we invoke the handle
completion(data: data, response: response, error: error)
} .resume()
}
hope it be helpful :D

Categories