Alter is available under the MIT license. See the LICENSE file for more info.
Usage
Basic Usage
For example, if you need to map User JSON to Swift Object, you just need to create User class/struct and implement Alterable protocol and then mark all the property you need to map to JSON with @Mapped attributes.
struct User: Codable, Alterable {
@Mapped
var name: String = ""
@Mapped
var userName: String = ""
@Mapped
var age: Int = 0
}
or just use AlterCodable which is typealias of Alterable & Codable:
struct User: AlterCodable {
@Mapped
var name: String = ""
@Mapped
var userName: String = ""
@Mapped
var age: Int = 0
}
and then you can parse JSON to User Swift Object or vice versa like this
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)
The Alterable actually just a simple protocol which will work with full functionality if paired with Codable. The only extendable function from Codable is that the Alterable will be use reflection to get all Mapped attributes and using it to do two way mapping.
public protocol Alterable
Since AlterCodable conform Codable, you could always do decode using codable decoder or encode using codable encoder just like 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)
The real power of Alterable is the mapping feature which eliminate the requirement of enumeration CodingKey when doing key mapping manually. If the property name of Decoded data is different with property in Swift object, then you can pass the name of that property at the attribute instead of creating CodingKey enumeration. Those properties then will be mapped using those key.
struct User: AlterCodable {
@Mapped(key: "full_name")
var fullName: String = ""
@Mapped(key: "user_name")
var userName: String = ""
@Mapped
var age: Int = 0
}
You could always do decode and encode manually by implement init(from:) throws and func encode(to:) throws. Alterable have some extensions to help you implement decode and encode manually
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")
}
}
}
Manual Mapping
If you use non Codable type for property, or maybe you want to represent different data in Swift property other than real property, you could use @AlterMapped attribute instead of @Mapped and pass TypeAlterer as converter. With this method, you don’t need to implement init(from:) throws and func encode(to:) throws manually.
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
}
If your data type is optional, array or both, you can use optionally computed property or forArray computed property or even the combination of both, since the property is the extension of the TypeAlterer protocol. The order of the property call will affect the result of TypeAlterer type.
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?] = []
}
There are native TypeAlterer from Alter which you could use:
UnixLongDateAlterer which for converting Date into Int64 or vice versa
StringDateAlterer which for converting Date into patterned String or vice versa
Base64DataAlterer which for converting Data into Base64 String or vice versa
Base64ImageAlterer which for converting UIImage into Base64 String or vice versa
If you want to implement your own TypeAlterer, just create class or struct that implement TypeAlterer. Value is the property value type, AlteredValue is encoded value, should be implement 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()
}
}
Nested Property
Alter can map nested property by using . example
instead of doing this:
struct Event: AlterCodable {
@Mapped
var title: String = ""
@Mapped
var ticket: Ticket = .init(price: 0)
struct Ticket: AlterCodable {
@Mapped
var price: Double = 0
}
}
you can skip Ticket object by using “ticket.price” key instead:
struct Event: AlterCodable {
@Mapped
var title: String = ""
@Mapped(key: "ticket.price")
var price: Double = 0
}
Mutability
In most case we don’t want our model to be mutable, But since Alter need the property to be mutable so it could be assigned on object creation, you could just make the setter private.
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
}
There’s some extras if you want to have mutable ability to treat Alterable as Dictionary. Any object that implement MutableAlterable protocol can be treated like Dictionary.
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)
The subscript can accept any type as long the type can be cast into property real type or altered type.
Alter
Alter is framework to make mapping Codable property and key easier.
With Alter, you don’t need to create CodingKey to manually mapping key and property.
Alter using propertyWrapper and reflection to achive key property mapping.
Requirements
Installation
Cocoapods
Alter is available through CocoaPods. To install it, simply add the following line to your Podfile:
Swift Package Manager from XCode
Swift Package Manager from Package.swift
Add as your target dependency in Package.swift
Use it in your target as
Alter
Author
Nayanda Haberty, hainayanda@outlook.com
License
Alter is available under the MIT license. See the LICENSE file for more info.
Usage
Basic Usage
For example, if you need to map User JSON to Swift Object, you just need to create User class/struct and implement
Alterable protocol
and then mark all the property you need to map to JSON with@Mapped attributes.
or just use
AlterCodable
which istypealias
ofAlterable & Codable
:and then you can parse JSON to User Swift Object or vice versa like this
The
Alterable
actually just a simple protocol which will work with full functionality if paired withCodable
. The only extendable function fromCodable
is that theAlterable
will be use reflection to get all Mapped attributes and using it to do two way mapping.Since
AlterCodable
conformCodable
, you could always do decode using codable decoder or encode using codable encoder just likeCodable
The real power of
Alterable
is the mapping feature which eliminate the requirement of enumerationCodingKey
when doing key mapping manually. If the property name of Decoded data is different with property in Swift object, then you can pass the name of that property at the attribute instead of creatingCodingKey
enumeration. Those properties then will be mapped using those key.You could always do decode and encode manually by implement
init(from:) throws
andfunc encode(to:) throws
.Alterable
have some extensions to help you implement decode and encode manuallyManual Mapping
If you use non
Codable
type for property, or maybe you want to represent different data in Swift property other than real property, you could use@AlterMapped
attribute instead of@Mapped
and passTypeAlterer
as converter. With this method, you don’t need to implementinit(from:) throws
andfunc encode(to:) throws
manually.If your data type is optional, array or both, you can use
optionally
computed property orforArray
computed property or even the combination of both, since the property is the extension of theTypeAlterer
protocol. The order of the property call will affect the result ofTypeAlterer
type.There are native TypeAlterer from Alter which you could use:
UnixLongDateAlterer
which for converting Date into Int64 or vice versaStringDateAlterer
which for converting Date into patterned String or vice versaBase64DataAlterer
which for converting Data into Base64 String or vice versaBase64ImageAlterer
which for converting UIImage into Base64 String or vice versaIf you want to implement your own
TypeAlterer
, just create class or struct that implementTypeAlterer
.Value
is the property value type,AlteredValue
is encoded value, should be implementCodable
.Nested Property
Alter can map nested property by using
.
exampleinstead of doing this:
you can skip Ticket object by using “ticket.price” key instead:
Mutability
In most case we don’t want our model to be mutable, But since Alter need the property to be mutable so it could be assigned on object creation, you could just make the setter private.
There’s some extras if you want to have mutable ability to treat Alterable as Dictionary. Any object that implement
MutableAlterable
protocol can be treated like Dictionary.or by using
MutableAlterCodable
which istypealias
ofMutableAlterable & Codable
Then you could just treat it like dictionary
The subscript can accept any type as long the type can be cast into property real type or altered type.