HollowCodable is a codable customization using property wrappers library for Swift.
- Make Complex Codable Serializate a breeze with declarative annotations!
- It does not support the custom coding key of a certain attribute. Once you have this requirement, either implement all coding keys manually to modify the desired coding key, or you have to set Decoder when decode, which is extremely inconvenient.
- Some non-Codable attributes cannot be ignored. The coding key needs to be implemented manually.
- Decode does not support multiple coding keys mapping to the same property.
- The default values of the model cannot be used, and it is obviously unreasonable to throw missing data errors instead of using the default values in the definition when decode data is missing.
- There is no support for simple type conversions, such as converting 0/1 to false/true, "123" to Int 123, or vice versa, who can ensure that the server will not accidentally modify the field type and cause the app to fail?
- Do not support Any property, many times our home page model will have a lot of submodels, but each submodel data structure is not the same, then need to decoding into Any, and then according to the module to confirm the submodel.
ok, All of these issues can be resolved after you use HollowCodable, so you're welcome!
- Immutable: Made immutable via property wrapper, It can be used with other Coding.
struct YourModel: HollowCodable {
@Immutable
var id: Int
@Immutable @BoolCoding
var bar: Bool?
}
- IgnoredKey: Add this to an Optional Property to not included it when Encoding or Decoding.
struct YourModel: HollowCodable {
@IgnoredKey
var ignorKey: String? = "Condy" // Be equivalent to use `lazy`.
lazy var ignorKey2: String? = "Coi"
}
- DefaultBacked: Set different default values for common types.
- This library has built-in many default values, such as Int, Bool, String, Array...
- if we want to set a different default value for the field.
struct YourModel: HollowCodable {
@DefaultBacked<Int>
var val: Int // If the field is not an optional, default is 0.
@DefaultBacked<Bool>
var boo: Bool // If the field is not an optional, default is false.
@DefaultBacked<String>
var string: String // If the field is not an optional, default is "".
@DefaultBacked<AnyDictionary>
var list: [String: Any] // If the field is not an optional, default is [:].
}
- Base64Coding: For a Data property that should be serialized to a Base64 encoded String.
struct YourModel: HollowCodable {
@Base64Coding
var base64Data: Data?
}
- Base64DataTransform: Serialized to a base 64 Data encoded String.
struct YourModel: HollowCodable {
var base64Data: Data?
static var codingKeys: [CodingKeyMapping] {
return [
CodingKeys.base64Data <-- "base64_data",
CodingKeys.base64Data <-- Base64DataTransform(),
]
}
}
And you can customize your own data (de)serialization type with DataValue.
Like:
public enum YourData: DataConverter {
public typealias Value = String
public typealias FromValue = Data
public typealias ToValue = String
public static let hasValue: Value = ""
public static func transformToValue(with value: FromValue) -> ToValue? {
// data to string..
}
public static func transformFromValue(with value: ToValue) -> FromValue? {
// string to data..
}
}
Used:
struct YourModel: HollowCodable {
@AnyBacked<DataValue<YourData>>
var data: Data?
}
- ISO8601DateCoding: Decodes
String
orTimeInterval
values as an ISO8601 date. - RFC2822DateCoding: Decodes
String
orTimeInterval
values as an RFC 2822 date. - RFC3339DateCoding: Decodes
String
orTimeInterval
values as an RFC 3339 date.
struct YourModel: HollowCodable {
@ISO8601DateCoding
var iso8601: Date? // Now decodes to ISO8601 date.
@RFC2822DateCoding
var data1: Date? // Now decodes RFC 2822 date.
@RFC3339DateCoding
var data2: Date? // Now decodes RFC 3339 date.
}
- SecondsSince1970DateCoding: For a Date property that should be serialized to SecondsSince1970.
- MillisecondsSince1970DateCoding: For a Date property that should be serialized to MillisecondsSince1970.
struct YourModel: HollowCodable {
@SecondsSince1970DateCoding
var timestamp: Date // Now encodes to SecondsSince1970
@MillisecondsSince1970DateCoding
var timestamp2: Date // Now encodes to MillisecondsSince1970
}
Supports (de)serialization using different formats, like:
struct YourModel: HollowCodable {
@DateCoding<Hollow.DateFormat.yyyy_mm_dd, Hollow.Timestamp.secondsSince1970>
var time: Date? // Decoding use `yyyy-MM-dd` and Encoding use `seconds` timestamp.
}
- CustomDateFormatTransform: Serialized to a custom Date encoded String.
- DateFormatterTransform: Serialized to a custom Date encoded String.
- ISO8601DateTransform: Serialized to a iso8601 time format Date encoded String.
- RFC3339DateTransform: Serialized to a RFC 3339 time format Date encoded String.
- RFC2822DateTransform: Serialized to a RFC 2822 time format Date encoded String.
- TimestampDateTransform: Serialized to a timestamp Since1970 time format Date encoded String/TimeInterval.
struct YourModel: HollowCodable {
var iso8601: Date? // Now decodes to ISO8601 date.
var data1: Date? // Now decodes RFC 2822 date.
var data2: Date? // Now decodes RFC 3339 date.
var timestampDate: Date? // Now decodes seconds Since1970 date.
static var codingKeys: [CodingKeyMapping] {
return [
TransformKeys(location: CodingKeys.iso8601, tranformer: ISO8601DateTransform()),
CodingKeys.data1 <-- RFC2822DateTransform(),
CodingKeys.data2 <-- RFC3339DateTransform(),
CodingKeys.timestampDate <-- TimestampDateTransform(type: .seconds),
]
}
}
- HexColorCoding: For a Color property that should be serialized to a hex encoded String.
- Support the hex string color with format
#RGB
γ#RGBA
γ#RRGGBB
γ#RRGGBBAA
.
- Support the hex string color with format
- RGBColorCoding: Decoding a red/green/blue to color.
- RGBAColorCoding: Decoding a red/green/blue/alpha to color.
struct YourModel: HollowCodable {
@HexColorCoding
var color: HollowColor?
@RGBColorCoding
var background_color: HollowColor?
}
- BoolCoding: Sometimes an API uses an
Bool
γInt
orString
for a booleans.- Uses <= 0 as false, and > 0 as true.
- Uses lowercase "true"/"yes"/"y"/"t"/"1"/">0" as true,
- Uses lowercase "false"/"no"/"f"/"n"/"0" as false.
- FalseBoolCoding: If the field is not an optional type, default false value.
- TrueBoolCoding: If the field is not an optional type, default true value.
struct YourModel: HollowCodable {
@Immutable @BoolCoding
var bar: Bool?
@FalseBoolCoding
var hasD: Bool
@TrueBoolCoding
var hasT: Bool
}
- EnumCoding: To be convertable, An enum must conform to RawRepresentable protocol. Nothing special need to do now.
struct YourModel: HollowCodable {
@EnumCoding<AnimalType>
var animal: AnimalType? //
}
enum AnimalType: String {
case Cat = "cat"
case Dog = "dog"
case Bird = "bird"
}
- NSDecimalNumberCoding: Decoding the
String
γDouble
γFloat
γCGFloat
γInt
orInt64
to a NSDecimalNumber property.
struct YourModel: HollowCodable {
@DecimalNumberCoding
var amount: NSDecimalNumber?
}
- DictionaryCoding: Support any value property wrapper with dictionary.
- ArrayCoding: Support any value dictionary property wrapper with array.
- AnyXCoding: Support for any general type,
"mixDict": {
"sub": {
"amount": "52.9",
},
"array": [{
"val": 718,
}, {
"val": 911,
}],
"opt": null
}
struct YourModel: HollowCodable {
@AnyBacked<AnyDictionary>
var mixDict: [String: Any]? // Nested support.
@AnyBacked<AnyDictionaryArray>
var mixLiat: [[String: Any]]?
@AnyBacked<AnyX>
var x: Any? // Also nested support.
}
Supports decoding network api response data.
{
"code": 200,
"message": "test case.",
"data": [{
"id": 2,
"title": "Harbeth Framework",
"github": "https://github.com/yangKJ/Harbeth",
"amount": "23.6",
"hex_color": "#FA6D5B",
"type": "text1",
"timestamp" : 590277534,
"bar": 1,
"hasDefBool": 2,
"time": "2024-05-29 23:49:55",
"iso8601": null,
"anyString": 5,
"background_color": {
"red": 255,
"green": 128,
"blue": 128
},
"dict": {
"amount": "326.0"
},
"mixDict": {
"sub": {
"amount": "52.9",
},
"array": [{
"val": 718,
}, {
"val": 911,
}]
},
"list": [{
"fruit": "Apple",
"dream": null
}, {
"fruit": "Banana",
"dream": "Night"
}]
}, {
"id": 7,
"title": "Network Framework",
"github": "https://github.com/yangKJ/RxNetworks",
"amount": 120.3,
"hex_color2": "#1AC756",
"type": null,
"timestamp" : 590288534,
"bar": null,
"hasDefBool": null,
"time": "2024-05-29 20:23:46",
"iso8601": "2023-05-23T09:43:38Z",
"anyString": null,
"background_color": null,
"dict": null,
"mixDict": null,
"list": null
}]
}
- You can used like:
let datas = ApiResponse<[YourModel]>.deserialize(from: json)?.data
Convert between Model and JSON.
json = """
{
"uid":888888,
"name": "Condy",
"age": 18,
"time": 590277534
}
"""
struct YourModel: HollowCodable {
@Immutable
var uid: Int?
@DefaultBacked<Int>
var age: Int // If the field is not an optional, is 0.
@AnyBacked<String>
var named: String? // Support multiple keys.
var time: Date? // Like to HandyJSON mode `TransformType`.
static var codingKeys: [CodingKeyMapping] {
return [
//CodingKeys.named <-- ["name", "named"],
ReplaceKeys(location: CodingKeys.named, keys: "name", "named"),
//CodingKeys.time <-- TimestampDateTransform(),
TransformKeys(location: CodingKeys.time, tranformer: TimestampDateTransform()),
]
}
}
// JSON to Model.
let model = YourModel.deserialize(from: json)
// Model to JSON
let json = model.toJSONString(prettyPrint: true)
- @Immutable: Becomes an immutable property.
- @IgnoredKey: Optional Property to not included it when Encoding or Decoding.
- @AnyBacked: Support multiple types when Encoding or Decoding.
- @DefaultBacked: When Decoding fails, the default value will be set.
- @BoolCoding: Bool Encoding or Decoding and can set default value.
- @Base64DataCoding: For a Data property that should be serialized to a Base64 encoded String.
- @DateFormatterCoding: Date property that should be serialized using the customized DataFormat.
- @ISO8601DateCoding: Date property that should be serialized using the ISO8601DateFormatter.
- @TimestampDateCoding: Date property that should be serialized to Since1970.
- @DecimalNumberCoding: Decoding to a NSDecimalNumber property.
- @EnumCoding: Decoding to a enumeration with RawRepresentable.
- @HexColorCoding: UIColor/NSColor property that should be serialized to hex string.
- @RGBAColorCoding: RGBA color Encoding or Decoding.
- @PointCoding: CGPoint Encoding or Decoding.
- @RectCoding: CGRect Encoding or Decoding.
- @DictionaryCoding: Any dictionary([String: Any]) Encoding or Decoding.
And support customization, you only need to implement the Transformer protocol.
It also supports the attribute wrapper that can set the default value, and you need to implement the HasDefaultValuable protocol.
- Base64DataTransform: Serialized to a base 64 Data encoded String.
- DataTransform: Serialized to a custom Data encoded String.
- DateTransform: Serialized to a custom Data encoded String.
- CustomDateFormatTransform: Serialized to a custom Date encoded String.
- DateFormatterTransform: Serialized to a custom Date encoded String.
- ISO8601DateTransform: Serialized to a iso8601 time format Date encoded String.
- RFC3339DateTransform: Serialized to a RFC 3339 time format Date encoded String.
- RFC2822DateTransform: Serialized to a RFC 2822 time format Date encoded String.
- TimestampDateTransform: Serialized to a timestamp Since1970 time format Date encoded String/TimeInterval.
And support customization, you only need to implement the TransformType protocol.
Data
and Date
, in order to be as compatible with HandyJSON as possible.
Booming is a base network library for Swift. Developed for Swift 5, it aims to make use of the latest language features. The framework's ultimate goal is to enable easy networking that makes it easy to write well-maintainable code.
This module is serialize and deserialize the data, Replace HandyJSON.
π· Example of use in conjunction with the network part:
func request(_ count: Int) -> Observable<[CodableModel]> {
CodableAPI.cache(count)
.request(callbackQueue: DispatchQueue(label: "request.codable"))
.deserialized(ApiResponse<[CodableModel]>.self)
.compactMap({ $0.data })
.observe(on: MainScheduler.instance)
.catchAndReturn([])
}
RxSwift deserialized extension.
public extension Observable where Element: Any {
@discardableResult func deserialized<T>(_ type: T.Type) -> Observable<T> where T: HollowCodable {
return self.map { element -> T in
return try T.deserialize(element: element)
}
}
@discardableResult func deserialized<T>(_ type: [T].Type) -> Observable<[T]> where T: HollowCodable {
return self.map { element -> [T] in
return try [T].deserialize(element: element)
}
}
@discardableResult func deserialized<T>(_ type: T.Type) -> Observable<ApiResponse<T.DataType>> where T: HasResponsable, T.DataType: HollowCodable {
return self.map { element -> ApiResponse<T.DataType> in
return try T.deserialize(element: element)
}
}
@discardableResult func deserialized<T>(_ type: T.Type) -> Observable<ApiResponse<[T.DataType.Element]>> where T: HasResponsable, T.DataType: Collection, T.DataType.Element: HollowCodable {
return self.map { element -> ApiResponse<[T.DataType.Element]> in
return try T.deserialize(element: element)
}
}
}
CocoaPods is a dependency manager. For usage and installation instructions, visit their website. To integrate using CocoaPods, specify it in your Podfile:
If you wang using Codable:
pod 'HollowCodable'
The general process is almost like this, the Demo is also written in great detail, you can check it out for yourself.π·
Tip: If you find it helpful, please help me with a star. If you have any questions or needs, you can also issue.
Thanks.π
- π· E-mail address: [email protected] π·
- πΈ GitHub address: yangKJ πΈ
Buy me a coffee or support me on GitHub.
![yellow-button](https://user-images.githubusercontent.com/1888355/146226808-eb2e9ee0-c6bd-44a2-a330-3bbc8a6244cf.png)
Booming is available under the MIT license. See the LICENSE file for more info.