KeyPathMapper is a thin library based on swift #KeyPath
feature which facilitates mapping between two different models.
pod "KeyPathMapper"
Create a file Package.swift
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "YourProject",
dependencies: [
.package(url: "https://github.com/marshallxxx/KeyPathMapper.git", "1.0.0" ..< "2.0.0")
],
targets: [
.target(name: "YourProject", dependencies: ["KeyPathMapper"])
]
)
// MARK: - Models
struct TypeA {
var name: String
var age: Int
var submodel: TypeASubmodel
}
struct TypeASubmodel {
var value: String = ""
}
struct TypeB {
var name: String
var age: Int?
var submodel: TypeBSubmodel
}
struct TypeBSubmodel {
var value: String = ""
}
// MARK: - Mapper generation
var mapper: KeyPathMapper<TypeA, TypeB> {
var mapper = KeyPathMapper<TypeA, TypeB>()
mapper.map(\TypeA.name, to: \TypeB.name)
mapper.map(\TypeA.age, to: \TypeB.age, fallbackValue: 0)
var submodelMapper = KeyPathMapper<TypeASubmodel, TypeBSubmodel>()
submodelMapper.map(\TypeASubmodel.value, to: \TypeBSubmodel.value)
mapper.map(\TypeA.submodel, to: \TypeB.submodel, with: NestedMapperTransformer(keyPathMapper: submodelMapper))
return mapper
}
// MARK: - Use of mapper
var a = TypeA(name: "TypeA", age: 20, submodel: TypeASubmodel(value: "TypeASubmodel"))
Var b = TypeB(name: "TypeB", age: nil, submodel: TypeBSubmodel(value: "TypeBSubmodel"))
let updated: TypeA = mapper.updated(value: a, from: b)
// OR
mapper.update(value: &a, from: b)
KeyPathMapper represent a base class for mapping two models.
Initialisation:
KeyPathMapper<TypeA, TypeB>()
- If it’s mapping same type then you can avoid
transformer
part
mapper.map(\TypeA.name, to: \TypeB.name)
- If it’s mapping same type, but one of them is optional library is providing a simplified mapping function. Fallback value is used when you try to map
nil
to non optional value.
mapper.map(\TypeA.age, to: \TypeB.age, fallbackValue: 0)
- In case you are sure that value will exist at time of mapping you can force unwrap KeyPath
mapper.map(\TypeA.age, to: \TypeB.age!)
- If you have nested subtype, you can use
NestedMapperTransformer
with their ownKeyPathMapper
var submodelMapper = KeyPathMapper<TypeASubmodel, TypeBSubmodel>()
submodelMapper.map(\TypeASubmodel.value, to: \TypeBSubmodel.value)
mapper.map(\TypeA.submodel, to: \TypeB.submodel, with: NestedMapperTransformer(keyPathMapper: submodelMapper))
Sometimes you may want to initialise a model from another one, through KeyPathMapper
. You can do that by making your model conform to MappableInitializable
, then you can use:
let updated: TypeA = mapper.convert(from: b)
You can observe KeyPath
change and map it into another model whenever new value is set. This functionality is provided only if observed object is instance of NSObject
and observing model is a reference type.
mapper.observe(object: d, mapInto: c)
MapperTransfomer are transforming value on mapping. You can create your own transformations by creating a type which conforms to MapperTransfomer.
Example transforming Int to String and vice versa will look like:
public struct IntStringMapperTransformer: MapperTransfomer {
public typealias TypeA = Int
public typealias TypeB = String
public func transform(from previous: TypeB, with value: TypeA) -> TypeB {
return TypeB(value)
}
public func transform(from previous: TypeA, with value: TypeB) -> TypeA {
return TypeA(value)!
}
}