5

I want to create variable with type of Codable. And later to use it in the JSONEncoder class. I thought that code from below should work fine, but it gives me error:

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

How to declare codable variable that JSONEncoder will be taking without error?

struct Me: Codable {
    let id: Int
    let name: String
}

var codable: Codable? // It must be generic type, but not Me.

codable = Me(id: 1, name: "Kobra")

let data = try? JSONEncoder().encode(codable!)

Here is similar question how to pass Codable using function. But I am looking how to set Codable using variable (class variable).

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
Ramis
  • 13,985
  • 7
  • 81
  • 100
  • use `Any` not `Codable` – Leo Dabus Oct 04 '17 at 12:17
  • @LeoDabus Then another question how to pass it to JSONEncoder? – Ramis Oct 04 '17 at 12:19
  • 1
    Whats wrong with using `Me`? – Leo Dabus Oct 04 '17 at 12:20
  • have a look at this [Answer on CodeDump](https://codedump.io/share/0W4liDhIF1RM/1) – chirag90 Oct 04 '17 at 12:21
  • @LeoDabus I want that it will be more generic approach. If I will have another "You" struct and it needs to be assigned to codable variable. – Ramis Oct 04 '17 at 12:23
  • @Ramis check the linked question. But anyway you will need to pass an instance of your Me struct. Not a codable or encodable. – Leo Dabus Oct 04 '17 at 12:35
  • @LeoDabus I added comment that it is not duplicate. What I want to avoid to pass Me struct. – Ramis Oct 04 '17 at 12:40
  • I might be wrong but you can't – Leo Dabus Oct 04 '17 at 12:41
  • @LeoDabus I spend sever hours how to do it, but did not found solution. Could you please remove duplicate as it is not duplicate. – Ramis Oct 04 '17 at 12:43
  • 1
    I still think it is a duplicate. Reopening probably won't make a difference. I will post the link here as reference https://stackoverflow.com/questions/45053060/using-json-encoder-to-encode-a-variable-with-codable-as-type – Leo Dabus Oct 04 '17 at 12:47
  • 2
    @Ramis This is not possible to do — `JSONEncoder` and `JSONDecoder` require concrete types, which `Codable` is not (a variable whose type is `Codable?` is called _existential_). Can you show your use case here? What else could go in this variable? – Itai Ferber Oct 04 '17 at 14:52

3 Answers3

7

Your code is all right, the only thing we need to focus is Codable.

Codable is a typealias which won't give you generic type.

JSONEncoder().encode(Generic confirming to Encodable).

So, i modified the code as below, it may help you..

protocol Codability: Codable {}

extension Codability {
    typealias T = Self
    func encode() -> Data? {
        return try? JSONEncoder().encode(self)
    }

    static func decode(data: Data) -> T? {
        return try? JSONDecoder().decode(T.self, from: data)
    }
}

struct Me: Codability
{
    let id: Int
    let name: String
}

struct You: Codability
{
    let id: Int
    let name: String
}

class ViewController: UIViewController
{
    override func viewDidLoad()
    {
        var codable: Codability
        codable = Me(id: 1, name: "Kobra")
        let data1 = codable.encode()

    codable = You(id: 2, name: "Kobra")
    let data2 = codable.encode()
}
}
Deepak
  • 348
  • 4
  • 14
5

I created the same scenario as yours:

struct Me: Codable
{
    let id: Int
    let name: String
}

struct You: Codable
{
    let id: Int
    let name: String
}

class ViewController: UIViewController
{
    override func viewDidLoad()
    {
        var codable: Codable?

        codable = Me(id: 1, name: "Kobra")
        let data1 = try? JSONEncoder().encode(codable)

        codable = You(id: 2, name: "Kobra")
        let data2 = try? JSONEncoder().encode(codable)
    }
}

The above code is not giving me any error. The only thing I changed is:

let data = try? JSONEncoder().encode(codable!)

I didn't unwrap codable and it is working fine.

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
PGDev
  • 23,751
  • 6
  • 34
  • 88
  • 1
    Compile do not provide any error, but when executing it give an error: fatal error: Optional does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode. – Ramis Oct 05 '17 at 09:40
  • 4
    That explains then. You need to use concrete types. – PGDev Oct 05 '17 at 09:41
1

I have used this way, maybe it helps on your case as well.

public protocol AbstractMessage: Codable {
  var id: Int { get } // you might add {set} as well
  var name: Int { get }
}

then created a method:

public func sendMessage<T>(message: T) where T: AbstractMessage {
  let json = try! String(data: JSONEncoder().encode(message), encoding: .utf8)!
  ...
}

Here I created a common protocol, and passed it as a generic type to my function.

guness
  • 6,336
  • 7
  • 59
  • 88
  • Are you sure? Is that working? I wrote the same method but there is an error saying "Cannot invoke 'encode' with an argument list of type '(Codable)'" – Sanju May 22 '18 at 13:56
  • @Sanju this is currently used on the project I am developing now. – guness May 23 '18 at 00:42