Alter 是一个框架,可以更轻松地映射 Codable 属性和键。
使用 Alter,您无需创建 CodingKey 来手动映射键和属性。
Alter 使用 propertyWrapper 和反射来实现键-属性映射。
Alter 可以通过 CocoaPods 获得。 要安装它,只需将以下行添加到您的 Podfile 中
pod 'Alter', '~> 1.2.9'
在 Package.swift 中将其添加为您的目标依赖项
dependencies: [
.package(url: "https://github.com/hainayanda/Alter.git", .upToNextMajor(from: "1.2.9"))
]
在您的目标中使用它作为 Alter
.target(
name: "MyModule",
dependencies: ["Alter"]
)
Nayanda Haberty, hainayanda@outlook.com
Alter 在 MIT 许可证下可用。 有关更多信息,请参阅 LICENSE 文件。
例如,如果您需要将 User JSON 映射到 Swift 对象,您只需要创建 User 类/结构体并实现 Alterable protocol
,然后使用 @Mapped
属性标记所有需要映射到 JSON 的属性。
struct User: Codable, Alterable {
@Mapped
var name: String = ""
@Mapped
var userName: String = ""
@Mapped
var age: Int = 0
}
或者直接使用 AlterCodable
,它是 Alterable & Codable
的 typealias
。
struct User: AlterCodable {
@Mapped
var name: String = ""
@Mapped
var userName: String = ""
@Mapped
var age: Int = 0
}
然后您可以像这样将 JSON 解析为 User Swift 对象或反之亦然
let user: User = getUserFromSomewhere()
//to JSON
let jsonObject: [String: Any] = try! user.toJSON()
let jsonString: String = try! user.toJSONString()
let jsonData: Data = try! user.toJSONData()
//from JSON
let userFromJSON: User = try! .from(json: jsonObject)
let userFromString: User = try! .from(jsonString: jsonString)
let userFromData: User = try! .from(jsonData: jsonData)
实际上,Alterable
只是一个简单的协议,如果与 Codable
配对,它将发挥全部功能。 从 Codable
唯一可扩展的功能是 Alterable
将使用反射来获取所有 Mapped 属性,并使用它来进行双向映射。
public protocol Alterable
由于 AlterCodable
符合 Codable
,您可以始终使用 codable decoder 进行解码,或者使用 codable encoder 进行编码,就像 Codable
一样
let user: User = getUserFromSomewhere()
let propertyListData = try! PropertyListEncoder().encode(user)
let decodedPropertyList = try! PropertyListDecoder().decode(User.self, from: propertyListData)
let jsonData = try! JSONEncoder().encode(user)
let decodedJsonData = try! JSONDecoder().decode(User.self, from: jsonData)
Alterable
的真正强大之处在于映射功能,它消除了手动进行键映射时枚举 CodingKey
的要求。 如果解码数据的属性名称与 Swift 对象中的属性名称不同,则可以在属性传递该属性的名称,而不是创建 CodingKey
枚举。 然后,这些属性将使用这些键进行映射。
struct User: AlterCodable {
@Mapped(key: "full_name")
var fullName: String = ""
@Mapped(key: "user_name")
var userName: String = ""
@Mapped
var age: Int = 0
}
您始终可以通过实现 init(from:) throws
和 func encode(to:) throws
来手动进行解码和编码。 Alterable
有一些扩展来帮助您手动实现解码和编码
struct User: AlterCodable {
@Mapped(key: "full_name")
var fullName: String = ""
@Mapped(key: "user_name")
var userName: String = ""
@Mapped
var age: Int = 0
var image: UIImage? = nil
required init() {}
init(from decoder: Decoder) throws {
self.init()
// this will automatically decode all Mapped properties and return container which you could use to decode property that not mapped
let container = try decodeMappedProperties(from: decoder)
// you could decode any type as long is Codable and passing String as a Key
let base64Image: String = try container.decode(forKey: "image")
if let imageData: Data = Data(base64Encoded: base64Image) {
self.image = UIImage(data: imageData)
}
}
func encode(to encoder: Encoder) throws {
// this will automatically encode all Mapped properties and return container which you could use to encode property that not mapped
var container = try encodeMappedProperties(to: encoder)
if let base64Image = self.image.pngData()?.base64EncodedString() {
// you could encode any type as long is Codable and passing String as a Key
container.encode(value: base64Image, forKey: "address")
}
}
}
如果您对属性使用非 Codable
类型,或者您想在 Swift 属性中表示与真实属性不同的数据,您可以使用 @AlterMapped
属性而不是 @Mapped
并传递 TypeAlterer
作为转换器。 使用此方法,您无需手动实现 init(from:) throws
和 func encode(to:) throws
。
struct User: AlterCodable {
@Mapped(key: "full_name")
var fullName: String = ""
@Mapped(key: "user_name")
var userName: String = ""
@Mapped
var age: Int = 0
// manual mapping
@AlterMapped(alterer: Base64ImageAlterer(format: .png))
var image: UIImage = .init()
// manual mapping with key
@AlterMapped(key: "birth_date", alterer: StringDateAlterer(pattern: "dd-MM-yyyy"))
var birthDate: Date = .distantPast
}
如果您的数据类型是可选的、数组或两者,您可以使用 optionally
计算属性或 forArray
计算属性,甚至可以使用两者的组合,因为该属性是 TypeAlterer
协议的扩展。 属性调用的顺序会影响 TypeAlterer
类型的结果。
struct User: AlterCodable {
@Mapped(key: "full_name")
var fullName: String = ""
@Mapped(key: "user_name")
var userName: String = ""
@Mapped
var age: Int = 0
@AlterMapped(key: "birth_date", alterer: StringDateAlterer(pattern: "dd-MM-yyyy"))
var birthDate: Date = .distantPast
// optional
@AlterMapped(alterer: Base64ImageAlterer(format: .png).optionally)
var image: UIImage? = nil
// array
@AlterMapped(key: "login_times", alterer: UnixLongDateAlterer().forArray)
var loginTimes: [Date] = []
// array optional
@AlterMapped(key: "crashes_times", alterer: UnixLongDateAlterer().forArray.forOptional)
var crashesTimes: [Date]? = nil
// array of optional
@AlterMapped(key: "some_times", alterer: UnixLongDateAlterer().forOptional.forArray)
var someTimes: [Date?] = []
}
Alter 提供了一些原生 TypeAlterer,您可以直接使用
UnixLongDateAlterer
用于将 Date 转换为 Int64 或反之亦然StringDateAlterer
用于将 Date 转换为模式化的 String 或反之亦然Base64DataAlterer
用于将 Data 转换为 Base64 String 或反之亦然Base64ImageAlterer
用于将 UIImage 转换为 Base64 String 或反之亦然如果您想实现自己的 TypeAlterer
,只需创建实现 TypeAlterer
的类或结构体。 Value
是属性值类型,AlteredValue
是编码值,应该实现 Codable
。
public struct MyOwnDataAlterer: TypeAlterer {
public typealias Value = Data
public typealias AlteredValue = String
public init() { }
public func alter(value: Data) -> String {
value.base64EncodedString()
}
public func alterBack(value: String) -> Data {
Data(base64Encoded: value) ?? .init()
}
}
Alter 可以使用 .
映射嵌套属性,例如
而不是这样做
struct Event: AlterCodable {
@Mapped
var title: String = ""
@Mapped
var ticket: Ticket = .init(price: 0)
struct Ticket: AlterCodable {
@Mapped
var price: Double = 0
}
}
您可以使用 "ticket.price" 键跳过 Ticket 对象
struct Event: AlterCodable {
@Mapped
var title: String = ""
@Mapped(key: "ticket.price")
var price: Double = 0
}
在大多数情况下,我们不希望我们的模型是可变的,但是由于 Alter 需要属性是可变的,以便可以在对象创建时进行分配,您可以只将 setter 设置为私有。
struct User: AlterCodable {
@Mapped(key: "full_name")
private(set) var fullName: String = ""
@Mapped(key: "user_name")
private(set) var userName: String = ""
@Mapped
private(set) var age: Int = 0
}
如果您希望具有可变能力来将 Alterable 视为 Dictionary,则有一些额外的功能。 任何实现 MutableAlterable
协议的对象都可以像 Dictionary 一样处理。
struct MutableUser: MutableAlterable {
@Mapped(key: "user_name")
var userName: String? = nil
...
...
...
}
或者使用 MutableAlterCodable
,它是 MutableAlterable & Codable
的 typealias
。
struct MutableUser: MutableAlterCodable {
@Mapped(key: "user_name")
var userName: String? = nil
...
...
...
}
然后您可以像处理字典一样处理它
let user = MutableUser()
user[mappedKey: "user_name"] = "this is username"
// will print "this is username"
print(user.userName)
let userName: String = user[mappedKey: "user_name"] ?? ""
// will print "this is username"
print(userName)
下标可以接受任何类型,只要该类型可以强制转换为属性的真实类型或更改后的类型即可。