修改 1.2.9

Alter 1.2.9

nayanda1 维护。



Alter 1.2.9

  • nayanda

Alter

Alter是一个框架,用来使映射Codable属性和键更加容易。

使用Alter,您不需要创建CodingKey来手动映射键和属性。

Alter通过使用属性包装器和反射来实现键属性映射。

build test Version License Platform

要求

  • Swift 5.1或更高版本
  • iOS 10.0或更高版本

安装

CocoaPods

Alter可以通过CocoaPods获取。要安装它,只需将以下行添加到您的Podfile中:

pod 'Alter'

Swift 包管理器

首先,创建一个 Package.swift 文件并添加此 GitHub 网址。应如下所示

dependencies: [
    .package(url: "https://github.com/nayanda1/Alter.git", from: "1.2.0")
]

然后,在您使用之前,运行 swift build 来编译依赖项

作者

Nayanda Haberty, [email protected]

许可证

ALTER 在 MIT 许可证下可用。有关更多信息,请参阅 LICENSE 文件。

用法

基本用法

例如,如果您需要将 User JSON 映射到 Swift 对象,只需创建 User 类/结构体并实现 Alterable 协议,然后将所有需要映射到 JSON 的属性标记为 @Mapped 属性。

struct User: Codable, Alterable {
    
    @Mapped
    var name: String = ""
    
    @Mapped
    var userName: String = ""
    
    @Mapped
    var age: Int = 0
}

或直接使用 AlterCodable,它是 Alterable & Codable 的别名。

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 将使用反射来获取所有已映射的属性,并使用它们进行双向映射。

public protocol Alterable

由于 AlterCodable 符合 Codable,您可以使用与 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: Alterable {
    
    @Mapped(key: "full_name")
    var fullName: String = ""
    
    @Mapped(key: "user_name")
    var userName: String = ""
    
    @Mapped
    var age: Int = 0
}

您可以通过实现init(from:) throwsfunc 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:) throwsfunc 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转换为模式化字符串或反向转化。
  • Base64DataAlterer,用于将Data转换为Base64字符串或反向转化。
  • Base64ImageAlterer,用于将UIImage转换为Base64字符串或反向转化。

如果您想实现自己的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需要属性可变,以便在对象创建时进行分配,因此您只需将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视为字典,有一些额外功能。任何实现MutableAlterable协议的对象都可以像字典一样处理。

struct MutableUser: MutableAlterable {
    @Mapped(key: "user_name")
    var userName: String? = nil
    ...
    ...
    ...
}

或者使用MutableAlterCodable,它是MutableAlterable & Codable的类型别名

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)

索引器可以接受任何类型,只要该类型可以转换为属性的实际类型或转换后的类型。