2

We just switched to swift 4.1 and we are having some difficulties with type conformance for Arrays. Here is the old way:

public typealias XDRCodable = XDREncodable & XDRDecodable

public protocol XDREncodable: Encodable {
    func xdrEncode(to encoder: XDREncoder) throws
}

public protocol XDRDecodable: Decodable {
    init(fromBinary decoder: XDRDecoder) throws
    init(fromBinary decoder: XDRDecoder, count: Int) throws
}

extension Array: XDRCodable {
    public func xdrEncode(to encoder: XDREncoder) throws {
        try encoder.encode(UInt32(self.count))
        for element in self {
            try (element as! Encodable).encode(to: encoder)
        }
    }

    public init(fromBinary decoder: XDRDecoder) throws {
        guard let binaryElement = Element.self as? Decodable.Type else {
            throw XDRDecoder.Error.typeNotConformingToDecodable(Element.self)
        }

        let count = try decoder.decode(UInt32.self)
        self.init()
        self.reserveCapacity(Int(count))
        for _ in 0 ..< count {
            let decoded = try binaryElement.init(from: decoder)
            self.append(decoded as! Element)
        }
    }
}

This gives the following error in swift 4.1:

'XDRDecodable' requires that 'Element' conform to 'Decodable'

So we tried changing the declaration to:

extension Array: XDRCodable where Element : XDRCodable

While this compiles it still fails to encode the array and the following warning is generated:

warning: Swift runtime does not yet support dynamically querying conditional conformance ('Swift.Array': 'stellarsdk.XDREncodable')

I saw that this is a work in progress but does anyone have a workaround for this until type conformance is properly implemented. I'd like it to work as it was in swift 4.0 for now.

Jelly
  • 4,522
  • 6
  • 26
  • 42

1 Answers1

1

I have similar problem with BinaryCoder and created workaround. But you must have access to encoder a decoder implementation.

protocol XDRCodableArray {
    func binaryEncode(to encoder: XDREncoder) throws
    init(fromBinary decoder: XDRDecoder) throws
}

extension Array: XDRCodableArray {
   //copy implementation of XDRCodable from Array: XDRCodable
}

//delete extension Array: XDRCodable

In decode append special implementation for arrays:

...
case let binaryT as BinaryDecodable.Type:
    return try binaryT.init(fromBinary: self) as! T
//new case
case let array as BinaryCodableArray.Type:
    return try array.init(fromBinary: self) as! T
...     

And also in encode:

...
case let binary as BinaryEncodable:
    try binary.binaryEncode(to: self)
//new case    
case let array as BinaryCodableArray:
    try array.binaryEncode(to: self)
...

Generaly there is problem with conditional protocol conformance. You can't cast variable to that protocol (in some cases).

protocol Test: Codable {
    func test() -> String
}

extension Array: Test where Element: Codable {
    func test() -> String {
        return "Success"
    }
}

func doSomething(x: Codable) {
   let test = x as! Test
   test.test()
}


let array = ["value"]
let t = (array as! Test).test   //success
doSomething(x: array)   //fail on first row inside function

I hope that Swift 4.2 with dynamically querying conditional conformance support will solve this.

Dan
  • 106
  • 4