1
func sendToServer(message: Codable) {
    do {
        let jsonData = try JSONEncoder().encode(message)
        let jsonString = String(data: jsonData, encoding: .utf8)!
        // send to server jsonString
    } catch let error {
        debugPrint("Error occured during parsing \(error.localizedDescription)")
    }
}

I am trying to create a method which accepts objects which conform to Codable but I am getting this error when I am trying to encode:

Cannot invoke 'encode' with an argument list of type '(Codable)'

How can I write a method to achieve this?

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
AppleBee
  • 1,199
  • 12
  • 26

1 Answers1

2

Your signature is incorrect. You don't want a Codable. You want a generic type that conforms to Codable. Specifically, you only really need one that conforms to Encodable:

func sendToServer<Message: Encodable>(message: Message) { ... }

A "Codable" or "Encodable" (the protocols) can't itself be encoded. It doesn't have any information about what to encode. But types that conform to Encodable provide that information.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Why is it a problem to pass a `Codable` to a method that expects a `T: Encodable`? – zneak Feb 11 '19 at 18:29
  • @zneak I don't understand the question. It generally should not be a problem to pass a type that conforms to Codable to a function that expects a type that conforms to Encodable. – Rob Napier Feb 11 '19 at 18:31
  • `Codable` names a type (`Encodable & Decodable`) and it has no associated types. It's surprising to me that you can't pass an instance of `Codable` or `Encodable` to a method that accepts a `T: Encodable`. I'm under the impression that `Encodable` should trivially be found to implement `Encodable`. – zneak Feb 11 '19 at 18:36
  • @zneak *`Codable` names a* protocol, not a type. There is no *instance of `Codable`*. – vadian Feb 11 '19 at 18:43
  • @zneak Protocols do not conform to themselves. The protocol `Codable` describes types, but it is not itself `Codable` (it's a bit wishy-washy about whether it's even a type). If it were, how would `.decode(Decodable.self, from: data)` work? (This is one of several examples of why protocols cannot conform to themselves.) – Rob Napier Feb 11 '19 at 18:43
  • @vadian, how do succinctly call the thing inside a variable of type `Codable`? – zneak Feb 11 '19 at 18:45
  • What is "the thing inside a variable of type `Codable`?" There's nothing "inside" a `Codable`. Other types can conform to `Codable`. It's not a type; it's not a wrapper. (In some cases Swift will create an existential wrapper for you for convenience, but only in very limited cases; in many cases this is impossible.) – Rob Napier Feb 11 '19 at 18:47
  • The existential wrappers are opaque and a compiler implementation detail, not a language-level construct. You can't extract the thing they wrapped (and if you could, there'd be no way to work with them). In many cases they don't even exist because Swift will optimize them away. (I only bring them up because they exist and they allow things like arrays of protocols in certain cases; but it's better to pretend they don't and just think of protocols as a description of a type.) Protocols are not like classes; they're mostly compile-time constructs, not runtime. – Rob Napier Feb 11 '19 at 18:49
  • @RobNapier, there is no way to implement `.decode(Decodable.self...)`, as it has an `init` method. `Encodable` does not. Its single method requires no knowledge of the inside of the package. You could wrap an Encodable in another layer of Encodable and it would still work. – zneak Feb 11 '19 at 18:52
  • @RobNapier Now coming to Message object I have declared like this public protocol Message: class, Codable { } But I cant use this function send to server because it does not inherit from NSObject if I make NSObject inherit I am not able Encode to JSON , please suggest how can I solve this problem too. – AppleBee Feb 11 '19 at 18:53
  • @zneak The compiler to prove that (it doesn't necessarily have access to all the implementation details of what you're going to do with this type). If protocols conformed to themselves, then I could pass `Decodable.self` to `decode`. The type system can't allow things it can't prove are correct, even if you could probably get away with them. – Rob Napier Feb 11 '19 at 18:55
  • @santhosh This sounds like a completely different question; you should open a question for that. I don't see anything in your existing code that requires NSObject. – Rob Napier Feb 11 '19 at 18:56
  • @RobNapier, I now understand that this is how Swift works today, but I think that it's misleading to say that it's impossible. There are already at least 4 types of protocols in Swift (normal, Swift, class-constrained, with associated types/Self requirements). You can (conceptually) easily break it down further and say that protocols that don't put any constraints on metatypes, like `Encodable` but unlike `Decodable`, can conform to themselves. Whether that is currently the case (it is not) and whether it will ever be the case are separate questions from whether it's possible. – zneak Feb 11 '19 at 19:06
  • @zneak Agreed; I meant it's impossible today given how Swift works. It is not a fundamental type problem. There was a discussion just a few weeks ago about exactly the approach you're describing, and it was rejected it because it would mean that small changes to the protocol (like adding a static method) could break existing code in non-obvious ways and confusing ways. That doesn't mean Swift will never do it. – Rob Napier Feb 11 '19 at 20:00