在Argo中解码泛型类型

时间:2016-04-28 17:08:32

标签: ios swift generics protocols

我正在使用Thoughtbot的Argo框架将JSON对象解析为模型。

我遇到了一个问题,我有一个协议及其扩展名如此

protocol SomeProtocol {
    associatedtype Model
    func foo()     
}

extension SomeProtocol where Model: Decodable {
    func foo() -> Model? {
        // someJSON is AnyObject in this case, say, from a network call
        guard let model: Model = decode(someJSON) else { return nil }
        return model
    }
}

并且符合此协议的类看起来像这样

class SomeClass: SomeProtocol {
    typealias Model = ArgoModel

    func bar() {
        print(foo())
    }
}

和这样的模型

struct ArgoModel {
    let id: String
}

extension ArgoModel: Decodable {
    static func decode(j: AnyObject) -> Decoded<ArgoModel> {
        return curry(self.init)
            <^> j <| "id"
    }
}

(我也使用他们的Curry库来讨论init方法)

我遇到的问题是,在SomeProtocol扩展中,关联类型Model不能被Argo解码。我得到的错误是

No 'decode' candidates produced the expected contextual result type 'Self.Model?'

这是Swift类型系统的限制吗?或者有什么我想念的东西?

1 个答案:

答案 0 :(得分:1)

在研究了一些之后,似乎这是Swift类型系统的限制,如Swift 2.3。问题的确切原因是诸如集合和monad之类的上下文类型不能符合Argo中的Decodable。所以我的模型只要不包含在集合中就可以工作。使用Swift 3.0,目标是允许

  

使约束扩展符合新协议的能力(即,Equatable元素数组是Equatable)

如本期所述:https://github.com/thoughtbot/Argo/issues/334

我目前的解决方法是创建一个包含模型数组的复数模型,并在SomeProtocol扩展中解码 。所以现在我的模型看起来像这样:

struct ArgoModels {
    let models: [ArgoModel]
}

extension ArgoModels: Decodable {
    static func decode(j: JSON) -> Decoded<ArgoModels> {
        switch j {
            case .Array(let a):
                return curry(self.init) <^> sequence(a.map(ArgoModel.decode))
            default:
                return .typeMismatch("Array", actual: j)
        }
    }
}

struct ArgoModel {
    let id: String
}

extension ArgoModel: Decodable {
    static func decode(j: AnyObject) -> Decoded<ArgoModel> {
        return curry(self.init)
            <^> j <| "id"
    }
}

然后在实现类中,我可以创建一个typealias,Model可以是一个对象,也可以是通用方式的集合。