2016-04-28 64 views
4

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

我遇到了一个问题,我有一个协议及其扩展,像这样

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" 
    } 
} 

(我也在用他们的咖喱库以及咖喱ini t方法)

我遇到的问题是,在SomeProtocol扩展中,Argo无法解码associatedtype,Model。我得到的错误是

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

这是Swift类型系统的限制吗?还是有我失踪的东西?

回答

1

经过一番研究,似乎这是Swift 2.3 Swift类型系统的限制。问题的确切原因是集合和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,型号,它们可以是单个对象或它们的一种通用方式的集合。