开心果是一个 Swift 通用模型框架。通过利用 透镜 和值转换器,它允许您为任何递归数据结构创建类型安全的适配器,无论是 JSON、YAML 还是 XML。
如果您已经熟悉 Argo,请查看 Pistachiargo。
让我们从定义一个简单的模型开始
struct Origin {
var city: String
init(city: String = "") {
self.city = city
}
}
struct Person {
var name: String
var origin: Origin
init(name: String = "", origin: Origin = Origin()) {
self.name = name
self.origin = origin
}
}
透镜基本上是获取器和设置器的组合,提供对模型的视图
struct OriginLenses {
static let city = Lens(get: { $0.city }, set: { (inout origin: Origin, city) in
origin.city = city
})
}
struct PersonLenses {
static let name = Lens(get: { $0.name }, set: { (inout person: Person, name) in
person.name = name
})
static let origin = Lens(get: { $0.origin }, set: { (inout person: Person, origin) in
person.origin = origin
})
}
它们可以用来自访问和修改您的模型
var person = Person(name: "Felix", origin: Origin(city: "Berlin"))
person = set(PersonLenses.name, person, "Robb")
get(PersonLenses.name, person) // == "Robb"
并且您可以用组合、提升、转换等对它们进行操作
let composed = PersonLenses.origin >>> OriginLenses.city
person = set(composed, person, "New York")
get(composed, person) // == "New York"
var persons = [ person ]
let arrayLifted: Lens<[Person], [String]> = lift(composed)
persons = set(arrayLifted, [ person ], [ "San Francisco" ])
get(arrayLifted, persons) // == [ "San Francisco" ]
var result: Result<[Person], NSError> = success(persons)
let resultLifted: Lens<Result<[Person], NSError>, Result<[String], NSError>> = lift(arrayLifted)
result = set(resultLifted, result, success([ "London" ]))
get(resultLifted, result) // == .Success(Box([ "London" ]))
let valueTransformer: ValueTransformer<String, Int> = SocialSecurityNumberValueTransformer
let transformed = transform(PersonLenses.name, valueTransformer)
person = set(transformed, person, 1234567890)
get(PersonLenses.name, person) // == "Felix"
值转换器可以翻转、组合和提升
let flipped = flip(valueTransformer)
flipped.transformedValue(1234567890) // == "Felix"
let composed = flipped >>> UppercaseValueTransformer
flipped.transformedValue(1234567890) // == "FELIX"
let dictionaryLifted = lift([ "Felix": 1234567890 ], 0, "Unknown")
dictionaryLifted.transformedValue("Felix") // == 1234567890
dictionaryLifted.transformedValue("Hans") // == 0
dictionaryLifted.reverseTransformedValue(1234567890) // == "Felix"
dictionaryLifted.reverseTransformedValue(0) // == "Unknown"
let optionalLifted = lift(UppercaseValueTransformer, "")
optionalLifted.transformedValue("Felix") // == "FELIX"
optionalLifted.transformedValue(nil) // == ""
optionalLifted.reverseTransformedValue("FELIX") // == "felix"
optionalLifted.reverseTransformedValue("") // == nil
let arrayLifted = lift(UppercaseValueTransformer)
arrayLifted.transformedValue([ "Felix", "Robb" ]) // == [ "FELIX", "ROBB" ]
通过透镜和值转换器,您可以创建模型的适配器
struct Adapters {
static let origin = DictionaryAdapter(specification: [
"city_name": transform(OriginLenses.city, StringToAnyObjectValueTransformers)
], dictionaryTansformer: DictionaryToAnyObjectValueTransformers)
static let person = DictionaryAdapter(specification: [
"name": transform(PersonLenses.name, StringToAnyObjectValueTransformers),
"origin": transform(PersonLenses.origin, lift(origin, Origin()))
], dictionaryTansformer: DictionaryToAnyObjectValueTransformers)
}
嗯,那是什么?对了,原本的适配器被提升为值转换器。
使用 fix
为递归模型创建适配器
let adapter: DictionaryAdapter<Model, Data, Error> = fix { adapter in
// use `adapter` to reference the currently created adapter
}
适配器处理对数据的编码和解码
let adapter = Adapters.person
var person = Person(name: "Seb", origin: Origin(city: "Berlin"))
var data = adapter.encode(person)
// == .Success(Box([ "name": "Seb", "origin": [ "city_name": "Berlin" ] ]))
adapter.decode(Person(), from: data.value!)
// == .Success(Box(person))
both encode
及 decode
返回一个 LlamaKit.Result
,它持有编码/解码值或错误。这使您能够优雅地处理编码错误。
开心果是由 Felix Jendrusch 和 Robert Böhnke 构建的。柏林问候