[swift-users] Decodable where T: MyProtocol?
Stephen Celis
stephen.celis at gmail.com
Mon Jul 17 08:39:57 CDT 2017
TL;DR: I want to decode types differently based on whether they conform to a protocol on top of "Decodable". I've figured out how to get keyed containers to decode these types, but can't figure out how to do the same for un-keyed and single value containers. Is it possible?
---
I have written some code that generates arbitrary values given a Decodable type. E.g.:
struct User: Decodable {
let id: Int
let name: String
}
dump(User.arbitrary)
// ▿ User
// - id: 787375
// - name: "服쎱竺观듰믻뜪킲啨磟°"
This works great! But the ability to override ".arbitrary" on a per-value basis would be much better. Something like:
protocol Arbitrary: Decodable {
static var arbitrary: Self { get }
}
struct User: Arbitrary {
static var arbitrary: User {
return User(
id: Int(arc4random_uniform(100)),
name: Fake.names[Int(arc4random_uniform(UInt32(Fake.names.count)))]
)
}
}
dump(User.arbitrary)
// ▿ User
// - id: 43
// - name: "A. Anypenny"
This solves the problem for root, static calls (more on static vs. dynamic dispatch in a bit). So what about nested instances?
struct Project: Decodable {
let owner: User
}
dump(Project.arbitrary)
// ▿ Project
// ▿ owner: User
// - id: 464786
// - name: "뗗涮ऒꂆ鳔ᩘꦞ꺰䙢覧똮漽翮귦ꜛ●㬪ⴾ枵⿵먳⏈彲≲芁۫⨸콬蘆윰拺握ஷ䰒"
Oof. Well luckily I've found that I can extend "KeyedDecodingContainer" and convince the decoder to use my protocol:
extension KeyedDecodingContainer {
public func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Arbitrary {
return T.arbitrary
}
}
dump(Project.arbitrary)
// ▿ Project
// ▿ owner: User
// - id: 76
// - name: "C. Doodler"
Unfortunately I've had no such luck convincing un-keyed containers to decode using this protocol:
dump([User].arbitrary)
// ▿ 2 elements
// ▿ User
// - id: 980813
// - name: "㎜⽪羋⢢"
// ▿ User
// - id: -216180
// - name: "橿鰉疍旱콠싺힞㘇"
("[Int: User].arbitrary" fails similarly, though I think "Dictionary" is technically a keyed container...)
I've tried adding similar "decode" functions as I did on the keyed container, but they don't seem to get called.
Now back to the static dispatch of "User.arbitrary". Any interface that takes a "T: Decodable" is going to dynamically dispatch to the single value decoder and bypass my overload all over again :(
(For un-keyed containers, I found that I could add extensions all day long to "Array where Element: Arbitrary", "Optional where Wrapped: Arbitrary", etc., but these too would only work from root-level static dispatch.)
My hope is I've overlooked something in the decodable interfaces that would allow my un-keyed and single value containers to decode "Arbitrary" data differently than "T: Decodable". Can anyone help me make my hopes come true?
---
Stephen
More information about the swift-users
mailing list