11

我想使用 ResponseTransformer(或其中的一系列)自动将我的对象模型类映射到从 Siesta 服务返回的响应,以便我的 Siesta 资源是我的模型类的实例。我有一个类的工作实现,但我想知道在为每种类型的资源(模型)构建单独的 ResponseTransformer 之前是否有更安全、更智能或更有效的方法来做到这一点。

这是一个示例模型类:

import SwiftyJSON

class Challenge {
    var id:String?
    var name:String?

    init(fromDictionary:JSON) {
        if let challengeId = fromDictionary["id"].int {
            self.id = String(challengeId)
        }
        self.name = fromDictionary["name"].string
    }
}

extension Challenge {

    class func parseChallengeList(fromJSON:JSON) -> [Challenge] {
        var list = [Challenge]()

        switch fromJSON.type {
        case .Array:
            for itemDictionary in fromJSON.array! {
                let item = Challenge(fromDictionary: itemDictionary)
                list.append(item)
            }
        case .Dictionary:
            list.append(Challenge(fromDictionary: fromJSON))
        default: break
        }

        return list
    }
}

这是我构建的 ResponseTransformer,用于映射来自任何端点的响应,该端点返回此模型类型的集合或此模型类型的单个实例:

public func ChallengeListTransformer(transformErrors: Bool = true) -> ResponseTransformer {
    return ResponseContentTransformer(transformErrors: transformErrors)
        {
            (content: NSJSONConvertible, entity: Entity) throws -> [Challenge] in        
            let itemJSON = JSON(content)        
            return Challenge.parseChallengeList(itemJSON)
    }
}

最后,这是我在配置 Siesta 服务时所做的 URL 模式映射:

class _GFSFAPI: Service {

    ...

    configure("/Challenge/*")    { $0.config.responseTransformers.add(ChallengeListTransformer()) }
}

我计划为每种模型类型构建一个单独的 ResponseTransformer,然后将每个 URL 模式单独映射到该转换器。这是最好的方法吗?顺便说一句,我对新的 Siesta 框架感到非常兴奋。我喜欢面向资源的 REST 网络库的想法。

4

1 回答 1

8

你的方法很扎实!你基本上已经掌握了。您可以做一些事情来简化变压器。

大图

听起来您已经掌握了这种权衡,但是对于找到此答案的其他人……您有两种通用方法可供选择:

  1. 在您的午睡观察者中构​​建您的模型对象,或
  2. 在变压器中构建您的模型对象。

选项 1 更容易设置 - 只需在现场制作模型,您就完成了!

func resourceChanged(resource: Resource, event: ResourceEvent) {
    let challenges = Challenge.parseChallengeList(
        JSON(resource.latestData?.jsonDict))
    ...
}

这适用于许多项目。然而,它也有缺点:

  • 选项 1 为每个事件乘以每个观察者实例化一个新模型对象;选项 2 仅根据“新数据”响应实例化模型对象。
  • 选项 1 中没有中心位置来跟踪哪些路由映射到哪些模型对象。
  • 如果服务器没有返回您期望的内容类型,选项 2 会提供更好的错误。

如果(且仅当)项目很小并且模型很轻巧,我更喜欢选项 1。

您将在用户指南的 Pipeline 部分找到有关选项 2 的大量文档。这是一个快速概述。

使用configureTransformer

您可以使用以下方法简化您ChallengeListTransformerconfigureTransformer(...)

configureTransformer("/Challenge/*") {
    (content: NSJSONConvertible, entity: Entity) throws -> [Challenge] in        
    let itemJSON = JSON(content)        
    return Challenge.parseChallengeList(itemJSON)
}

但是等等,还有更多!观看 Swift 为您提供的惊人的类型推理切片和骰子:

configureTransformer("/Challenge/*") {
    Challenge.parseChallengeList(
        JSON($0.content as NSJSONConvertible))
}

(请注意,configureTransformer设置transformErrors为 false。这几乎可以肯定是您想要的……除非您的服务器发送 JSON“挑战”模型作为错误响应的主体!该transformErrors选项通常仅适用于文本和 JSON 解析等通用转换器与内容类型相关联,而不是附加到路由的内容类型。)

全球 SwiftyJSON 转换器

如果您使用的是 SwiftyJSON(我也喜欢它,顺便说一句),那么您可以将它整体应用于所有 JSON 响应:

private let SwiftyJSONTransformer =
    ResponseContentTransformer(skipWhenEntityMatchesOutputType: false)
        { JSON($0.content as AnyObject) }

…接着:

service.configure {
    $0.config.responseTransformers.add(
        SwiftyJSONTransformer, contentTypes: ["*/json"])
}

…这进一步简化了每条路线的内容转换器:

configureTransformer("/Challenge/*") {
    Challenge.parseChallengeList($0.content)
}

请注意,Swift 的类型推断告诉 Siesta 这个转换器需要一个JSON结构作为输入,如果它没有以这种方式从转换器管道中出来,Siesta 会使用它来将其标记为错误。与 JSON 相关的转换器都附加到*/json内容类型,因此如果服务器返回任何意外的内容,您的观察者会看到一个整洁的“嘿,这不是 JSON!” 错误。

有关这一切的更深入信息,请参阅用户指南。

从资源中获取模型

正如 Siesta API 当前所代表的那样,您需要向下转换模型的内容:

func resourceChanged(resource: Resource, event: ResourceEvent) {
    let challenges = resource.latestData?.content as? [Challenge]
    ...
}

TypedContentAccessors或者,如果数据尚不存在转换失败,您可以使用协议扩展方法同时进行转换并获取默认值。例如,如果没有挑战,则此代码默认为空数组:

func resourceChanged(resource: Resource, event: ResourceEvent) {
    let challenges = resource.typedContent(ifNone: [Challenge]())
    ...
}

Siesta 目前不提供将模型类型绑定到资源的静态类型方法;你必须做演员。这是因为 Swift 类型系统的限制阻止了泛型资源类型(例如Resource<[Challenge]>)在实践中的可操作性。希望 Swift 3 能够解决这些问题,以便将来的 Siesta 版本能够提供这些问题。更新:对于 Swift 3 来说,必要的泛型改进已经出来了,所以希望在 Swift 4 中......。

于 2015-11-05T22:52:34.243 回答