Protocol Extending Encodable (Or Codable) Does Not Conform to It

Protocol extending Encodable (or Codable) does not conform to it

As discussed in Protocol doesn't conform to itself?, a protocol does not conform to itself, or to
a protocol that it inherits from. In your case, Filters does not conform to Encodable.

A possible solution is to make struct BankAccountParamters and
protocol Parameters generic:

protocol Filters: Encodable {
var page: Int { get }
}

protocol Parameters: Encodable {
associatedtype T: Filters
var type: String { get }
var filters: T { get }
}

struct BankAccountFilters: Filters {
var page: Int
var isWithdrawal: Bool
}

struct BankAccountParamters<T: Filters>: Parameters {
let type: String = "Bank"
var filters: T
}

Now var filters has type T, which conforms to Filters and consequently, to Encodable.

This compiles and produces the expected result:

let baf = BankAccountFilters(page: 1, isWithdrawal: true)
let bap = BankAccountParamters(filters: baf)

let data = try! JSONEncoder().encode(bap)
print(String(data: data, encoding: .utf8)!)
// {"type":"Bank","filters":{"isWithdrawal":true,"page":1}}

Generics Type 'T' does not conform to protocol 'Encodable'

There is a mismatch:

It should be HttpResponse<DATA: Decodable> instead of HttpResponse<DATA: Codable>, see definition Network<T: Decodable>.

Codable declares conformance to both Decodable and Encodable protocols, see the definition of Codable:

public typealias Codable = Decodable & Encodable

So your HttpResponse expects a generic that conforms to both Decodable and Encodable protocol. But in the definition of Network a generic that conforms only to Decodable is used. Therefore as soon as the compiler checks the method signature of getItems, it complains that 'T' does not conform to protocol 'Encodable'.

Type 'Response' does not conform to protocol 'Decodable' \ 'Encodable'

The issue is caused by the fact that you declared both properties of Response as implicitly unwrapped optionals (IOU). The compiler cannot autogenerate the required methods of Codable for IOU properties.

However, there's no need to make those IOU anyways. If they are required properties that are always present in the response, make them non-optional. If they might be missing, make them Optional (use ? instead of !).

Also, Swift is not Objective-C. There's no need to make your types inherit from NSObject. And you should also use structs instead of classes unless you explicitly need reference type behaviour. You should also make all properties immutable unless you explicitly need to be able to mutate them.

struct ErrorObj: Codable {
let numError: Int
let description: String
}

struct Response<T: Codable>: Codable {
let error: ErrorObj
let result: T

func getResponse(errorObj: (ErrorObj) -> Void, successObj: (T) -> Void) {
if error.numError != 0 {
errorObj(error)
} else{
successObj(result)
}
}

}

Can I write protocol behave similar to Encodable & Decodable?

Martin is correct you cannot make this on your own without touching the compiler.

First let's take a look at this basic example where I explain how coding keys are used.

struct CodableStruct: Codable {
let primitive: Int // No issues yet

enum CodingKeys: String, CodingKey {
case primitive
// This is the default coding key (i.e the JSON has structure ["primitive": 37]
// You can change this key to anything you need
//
// ex case primitive = "any_thing_you_want"
// JSON has to have structure ["any_thing_you_want": 37]
}

}

Changing the codingKey just changes the key the code will use when looking to "decode" that value from your JSON.

Now let's talk about the compiler. Let's say we create another struct

struct NotCodableStruct {
let number: Double
}

This struct does not conform to Codable. If we go and add this into our previous struct we have:

struct CodableStruct: Codable {
let primative: Int
let notCodable: NotCodableStruct // doesn't compile because this doesn't conform to codable

enum CodingKeys: String, CodingKey {
case primative
case notCodable
}
}

Since NotCodableStruct does not conform to Codable the compiler complains. In other words all variables in a struct or object that conforms to Codable must also conform to Codable. See the below screenshot for more information.

compiler error

Of course if you make NotCodableStruct conform to Codable everyone will be happy again. Since there is no way for you to enforce the requirement that all variables conform to Codable you cannot make a similar protocol.

Protocol doesn't conform to itself?

EDIT: Eighteen more months of working w/ Swift, another major release (that provides a new diagnostic), and a comment from @AyBayBay makes me want to rewrite this answer. The new diagnostic is:

"Using 'P' as a concrete type conforming to protocol 'P' is not supported."

That actually makes this whole thing a lot clearer. This extension:

extension Array where Element : P {

doesn't apply when Element == P since P is not considered a concrete conformance of P. (The "put it in a box" solution below is still the most general solution.)


Old Answer:

It's yet another case of metatypes. Swift really wants you to get to a concrete type for most non-trivial things. [P] isn't a concrete type (you can't allocate a block of memory of known size for P). (I don't think that's actually true; you can absolutely create something of size P because it's done via indirection.) I don't think there's any evidence that this is a case of "shouldn't" work. This looks very much like one of their "doesn't work yet" cases. (Unfortunately it's almost impossible to get Apple to confirm the difference between those cases.) The fact that Array<P> can be a variable type (where Array cannot) indicates that they've already done some work in this direction, but Swift metatypes have lots of sharp edges and unimplemented cases. I don't think you're going to get a better "why" answer than that. "Because the compiler doesn't allow it." (Unsatisfying, I know. My whole Swift life…)

The solution is almost always to put things in a box. We build a type-eraser.

protocol P { }
struct S: P { }

struct AnyPArray {
var array: [P]
init(_ array:[P]) { self.array = array }
}

extension AnyPArray {
func test<T>() -> [T] {
return []
}
}

let arr = AnyPArray([S()])
let result: [S] = arr.test()

When Swift allows you to do this directly (which I do expect eventually), it will likely just be by creating this box for you automatically. Recursive enums had exactly this history. You had to box them and it was incredibly annoying and restricting, and then finally the compiler added indirect to do the same thing more automatically.

Why can't we use protocol `Encodable` as a type in the func?

Solution 1.

Try this code, which extend encodable

extension Encodable {
func toJSONData() -> Data? { try? JSONEncoder().encode(self) }
}

Solution 2.

To avoid polluting Apple-provided protocols with extensions

protocol MyEncodable: Encodable {
func toJSONData() -> Data?
}

extension MyEncodable {
func toJSONData() -> Data?{ try? JSONEncoder().encode(self) }
}

Use

var dataSource2: Encodable?
dataSource2 = TestClass2()
let data = dataSource2?.toJSONData()

Protocol with Encodable protocol

First you need to make the protocol extend Encodable to tell the compiler that anything that conforms to CheckoutOrder also conforms to Encodable

protocol CheckoutOrder: Encodable {}

Then I would use generics for this and change CheckoutOrderEntity to

struct CheckoutOrderEntity<Payment: CheckoutOrder>: Encodable {
private enum CodingKeys: String, CodingKey {
case amount
case payment
}

let amount: Int
let payment: Payment
}

Example

let checkout = CheckoutOrderEntity(amount: 1000,
payment: CheckoutPaymentEntity(shouldUseBalance: true,
terminalName: "terminal"))

Why does this model not conform to Decodable? (a polymorphic JSON Christmas tale)

data in Artist must be a concrete type or a generic constrained to Codable. It can't be a protocol.

My suggestion is to drop the protocol and declare an enum with associated types.

enum Artist : Decodable {
case individual(String, IndividualArtist), band(String, BandArtist)

private enum CodingKeys : String, CodingKey { case id, data }
private enum ArtistKeys : String, CodingKey { case type }

init(from decoder : Decoder) throws
{
let container = try decoder.container(keyedBy: CodingKeys.self)
let id = try container.decode(String.self, forKey: .id)
let nestedContainer = try container.nestedContainer(keyedBy: ArtistKeys.self, forKey: .data)
let type = try nestedContainer.decode(ArtistType.self, forKey: .type)
switch type {
case .individual:
let individualData = try container.decode(IndividualArtist.self, forKey: .data)
self = .individual(id, individualData)
case .band:
let bandData = try container.decode(BandArtist.self, forKey: .data)
self = .band(id, bandData)
}
}
}

enum ArtistType : String, Decodable {
case individual
case band
}

struct IndividualArtist : Decodable {
let type : ArtistType
let firstName: String
let lastName: String
}

struct BandArtist : Decodable {
let type : ArtistType
let name: String
}


Related Topics



Leave a reply



Submit