Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decoding Void with Swift 4’s Decodable

I have a generic REST request:

struct Request<T> {…}

The T is the return type of the request, for example:

struct Animal {…}
let animalRequest = Request<Animal>
let animal: Animal = sendRequest(animalRequest)

Now I would like to express that the generic type has to conform to Decodable so that I can decode the JSON response from the server:

struct Request<T> where T: Decodable {…}
struct Animal: Decodable {…}

This makes sense and works – until I arrive at a request that has no response, a Request<Void>. The compiler is not happy about that one:

Type 'Void' does not conform to protocol 'Decodable'

My mischevious attempt to solve this by adding the Decodable conformance to Void was quickly found out by the compiler:

extension Void: Decodable {…} // Error: Non-nominal type 'Void' cannot be extended

It feels right to have the request generic over the return type. Is there a way to make it work with Void return types? (For example the requests that just create something on the server and don’t return anything.)

like image 238
zoul Avatar asked Aug 11 '17 12:08

zoul


2 Answers

A simple workaround is to introduce a custom “no-reply” type that would replace Void:

struct NoReply: Decodable {}

Conforming Void to Decodable is not possible. Void is just a type alias for an empty tuple, (), and tuples cannot conform to protocols at this moment, but they will, eventually.

like image 61
zoul Avatar answered Nov 07 '22 10:11

zoul


I found that sometimes other encoded objects of another types can be decoded to NoReply.self. For example custom Error type (enum) can be.

Playground example of this case:

enum MyError: String, Codable {
    case general
}

let voidInstance = VoidResult()
let errorInstance = MyError.general
let data1 = try! JSONEncoder().encode(voidInstance)
let data2 = try! JSONEncoder().encode(errorInstance)

let voidInstanceDecoded = try! JSONDecoder().decode(VoidResult.self, from: data1)
//VoidResult as expected

let errorInstanceDecoded = try! JSONDecoder().decode(MyError.self, from: data2)
//MyError.general as expected

let voidInstanceDecodedFromError = try! JSONDecoder().decode(VoidResult.self, from: data2)
//VoidResult - NOT EXPECTED

let errorInstanceDecodedFromVoid = try! JSONDecoder().decode(ScreenError.self, from: data1)
//DecodingError.typeMismatch - Expected

So my suggestion is to add "uniqueness to NoReply (zoul's answer)):

struct VoidResult: Codable {
    var id = UUID()
}

let voidInstanceDecodedFromError = try! JSONDecoder().decode(VoidResult.self, from: data2)
//DecodingError.typeMismatch - Now its fine - as expected
like image 1
protspace Avatar answered Nov 07 '22 09:11

protspace