0

How to resolve this issue.......

Im trying to build a network layer for my app so as I go through the project

I'm getting the error

"Cannot invoke 'decode' with an argument list of type '(Codable, from: Data)'" I think its happening because of error type or a mismatch Help me resolve this issue

enum Type:String {
    case GET
    case POST
    case PUT
    case DELETE
}


func networkRequest(MethodType:Type, url:String, codableType:Codable) {

    guard let getUrl = URL(string: url) else {return}

    if MethodType == Type.GET  {

        URLSession.shared.dataTask(with: getUrl) { (data, response, err) in

            if let urlRes = response as? HTTPURLResponse{

                if 200...300 ~= urlRes.statusCode {

                    guard let data = data else {return}

                    do {
                        let newData = try JSONDecoder().decode(codableType.self, from: data)
                    }
                    catch let jsonerr {
                        print("Error Occured :"+jsonerr.localizedDescription)
                    }
                }


            }
        }.resume()

    }

}
Scriptable
  • 19,402
  • 5
  • 56
  • 72

2 Answers2

3

Generics can solve this problem.

First, introduce a generic type parameter:

func networkRequest<T: Decodable>(MethodType:Type, url:String)
                   ^^^^^^^^^^^^^^

Now you can use T.self for the type to decode:

try JSONDecoder().decode(T.self, from: data)

Also, you might consider adding a completion handler, otherwise the value you fetched will be lost:

func networkRequest<T: Decodable>(MethodType:Type, url:String, completionHandler: (T) -> Void)

Usage:

networkRequest(MethodType: .GET, url: ...) {
    (myStuff: MyType) in
    ...
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • when I'm calling the function getting error like this "rgument type 'News.Type' does not conform to expected type 'Decodable" –  Jun 28 '18 at 08:09
  • networkRequest(MethodType: .DELETE, url: "Test", codableType: News) –  Jun 28 '18 at 08:11
  • @akiladilshan You should be calling the method like this: `networkRequest(MethodType: .GET, url: ...) { (news: News) in ... }` – Sweeper Jun 28 '18 at 08:11
  • Remove the `codableType` parameter and add a completion handler. Look at the third code snippet in my answer. That should be your method signature. @akiladilshan – Sweeper Jun 28 '18 at 08:14
  • @akiladilshan Describe _how_ it is not working. Show the error messages and the code you wrote. – Sweeper Jun 28 '18 at 09:31
  • Cannot convert value of type 'T' (generic parameter of global function 'networkRequest(MethodType:url:completionHandler:)') to expected argument type 'T' (generic parameter of instance method 'decode(_:from:)') –  Jun 28 '18 at 09:51
  • In argument type 'T.Type', 'T' does not conform to expected type 'Decodable' –  Jun 28 '18 at 09:52
  • @akiladilshan Ah! I forgot to add the generic constraint on `T`. Edited now.You need `` – Sweeper Jun 28 '18 at 09:53
  • networkRequest(MethodType: Type.GET, url: "Random", completionHandler: (News) -> ()) –  Jun 28 '18 at 10:09
  • after that I'm getting an error Cannot convert value of type '((News) -> ()).Type' to expected argument type '(_) -> Void' –  Jun 28 '18 at 10:10
  • @akiladilshan Do you know how closures work? You should write something like `{ (news: News) in /*do things with news*/ }`. If this is your first time dealing with closures, I suggest you read more about swift closures. – Sweeper Jun 28 '18 at 10:11
1

JSONDecoder expects a concrete type which conforms to Decodable. A protocol cannot conform to itself.

You could make the method generic

func networkRequest<T : Decodable>(MethodType: Type, url: String, codableType: T.Type) {
...
   let newData = try JSONDecoder().decode(T.self, from: data)

And call it

networkRequest(MethodType: .GET, 
                      url: "https://test.com/api", 
              codableType: News.self)
vadian
  • 274,689
  • 30
  • 353
  • 361