Reputation: 119302
Assuming there is an empty protocol to limit the types:
public protocol DataType { }
protocol Parser {
func parseData<T: DataType>(_ data: Data, to: T.Type) throws -> T
}
So we need a parser specifically for parsing JSON objects:
typealias DecodableDataType = Decodable & DataType
protocol JSONParser: Parser {
var jsonDecoder: JSONDecoder { get }
func parseData<T: DecodableDataType>(_ data: Data, to: T.Type) throws -> T
}
So it is matching the parser
needs too and as jsonDecoder
is already defined, a simple extension
would be great:
extension JSONParser {
func parseData<T: DecodableDataType>(_ data: Data, to: T.Type) throws -> T { try jsonDecoder.decode(T.self, from: data) }
}
So if we implement a manager class for this:
class JSONParsingManager: JSONParser {
public var jsonDecoder: JSONDecoder
init(jsonDecoder: JSONDecoder) {
self.jsonDecoder = jsonDecoder
}
}
Expect to everything works automatically but it throws:
Type 'JSONParsingManager' does not conform to protocol 'Parser'
What I've missed? I need to define managers for other serializers like Protobuf parser and etc. so I can't just conform to Decodable
at the first place.
More clarifying:
Another protocol that should work the same way:
protocol ProtobufParser: Parser {
func parseData<T: Message>(_ data: Data, to: T.Type) throws -> T
}
extension ProtobufParser {
func parseData<T: Message>(_ data: Data, to: T.Type) throws -> T { try T.init(serializedData: data) }
}
I can't define standalone protocols, because there is a function that needs to get any kind of Parser
to parse any parsable objects.
P.S. This question probably had been asked before with a different title and scenario. Please feel free to mention the answer if you know how this question should be asked.
Upvotes: 0
Views: 89
Reputation: 22325
There is a function that needs to get any kind of Parser to parse any parsable objects.
Why not use Swift's built-in Decodable
protocol? It is designed to solve this problem for a broad category of data. Here is what it assumes: every kind of data you might want to parse is in one of these categories:
Note that these categories are recursive, so this very naturally represents something like JSON where you can have an array containing objects containing strings.
A decoder in Swift (such as JSONDecoder) is something that can take Data and try to interpret it as one of those three categories (specifically SingleValueDecodingContainer, UnkeyedDecodingContainer, or KeyedDecodingContainer).
Then a Decodable class knows how to take one of those three categories and instantiate itself.*
So as long as you think you can write initializers for all of your parsable objects that use one or more of those categories, your objects can simply conform to the built in Decodable
protocol.
If you want a custom parser such as a ProtoBufParser, all you have to do is tell it how to produce one of the generic container types by implementing the Decoder
protocol.
That is how you are allowed to mix and match any kind of parser with any kind of parsable object.
*Technically the Decodable object uses the Decoder itself to instantiate itself. This allows you, for example, to try multiple alternative decoding strategies in the constructor until something works.
Upvotes: 0
Reputation: 22325
The error message is accurate. The requirement of the protocol is
func parseData<T: DataType>(_ data: Data, to: T.Type) throws -> T
but you have an extension that implements
func parseData<T: Decodable & DataType>(_ data: Data, to: T.Type) throws -> T
i.e. your parser can only parse things that are Decodable
but your protocol says it has to be able to parse anything conforming to DataType
.
You might be able to use a where clause to specify the more specific requirement
extension JSONParser {
func parseData<T: DataType>(_ data: Data, to: T.Type) throws -> T where T: Decodable {
try jsonDecoder.decode(T.self, from: data)
}
func parseData<T: DataType>(_ data: Data, to: T.Type) throws -> T {
throw CannotDecodeError
}
}
Upvotes: 1