Genome 3.2.1

Genome 3.2.1

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布上次发布2017年4月
SwiftSwift 版本3.0-GM-CANDIDATE
SPM支持 SPM

Logan Wright 维护。



Genome 3.2.1

欢迎来到 Genome 3.0。这个库旨在满足以下目标

  • [x] 灵活的数据类型
  • [x] 错误驱动
  • [x] 嵌套映射
  • [x] 集合映射
  • [x] 简单且一致
  • [x] 双向序列化
  • [x] 转换
  • [x] 类型安全
  • [x] 常量(let
  • [x] 完全支持 Linux
  • [x] 结构友好
  • [x] 支持继承
  • [x] 与 Core Data 和持久化兼容

Node

Genome 基于而非直接基于 JSON 构建 Node。这使得 Genome 通过少量努力就可以轻松地与任何数据类型一起工作。

所有映射操作都构建在 Node 的核心之上。

针对 JSON 优化

默认情况下,与 JSON 工作得很好

let task = URLSession.shared.dataTask(with: url) { data, response, error in
    guard let data = data else { return }
    do {
        let model = try Model(node: data)
        completion(model)
    } catch {
        completion(error)
    }
}
task.resume()

如果您的数据是嵌套的,您可以使用 Node 进一步处理。

let json = try rawJSONData.makeNode()
guard let items = json["root", "items"] else { return }
let models = try [Item](node: items)

您会注意到上述代码中使用了一个初始化后的数组,这在使用 Genome 时是完全没有问题的。

如果您正在使用 SwiftPM 在 Linux 上进行工作,强烈建议您使用类型安全的 JSON 库,如 此库

转换为 JSON

我们可以用同样的方式创建我们的 JSON 数据

let jsonData = try Data(node: item)
api.post(jsonData) { response in ... }

构建项目

所有未来的 Cocoapods 开发都将使用 SwiftPM 完成。Cocoapods 和 Carthage 的支持将被维护,但在开发中不使用。以下是一些有用的命令

# make xcode project
swift package generate-xcodeproj
# build project
swift build
# test project
swift test

SwiftPM

要使用 SwiftPM,请将其添加到您的 Package.swift

.Package(url: "https://github.com/LoganWright/Genome.git", majorVersion: 3)

目录

快速入门

让我们考虑以下假设的 JSON

[
    "name" : "Rover",
    "nickname" : "RoRo", // Optional Value
    "type" : "dog"
]

以下是如何为该 JSON 创建模型的示例

enum PetType: String {
    case dog
    case cat
    case unknown
}

struct Pet: MappableObject {
    let name: String
    let type: PetType
    let nickname: String?

    init(map: Map) throws {
        name = try map.extract("name")
        nickname = try map.extract("nickname")
        type = try map.extract("type") { PetType(rawValue: $0) ?? .unknown }
    }

    func sequence(map: Map) throws {
        try name ~> map["name"]
        try type ~> map["type"].transformToNode { $0.rawValue }
        try nickname ~> map["nickname"]
    }
}

一旦完成,我们可以这样构建

let pet = try Pet(node: json)

它还会与集合一起工作

let pets = try [Pet](node: jsonArray)

NASA 图片

让我们构建一个简单的例子,来获取NASA的每日照片。请注意,这是一个同步API,为了简洁,使用了Data。建议使用异步且恰当的HTTP客户端,如URLSession。

struct Photo: BasicMappable {
    private(set) var title: String = ""
    private(set) var mediaType: String = ""
    private(set) var explanation: String = ""
    private(set) var concepts: [String] = []

    private(set) var imageUrl: NSURL!

    mutating func sequence(_ map: Map) throws {
        try title <~ map["title"]
        try mediaType <~ map ["media_type"]
        try explanation <~ map["explanation"]
        try concepts <~ map["concepts"]
        try imageUrl <~ map["url"]
            .transformFromNode { NSURL(string: $0) }
    }
}

struct NASA {
    static let url = URL(string: "https://api.nasa.gov/planetary/apod?concept_tags=True&api_key=DEMO_KEY")!

    static func fetchPhoto() throws -> Photo {
        let data = try Data(contentsOf: NASA.url)
        return try Photo(node: data)
    }
}

现在我们可以这样调用

let photo = try NASA.fetchPhoto()

警告:请首先阅读有关同步性和API的第一段内容。

MappableObject

这是本库的核心协议选项之一。它将是大多数标准映射操作的首选。

它有两个要求

init(map: Map) throws

这是用于映射对象的初始化器。如果您喜欢,可以手动调用它,但如果使用任何内置便捷初始化器,则会自动调用。否则,如果需要初始化一个Map,请使用

let map = Map(node: someNode, in: someContext)

它有两个主要要求

sequence(map: Map) throws

在两个主要情况下会调用sequence函数。它被标记为mutating,因为这将在fromNode操作上修改值。然而,如果您只使用toNode,则不会进行任何修改,可以移除mutating关键字。(如在上述例子中所示)

FromNode

在映射到使用任何便利初始化器的Node时。在实例化对象后,将调用sequence。这使得无法初始化常量或使用双向操作符的对象能够完成映射。

如果您直接使用init(map: Map)进行初始化,那么如果对象需要,您将负责手动调用sequence

它被标记为mutating,因为它会修改值。

请注意,如果您只映射到Node,则不会进行任何修改。

ToNode

当访问对象的makeNode()时,将调用序列操作来收集值到一个Node包中。

~>

这是本库中使用的核心操作之一。~符号表示连接,而<>符号分别表示值的流向。当声明为~>时,表示仅从值映射到Node。

您还可以使用以下操作符:

操作符 方向 例子 修改
<~> 到和从Node try name <~> map["name"]
~> 仅到Node try clientId ~> map["client_id"] 𝘅
<~ 仅从Node try updatedAt <~ map["updated_at"]

transform

Genome提供了各种转换值的方式。这些是类型安全的,并将由编译器进行检查。

这些是可链的,如下所示:

try type <~> map["type"]
    .transformFromNode {
        return PetType(rawValue: $0)
    }
    .transformToNode {
        return $0.rawValue
    }

注意:目前,在某些情况下,转换需要绝对的选项性符合性。例如,Optionals变为Optionals,ImplicitlyUnwrappedOptionals变为ImplicitlyUnwrappedOptionals等。

fromNode

当使用let常量时,您需要调用一个即时设置值的转换器。在这种情况下,您将调用fromNode,并传递任何接受一个NodeConvertibleType(一个标准Node类型)并返回值的闭包。

transformFromNode

如果需要将节点输入转换以适应您自己的类型,请使用此命令。在我们上面的例子中,我们需要将原始节点转换为我们的枚举。这也可以附加到 <~ 操作符的映射中。

transformToNode

如果需要将给定的值转换为更适合数据格式的形式,请使用此命令。这也可以附加到 ~> 操作符的映射中。

try

为什么每行都有 try 关键字!每个映射操作如果不正确指定,都将失败。最好首先处理这些可能性。

例如,如果设置的属性是非可选的,在 Node 中发现在 nil,则操作应该抛出一个容易捕获的错误。

更多概念

Genome 可用的不同功能

Genome 的构造方式意味着您无需处理 Node 以便在您的网络服务中进行反序列化和序列化。如果需要,它仍然可以直接使用。

继承

Genome 适用于 final 类和结构,但也支持继承。遗憾的是,由于泛型、协议和 Self 的某些限制,它需要一些额外的努力。

Object

库提供了 Object 类型来满足大多数基于继承的映射操作。只需要简单地将 Object 作为子类,就可以正常使用了。

class MyClass : Object {}

注意:如果您使用 Realm 或其他已使用 Object 的库,不要忘记在 Swift 中这些是模块命名空间的。如果是这种情况,您应该这样声明您的类: class MyClass : Genome.Object {}

BasicMappable

为了支持灵活的自定义,Genome 为协议提供了各种映射选项。您的对象可以符合以下任何一个。尽管每个初始化器都标记为 throws,但如果您确定它将成功,则不需要初始化器 throw。在这种情况下,您可以安全地省略 throws 关键字。

协议 必须初始化器
BasicMappable init() throws
MappableObject init(map: Map) throws

这些都是便利协议,最终都继承自 MappableBase。如果您想要定义自己的实现,库的其他功能仍然适用。

NodeConvertibleType

这是库的真正根源。即使上面提到的 MappableBase 也继承自这个核心类型。它有两个要求:

public protocol NodeConvertibleType {
    init(node: Node, in context: Context) throws
    func makeNode(context: Context) throws -> Node
}

所有基本类型,如 IntString 等,都符合此协议,这为定义库提供了最大的灵活性。它也为 fewer overloads 的出现铺平了道路,当集合的 NodeConvertible 也符合此协议时。

实例化

如果您正在使用图书馆中建立的默认实例化方案,您很可能需要使用此函数进行初始化。

public init(node: Node, in context: Context = EmptyNode) throws

现在我们可以轻松地安全地创建一个对象。

do {
    let rover = try Pet(node: nodeRover)
    print(rover)
} catch {
    print(error)
}

如果我们只关心是否能够创建一个对象,我们也可以这样做

let rover = try? Pet(node: nodeRover)
print(rover) // Rover is type: `Pet?`

上下文

上下文 被定义为空协议,任何可能需要访问的对象都可以遵守并通过它传递。

基础

如果您正在使用 基础,您可以通过将它们转换成节点首先来转换 Any[String: Any][Any] 类型。Node(any: yourTypeHere)

集合类型

您也可以无需映射直接实例化集合。

let people = try [People](node: someNode)

核心数据

如果您希望使用 核心数据,而不是从 NSManagedObject 继承,请从 ManagedObject 继承。

快乐的映射!