我有大约20个枚举的扩展名,如下所示:
extension CurrencyValue : JSONDecodable {
static func create(rawValue: String) -> CurrencyValue {
if let value = CurrencyValue(rawValue: rawValue) {
return value
}
return .unknown
}
static func decode(j: JSONValue) -> CurrencyValue? {
return CurrencyValue.create <^> j.value()
}
}
extension StatusValue : JSONDecodable {
static func create(rawValue: String) -> StatusValue {
if let value = StatusValue(rawValue: rawValue) {
return value
}
return .unknown
}
static func decode(j: JSONValue) -> StatusValue? {
return StatusValue.create <^> j.value()
}
}
它们几乎相同,除了枚举类型名称,我有20个 - 这显然是非常愚蠢的。有没有人知道如何将它们减少到一个,也许是通过使用泛型?我现在不知道。
更新
枚举就像这样简单:
enum CurrencyValue : String {
case EUR = "EUR"
case unknown = "unknown"
}
enum StatusValue : String {
case ok = "ok"
case pending = "pending"
case error = "error"
case unknown = "unknown"
}
让我们假设以下内容:
必须有一些技巧不能多次实现相同的扩展,只需更改类型。
更新
如下面的Gregory Higley所述,我使用JSON lib Argo。你可以阅读那里的运营商。
答案 0 :(得分:3)
您的问题的实质是您希望在实施Argo的JSONDecodable
时避免为所有这些枚举编写样板代码。您似乎还添加了create
方法,该方法不属于JSONDecodable
的类型签名:
public protocol JSONDecodable {
typealias DecodedType = Self
class func decode(JSONValue) -> DecodedType?
}
不幸的是,这无法做到。 Swift协议不是mixins。除运算符外,它们不能包含任何代码。 (我真的希望在未来的Swift更新中得到“修复”。协议的可覆盖默认实现会很棒。)
您当然可以通过以下几种方式简化实施:
.unknown
并使用选项。 (另外,你应该调用这个.Unknown
。Swift枚举值的约定是用大写字母开始它们。证明?看看Apple所做的每一个枚举。我找不到他们开始的一个例子用小写字母。)create
现在只是init?
的功能别名,可以非常简单地实现。decode
。虽然他可能认为这是暗示的,但他没有暗示的是用这个来实现JSONDecodable.decode
。 根据提问者的要求,这是从操场上快速实施的。我从未使用过Argo。事实上,在我看到这个问题之前,我从未听说过它。我只是通过应用我对Swift的了解来检验Argo的来源并推理它,我回答了这个问题。此代码直接从操场上复制。它不使用Argo,但它使用相关部分的合理传真。最终,这个问题对于Argo来说是不。它是关于Swift的类型系统,下面代码中的所有内容都有效地回答了问题并证明它是可行的:
enum JSONValue {
case JSONString(String)
}
protocol JSONDecodable {
typealias DecodedType = Self
class func decode(JSONValue) -> DecodedType?
}
protocol RawStringInitializable {
init?(rawValue: String)
}
enum StatusValue: String, RawStringInitializable, JSONDecodable {
case Ok = "ok"
case Pending = "pending"
case Error = "error"
static func decode(j: JSONValue) -> StatusValue? {
return decodeJSON(j)
}
}
func decodeJSON<E: RawStringInitializable>(j: JSONValue) -> E? {
// You can replace this with some fancy Argo operators,
// but the effect is the same.
switch j {
case .JSONString(let string): return E(rawValue: string)
default: return nil
}
}
let j = JSONValue.JSONString("ok")
let statusValue = StatusValue.decode(j)
这不是伪代码。它直接从正在运行的Xcode游乐场复制。
如果您创建协议RawStringInitializable
并让所有枚举实现它,那么您将是金色的。由于您的枚举都具有关联的String
原始值,因此无论如何它们都隐式实现了此接口。你只需要做出声明。 decodeJSON
全局函数使用此协议以多态方式处理所有枚举。
答案 1 :(得分:1)
如果您也可以提供枚举的示例,则可能更容易看到可以改进的内容。
只是看一下我会说你可能会考虑从所有枚举中删除.unknown
,只是让可选类型代表.unknown
值.None
。如果你这样做,你可以写:
extension StatusValue: JSONDecodable {
static func decode(j: JSONValue) -> StatusValue? {
return j.value() >>- { StatusValue(rawValue: $0) }
}
}
现在不需要所有的创建功能。这将减少许多重复。
修改强>
您可以使用泛型。如果您创建协议DecodableStringEnum
,请执行以下操作:
protocol DecodableStringEnum {
init?(rawValue: String)
}
然后让你的所有枚举符合它。您不必再编写任何代码,因为该init带有原始值枚举。
enum CreatureType: String, DecodableStringEnum {
case Fish = "fish"
case Cat = "cat"
}
现在编写一个全局函数来处理所有这些情况:
func decodeStringEnum<A: DecodableStringEnum>(key: String, j: JSONValue) -> A? {
return j[key]?.value() >>- { A(rawValue: $0) }
}
最后,在Argo中你可以让你的生物解码功能如下:
static func decode(j: JSONValue) -> Creature? {
return Creature.create
<^> j <| "name"
<*> decodeStringEnum("type", j)
<*> j <| "description"
}
答案 2 :(得分:1)
对于其他人来说,最新的答案是:https://github.com/thoughtbot/Argo/blob/td-decode-enums/Documentation/Decode-Enums.md
基本上,对于枚举
enum CurrencyValue : String {
case EUR = "EUR"
case unknown = "unknown"
}
enum StatusValue : String {
case ok = "ok"
case pending = "pending"
case error = "error"
case unknown = "unknown"
}
你只需要做
extension CurrencyValue: Decodable {}
extension StatusValue: Decodable {}
此外,似乎已经反复指出,如果您只是删除.Unknown
字段而是使用选项,您可以使用内置的枚举支持作为RawRepresentable
类型。
另请参阅:https://github.com/thoughtbot/Argo/blob/master/Argo/Extensions/RawRepresentable.swift了解实现这一目标的实现。