Snake(之前称为Serializable)是一个用于创建易于序列化和反序列化JSON的模型对象或结构的框架。它可以轻松扩展,处理使用REST API时常用的所有常见数据类型,以及自定义对象的递归解析。专为与Alamofire一起使用而设计。
它设计为与我们的辅助应用程序一起使用,即Model Boiler,使得模型创建变得轻松。
Snake使用协议扩展和静态类型实现。
📑 目录
🐍 为什么选择Snake?
有许多其他编码和解码框架可供选择。为什么您应该使用Snake?
- 性能。Snake速度快,比类似框架快四倍。
- 特性。Snake可以解析您抛给它的任何内容。嵌套对象、枚举、URL、UIColor 等!
Model Boiler。此类框架都需要编写繁琐的模板代码,需要花费大量时间。《a href="https://github.com/nodes-ios/ModelBoiler">
Model Boiler可以立即为您生成。
- Alamofire集成。使用包含的Alamofire扩展,实现返回解析模型数据的API调用变得简单至极!
- 可扩展性。添加对其他数据类型的解析变得容易。
- 持久化。结合我们的缓存框架Cashier,Snake对象可以非常容易地持久化到磁盘。
Serpent Xcode 文件模板 使得在Xcode中创建模型文件更加容易。
📝 要求
- 支持iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
- Swift 3.0+
(旧版本中支持Swift 2.2 & Swift 2.3)
📦 安装
Carthage
github "nodes-ios/Serpent" ~> 1.0
与较低Swift版本兼容的最后一个版本
Swift 2.3
github "nodes-ios/Serpent" == 0.13.2
Swift 2.2
github "nodes-ios/Serpent" == 0.11.2
注意: Serpent 之前被称为 Serializable.
CocoaPods
从以下选项中选择,将其添加到您的 Podfile
并运行 pod install
pod 'Serpent', '~> 1.0' # Just core
pod 'Serpent/Extensions', '~> 1.0' # Includes core and all extensions
pod 'Serpent/AlamofireExtension', '~> 1.0' # Includes core and Alamofire extension
pod 'Serpent/CashierExtension', '~> 1.0' # Includes core and Cashier extension
注意: CocoaPods 仅支持 Swift 版本 3.0 和更高版本的 Serpent。
Swift Package Manager
要使用 Serpent 作为 Swift Package Manager 包,只需将以下内容添加到您的 Package.swift
文件中。
import PackageDescription
let package = Package(
name: "YourPackage",
dependencies: [
.Package(url: "https://github.com/nodes-ios/Serpent.git", majorVersion: 1)
]
)
🔧 设置
我们强烈建议您使用我们的 Model Boiler 来辅助生成符合 Serpent 需要的代码。安装和使用说明可以在 Model Boiler GitHub 仓库 中找到。
💻 使用
入门指南
Serpent 支持所有原始类型、enum
、URL
、Date
、UIColor
、其他 Serpent
模型以及所有上述类型的 Array
。您的变量声明可以具有默认值或为可选值。
原始类型不需要显式类型,如果 Swift 可以正常推断,则 var name: String = ""
和 var name = ""
都可以工作。当然,可选值需要显式类型。
注意: 您创建的枚举必须符合
RawRepresentable
,这意味着它们必须具有显式类型。否则,解析器不知道如何处理它收到的数据。
创建您的模型结构或类
struct Foo {
var id = 0
var name = ""
var address: String?
}
注意:类必须被标记为
final
。
Encodable
和 Decodable
添加所需的函数
为 extension Foo: Serializable {
init(dictionary: NSDictionary?) {
id <== (self, dictionary, "id")
name <== (self, dictionary, "name")
address <== (self, dictionary, "address")
}
func encodableRepresentation() -> NSCoding {
let dict = NSMutableDictionary()
(dict, "id") <== id
(dict, "name") <== name
(dict, "address") <== address
return dict
}
}
注意:您可以添加对
Serializable
的遵从性,它是Encodable
和Decodable
的类型别名。
而且就是这样!如果您使用的是 Model Boiler,此扩展将为您自动生成,因此您不需要为每个模型都输入这些内容。
使用 Serpent 模型
您可以使用字典(例如,从解析的 JSON)创建您模型的新实例。
let dictionary = try JSONSerialization.jsonObject(with: someData, options: .allowFragments) as? NSDictionary
let newModel = Foo(dictionary: dictionary)
您可以通过调用 encodableRepresentation()
生成您模型的字典版本。
let encodedDictionary = newModel.encodableRepresentation()
更复杂的示例
在这个示例中,我们有两个模型,学生和学校。
struct Student {
enum Gender: String {
case male = "male"
case female = "female"
case unspecified = "unspecified"
}
var name = ""
var age: Int = 0
var gender: Gender?
}
struct School {
enum Sport: Int {
case football
case basketball
case tennis
case swimming
}
var name = ""
var location = ""
var website: URL?
var students: [Student] = []
var sports: [Sport]?
}
您可以使其尽可能复杂,语法始终保持相同。这些扩展将是
extension Student: Serializable {
init(dictionary: NSDictionary?) {
name <== (self, dictionary, "name")
age <== (self, dictionary, "age")
gender <== (self, dictionary, "gender")
}
func encodableRepresentation() -> NSCoding {
let dict = NSMutableDictionary()
(dict, "name") <== name
(dict, "age") <== age
(dict, "gender") <== gender
return dict
}
}
extension School: Serializable {
init(dictionary: NSDictionary?) {
name <== (self, dictionary, "name")
location <== (self, dictionary, "location")
website <== (self, dictionary, "website")
students <== (self, dictionary, "students")
sports <== (self, dictionary, "sports")
}
func encodableRepresentation() -> NSCoding {
let dict = NSMutableDictionary()
(dict, "name") <== name
(dict, "location") <== location
(dict, "website") <== website
(dict, "students") <== students
(dict, "sports") <== sports
return dict
}
}
再次提醒, Model Boiler 在不到一秒内为您生成所有这些代码!
使用Alamofire
Serpent自带整合了Alamofire,通过扩展Alamofire的Request
构造,添加了responseSerializable(completion:unwrapper)
函数。
该扩展用Alamofire的熟悉的Response
类型来保存返回的数据,并使用其泛型关联类型来自动解析数据。
考虑一个返回一个匹配上面示例中结构的单个school
端点。要实现调用,只需向共享连接管理器添加一个函数或您喜欢的任何地方。
func requestSchool(completion: @escaping (DataResponse<School>) -> Void) {
request("http://somewhere.com/school/1", method: .get).responseSerializable(completion)
}
在消费方法中使用它
requestSchool() { (response) in
switch response.result {
case .success(let school):
//Use your new school object!
case .failure(let error):
//Handle the error object, or check your Response for more detail
}
}
对于对象的数组,使用相同的技术
static func requestStudents(completion: @escaping (DataResponse<[Student]>) -> Void) {
request("http://somewhere.com/school/1/students", method: .get).responseSerializable(completion)
}
一些API将数据包装在容器中。使用unwrapper
闭包来实现。假设您的/students
端点返回的数据被包装在students
对象中
{
"students" : [
{
"..." : "..."
},
{
"..." : "..."
}
]
}
unwrapper
闭包有2个输入参数:sourceDictionary
(JSON响应字典)和expectedType
(目标Serpent的类型)。返回将作为可序列化初始化器的输入对象。
static func requestStudents(completion: (DataResponse<[Student]>) -> Void) {
request("http://somewhere.com/school/1/students", method: .get).responseSerializable(completion, unwrapper: { $0.0["students"] })
}
如果您需要在每个调用中解包响应数据,可以使用以下方法安装默认解包器
Parser.defaultWrapper = { sourceDictionary, expectedType in
// You custom unwrapper here...
return sourceDictionary
}
expectedType
可以用于根据类型名使用反射动态确定键。这在进行分页数据处理时特别有用。
请参阅此处了解我们在Nodes项目中如何使用此功能。
注意: responseSerializable
在请求内部调用validate().responseJSON()
,因此您不必这样做。
日期解析
Serpent可以从JSON中的日期字符串创建Date
对象。默认情况下,Serpent可以解析以下格式的日期字符串:yyyy-MM-dd'T'HH:mm:ssZZZZZ
,yyyy-MM-dd'T'HH:mm:ss
,yyyy-MM-dd
。如果您需要解析其他日期格式,可以在代码中添加此行(例如,在AppDelegate的didFinishLaunchingWithOptions:
中)
Date.customDateFormats = ["yyyyMMddHHmm", "yyyyMMdd"] // add the custom date formats you need here
自定义日期格式不会替代默认格式,它们仍然被支持。
👥 致谢
由
📄 许可证
Serpent遵从MIT许可证。有关更多信息,请参阅LICENSE文件。