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
andprotocol 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 struct
s instead of class
es 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.
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. (I don't think that's actually true; you can absolutely create something of size [P]
isn't a concrete type (you can't allocate a block of memory of known size for P
).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
Private Var Is Accessible from Outside the Class
How to Reference Swift Playground Itself
In Swift, Can You Split a String by Another String, Not Just a Character
Swift Error Handling for Methods That Do Not Throw
What Does "Get" Mean in a Protocol's Property Declaration
Nstextfield, Change Text in Swift
Extend Existing Protocols to Implement Another Protocol with Default Implements
Binding an Element of an Array of an Observableobject:'Subscript(_:)' Is Deprecated
Confusion Due to Swift Lacking Implicit Conversion of Cgfloat
How to Get Start and End of the Week in Swift
"Cannot Assign Value of Type 'String' to Type 'Anyobject'", Swift 3, Xcode 8 Beta 6
Implicitly Import Specific Swift Module
Make a Uibarbuttonitem Disappear Using Swift iOS
Self' Captured by a Closure Before All Members Were Initialized
Parameters After Opening Bracket
How to Use Alamofire with Custom Headers for Post Request