Unbox | 封装
封装是一个容易使用的 Swift JSON 编码器。不要花费数小时编写 JSON 编码代码 - 直接封装就可以了!
使用封装与调用 wrap()
操作你希望编码的任何 class
或 struct
实例相同简单。它会自动编码你类型的所有属性,包括嵌套对象、集合、枚举等!
它还提供了一组简单但强大的自定义 API,让您能够轻松地在任何模型设置中使用它。
假设你有一个常用的 User
模型
struct User {
let name: String
let age: Int
}
let user = User(name: "John", age: 28)
使用 wrap()
,你现在可以用一条命令来编码 User
实例
let dictionary: [String : Any] = try wrap(user)
这将生成以下 Dictionary
{
"name": "John",
"age": 28
}
前面的例子很简单,但封装可以为您编码最复杂的结构,包括可选的、非可选的和自定义类型值,而无需您额外编写任何代码。假设我们有一个以下模型设置
struct SpaceShip {
let type: SpaceShipType
let weight: Double
let engine: Engine
let passengers: [Astronaut]
let launchLiveStreamURL: URL?
let lastPilot: Astronaut?
}
enum SpaceShipType: Int, WrappableEnum {
case Apollo
case Sputnik
}
struct Engine {
let manufacturer: String
let fuelConsumption: Float
}
struct Astronaut {
let name: String
}
让我们创建一个 SpaceShip
实例
let ship = SpaceShip(
type: .Apollo,
weight: 3999.72,
engine: Engine(
manufacturer: "The Space Company",
fuelConsumption: 17.2321
),
passengers: [
Astronaut(name: "Mike"),
Astronaut(name: "Amanda")
],
launchLiveStreamURL: URL(string: "http://livestream.com"),
lastPilot: nil
)
现在让我们用一个 wrap()
调用来编码它
let dictionary: WrappedDictionary = try wrap(ship)
这将生成以下字典
{
"type": 0,
"weight": 3999.72,
"engine": {
"manufacturer": "The Space Company",
"fuelConsumption": 17.2321
},
"passengers": [
{"name": "Mike"},
{"name": "Amanda"}
],
"launchLiveStreamURL": "http://livestream.com"
}
如你所见,封装自动编码了 URL
属性为它的 absoluteString
,并忽略了任何 nil
属性(减少了生成的 JSON 的大小)。
尽管自动化很棒,但定制同样重要。幸运的是,封装提供了几个覆盖点,让您可以轻松地调整其默认行为。
默认情况下,封装使用类型的属性名作为其编码键,但有时这并不是你想要的。你可以选择通过使其符合 WrapCustomizable
并实现 keyForWrapping(propertyNamed:)
,来自定义一个类型的所有编码键,如下所示
struct Book: WrapCustomizable {
let title: String
let authorName: String
func keyForWrapping(propertyNamed propertyName: String) -> String? {
if propertyName == "authorName" {
return "author_name"
}
return propertyName
}
}
你还可以通过在这个方法中返回 nil 来使用 keyForWrapping(propertyNamed:)
API 完全跳过一个属性。
您可能有嵌套的字典,它们的键不是字符串,对于这些字典,Wrap提供了WrappableKey
协议。这使您能够轻松地将任何类型转换为可以作为JSON键使用的字符串。
如果想要Wrap生成的字典具有snake_cased键而不是默认值(默认值是匹配已编码属性的名称),可以通过遵守WrapCustomizable
并从wrapKeyStyle
属性返回.convertToSnakeCase
轻松实现。这样做会将属性名myProperty
转换为键my_property
,例如。
对于某些嵌套类型,您可能希望自行处理包装。这特别适用于任何自定义集合或编码时具有完全不同表示的类型的类型。要做到这一点,类型需要遵守WrapCustomizable
并实现wrap(context:dateFormatter:)
,如下所示。
struct Library: WrapCustomizable {
private let booksByID: [String : Book]
func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
return Wrapper(context: context, dateFormatter: dateFormatter).wrap(self.booksByID)
}
}
Wrap还使得轻松编码您的类型所使用的任何enum
值变得容易。如果一个enum
基于原始类型(如String
或Int
),您所需要做的只是声明符合WrappableEnum
,其余的都将为您处理。
非原始类型的enum
值也会自动编码。默认行为是将任何没有相关值的值编码为其字符串表示,并将具有相关值的值编码为字典(字符串表示是键),如下所示。
enum Profession {
case developer(favoriteLanguageName: String)
case lawyer
}
struct Person {
let profession = Profession.developer(favoriteLanguageName: "Swift")
let hobbyProfession = Profession.lawyer
}
编码为
{
"profession": {
"developer": "Swift"
},
"hobbyProfession": "lawyer"
}
为了能够轻松编码在编码过程中可能会用到任何依赖,Wrap提供在启动包装过程时提供上下文对象的能力(通过调用wrap(object, context: myContext
)。
上下文可以是Any
类型,并且可以在所有WrapCustomizable
包装方法中访问。以下是一个示例,其中我们向Book
的title
添加了一个前缀。
struct Book: WrapCustomizable {
let title: String
func wrap(context: Any?, dateFormatter: DateFormatter?) -> Any? {
guard let prefix = context as? String else {
return nil
}
return [
"title" : prefix + self.title
]
}
}
Wrap支持以下平台
Wrap的当前版本(以及master
分支)仅与Swift 3和Xcode 8兼容,但是有一个可以使用Swift 2.3的swift2
分支。
CocoaPods
将行pod "Wrap"
添加到您的Podfile
中
Carthage
将行github "johnsundell/wrap"
添加到您的Cartfile
中
手动
克隆仓库并将文件Wrap.swift
拖到Xcode项目中。
Swift Package Manager
将行.Package(url: "https://github.com/JohnSundell/Wrap.git", majorVersion: 2)
添加到您的Package.swift
文件中。
有关Wrap和其他开源项目的更多信息,请关注我的Twitter:@johnsundell
此外,请确保查看Unbox,它可让您轻松地对JSON进行解码。