License Platforms Build Status codecov

Outlaw

在 Swift 中,我们经常需要处理 JSON、plist 以及各种形式的 [String: Any]Outlaw 提供各种方法,以一种富有表现力且类型安全的方式来处理这些数据。Outlaw 将利用面向协议编程™的力量,帮助你编写声明式、高性能、错误处理完备的代码。

用法

使用 Outlaw 从 [String: Any] 中提取值非常简单,就像这样:

let name: String = try json.value(for: "name")
let url: URL = try json.value(for: "user.website") // extract from nested objects!

转换为模型

通常,我们希望获取一个可提取的对象(例如 [String: Any]),并将其反序列化为我们的本地模型。例如,我们可能希望获取一些 JSON 并使用它来初始化我们的本地模型。

struct User: Deserializable {
    var id: Int
    var name: String
    var email: String

    init(object: Extractable) throws {
        id = try object.value(for: "id")
        name = try object.value(for: "name")
        email = try object.value(for: "email")
    }
}

现在,仅通过提供一个简单的初始化器,你就可以直接从 [String: Any] 中提取你的模型

let users: [User] = try json.value(for: "users")

这太简单了!感谢面向协议编程™!

序列化模型

我们已经了解了如何从 [String: Any] 转换为我们的本地模型,但反过来呢?

extension User: Serializable {
	func serialized() -> [String: Any] {
        return [
            "id": "id",
            "name" : name,
            "email": email
        ]
    }
}

现在,你可能会想“我不能使用反射来自动完成这个吗?”你可以。如果你喜欢这样做,有一些其他很棒的框架供你使用。但 Outlaw 认为使用反射可能会导致问题。Outlaw 坚信所见即所得™,并且你可以轻松适应蛇形命名、驼峰命名,或者你的后端开发人员喜欢的任何命名风格的 API。Outlaw 代码是显式的、声明式的。不要只听 Outlaw 的一面之词——请阅读官方 Swift 博客中 这篇好文章的结尾。

错误处理

你是不关心错误的人吗?使用可选数据类型。

let users: [User]? = json.value(for: "users")

否则,将你的代码包装在 do-catch 块中,以便在出现问题时获取所有详细信息。

do {
	let users: [User] = try json.value(for: "users")
}
catch {
	print(error)
}

添加你自己的值

开箱即用,Outlaw 支持提取原生 Swift 类型,例如 StringInt 等,以及 URLDate 和任何符合 Deserializable 协议的类型,以及上述所有类型的数组或字典。

但是,Outlaw 不会让你孤立无援!添加你自己的 Outlaw 值类型就像用 Value 扩展你的类型一样简单。

extension CGPoint: Value {
    public static func value(from object: Any) throws -> CGPoint {
        guard let properties = object as? [String: CGFloat] else {
            throw OutlawError.typeMismatch(expected: [String: CGFloat].self, actual: type(of: object))
        }
        let x: CGFloat = properties["x"] ?? 0
        let y: CGFloat = properties["y"] ?? 0
        
        return CGPoint(x: x, y: y)
    }
}

通过简单地实现 value(from:),Outlaw 允许你立即这样做:

let point: CGPoint = try json.value(for: "point")

面向协议编程™ 再次发力!

中间值

不喜欢 Outlaw 实现的默认值提取方式吗?有不同的日期格式?没问题!提取值时,你只需要一个转换函数。

let formatter = DateFormatter()
formatter.timeZone = TimeZone(abbreviation: "GMT")
formatter.dateFormat = "MM/dd/yyyy"

let date: Date? = json.value(for: "date", with: { (dateString: String) -> Date? in
	return formatter.date(from: dateString)
})

我们还可以利用 swift 的强大功能,将上面的代码缩短为:

let date: Date? = json.value(for: "date", with: formatter.date)

性能

Outlaw 基于与 Marshal 相同的底层代码,并且具有相同的性能。你总是应该对基准测试持保留态度,但无论如何,请看看 这些基准测试。不幸的是,JSONShootout 项目的构建方式使得无法添加 Outlaw,因为与 Marshal 存在模糊的方法冲突。

目标

OutlawMarshal 的主要贡献者之一创建。但是,Marshal 被设计为一个精简的框架,需要扩展才能获得额外的功能。Outlaw 被设计为一个功能更丰富的框架,可以开箱即用地处理更多的数据提取场景。