hds
hds

Reputation: 33

Swift Generics function with escaping closure

I have method:

func getUsers(completion: @escaping (_ users: [User]?,_ error: String?)->()) { router.request(.getUsers) { (data, response, error) in
 var users = [User]()
 users = try JSONDecoder().decode(Array<User>.self, from: responseData)
}

But i want to do this function more generic. I tried :

    func getUsers<T: Decodable>(completion: @escaping (_ users: [T]?,_ error: String?)->()) { router.request(.getUsers) { (data, response, error) in 
      var users = [T]()
      users = try JSONDecoder().decode(Array<T>.self, from: responseData)
      completion(users, nil)
}

But when i call this func users array from escaping closure is [_]. If i just use only T users array is [User.Type] and it does not conform to Decodable so i can do JSONDecoder().decode. How should it look ?

Upvotes: 0

Views: 1910

Answers (2)

Serj
Serj

Reputation: 684

Hey Hds you have done most of the work correctly, only one missing point that is knowledge about how Decodables work.

An array of Decodable is also Decodable. So what I'm trying to say is you don't need to explicitly mention that you want to decode Array<T>.self, you can put T.self as long as you have passed to the function that you are expecting an Array<T>.self so T => Array<T>

So in your case when you call this function and pass [User] as the generic parameter, T will be Array<User>.self while decoding. So here is what the code should look like. I'm not sure if the code will 100% compile because I have some missing components, but it gets the idea across.

func get<T:Deocdable>(completion: @escaping (_ object: T?,_ error: String?) -> ()) {
    router.request(.getUsers) { (data, response, error) in
        do {
            let object = try JSONDecoder().decode(T.self, from: responseData)
        } catch {
            fatalError("Decoding failed because: ", error.localizedDescription)
        }
}

Also I added the do catch block because the try JSONDecoder() throws.

Also to add on how you should call the method its like the following:

get { (users: [User]?, error) in

}

Hope this helps.

Upvotes: 0

Prashant Tukadiya
Prashant Tukadiya

Reputation: 16446

Array< User > is not convertible to '[_]?'

This is because of _ users: [T]? type Optional

Your method is working fine you just have to define optional array.

 self.getUsers{ (res:Array<User>?, error) in

 }

I have created one similar

 func getUsers<T: Decodable>(completion: @escaping (_ users: [T]?,_ error: String?)->()) {

        URLSession.shared.dataTask(with: URL(string: "test")!) { (data, res, error) in
            guard let resData = data else  {completion(nil,"data is nil"); return}
            do {
                var users = [T]()
                users = try JSONDecoder().decode(Array<T>.self, from:resData)
                completion(users, nil)

            } catch {
                completion(nil, error.localizedDescription)
            }
        }

And call it

     self.getUsers{ (res:Array<User>?, error) in

    }

Here is struct

struct User :Codable {
    var name:String?
}

Upvotes: 3

Related Questions