Enhancement of current codable protocol using property wrappers for easier access and better [en/de]coding of JSON. The goal of these property wrappers is to avoid implementing custom initializer for decoder, encoder, sometimes coding keys and suffer through boilerplate code.
You can use either SGDecodable or SGEncodable for decoding and encoding respectively.
class Person: SGDecodable {
@SGSerializable(type: .string)
var name: String?
@SGSerializable(key: "age")
var age: Int
@SGSerializable(default: "", key: "city")
var city: String
@SGSerializable(default: Animal(), key: "animal")
var animmal: Animal?
required init() {}
}
class Role: Person {
@SGSerializable
var role: String
}
struct Animal: SGDecodable {
@SGSerializable
var name: String?
}
{
"name": "John",
"age": "12",
"city": "London"
}
Above mentioned JSON model successfully decode into Person class. It will successfully decode age: String into age: Int.
If type is provided, it’ll use that type to downcast the value. Default: .auto will be used.
Other available CodingTypes are mentioned below.
CodingType
case int
case double
case float
case bool
case string
case auto
case none
Custom initializer
struct Person: SGCodable {
init(name: String?) {
self.$name = name
}
@SGSerializable
var name: String
}
Custom Transformer Implementation
You can create own transormation by conforming to SGTranformable.
struct StringToBool: SGTranformable {
typealias FromType = String
typealias ToType = Bool
static func transform(from value: String?) -> Bool? {
guard let value = value else { return nil }
switch value {
case "true": return true
default: return false
}
}
static func transform(from value: Bool?) -> String? {
guard let value = value else { return nil }
let stringValue = value ? "true" : "false"
return stringValue
}
}
struct Person: SGCodable {
@SGTransformSerializable<StringToBool>
var isLogin: Bool?
}
You can access optional values by access attributes directly.
let person = Person()
let isLogin = person.isLogin
now isLogin is optional for transform classes.
You can access non optional values like person.$isLogin. It can automatically gives you default value of else fatalError if not found because of programming error. Make sure you provide default value for custom objects if you want to access non-optional value.
There are some cases when you don’t want to encode to decode attributes. You can do it by simply add variable and don’t use the property wrappers for that attribute.
struct Animal: SGCodable {
@SGSerializable
var name: String?
// This will omit from [en/de]coding
var omitVariable: String?
}
Classes to Dictionary
There is time we need to add custom function or computed property to provide dictionary from object as we write it manually.
Now it is possible by conforming your class to NSObject or NSObjectProtocol and get dictionary with object.swiftDictionary or you can convert any class and struct to dictionary by conforming to SGDictionaryConverter.
class Person: NSObject {
var name = "Frank"
var age = -1
var address: String?
}
let person = Person()
person.swiftDictionary
class Address: SGDictionaryConverter {
var street: String?
var city: String?
var state: String?
var country: String?
}
class Person: Address {
var name = "Frank"
var age = -1
}
let person = Person()
person.toDictionary()
struct Person: SGDictionaryConverter {
var name = "Frank"
var age = -1
var address: Address?
}
struct Address: SGDictionaryConverter {
var street: String?
var city: String?
var state: String?
var country: String?
}
let person = Person()
person.toDictionary()
Inheritance and Composition works out of the box using SGDictionaryConverter.
Strip Null or Default Values
If you want to omit optional or default values like empty string or nil, you can use strippingNullOrDefaults() to any Array or Dictionary.
let person = Person()
person.swiftDictionary.strippingNullOrDefaults()
it will omit default and nil values which can be useful while performing network request.
End Result:
["name": "Frank"]
Inheritance and Composition works out of the box with swiftDictionary and strippingNullOrDefaults()
Helper Functions for [De/En]coding
Define class/struct using required protocol SGCodable.
struct Person {
@SGSerializable
var name: String?
}
Decoable Helpers
let person = try Person.initialize(with: jsonData)
let person = try Person.initialize(with: jsonString)
let person = try Person.initialize(from: jsonURL)
or you can use like this:
let person = try [Person].initialize(with: jsonData)
let person = try [Person].initialize(with: jsonString)
let person = try [Person].initialize(from: jsonURL)
Encodable Helpers
let jsonString = person.jsonString()
let jsonData = person.jsonData()
let dictionary = person.dictionary
Contribute
This is only a tip of the iceberg of what we can achieve using Property Wrappers and how it can be improved [de/en]coding of JSON using property wrappers in Swift. If there is a any custom tranformation that could be added feel free to open an issue requesting it and/or submit a pull request with the new option.
SGSerializable
Enhancement of current codable protocol using property wrappers for easier access and better [en/de]coding of JSON. The goal of these property wrappers is to avoid implementing custom initializer for decoder, encoder, sometimes coding keys and suffer through boilerplate code.
Features
SGCodable
SGDecodable
&SGEncodable
SGSerializable
init(from decoder: Decoder) throws
andfunc encode(to encoder: Encoder) throws
StringToURL
etc. List is available below.SGTransformSerializable
String
to NativeURL
.Example
Requirements
Installation
Swift Package Manager
URL:
https://github.com/rehannali/SGSerializable.git
Add this to the depedencies in your
Package.swift
manifest file.Usage
SGSerializable
.SGCodable
.SGDecodable
orSGEncodable
for decoding and encoding respectively.Above mentioned JSON model successfully decode into
Person
class. It will successfully decode age:String
into age:Int
.If type is provided, it’ll use that type to downcast the value. Default:
.auto
will be used.CodingType
Custom initializer
Custom Transformer Implementation
You can create own transormation by conforming to
SGTranformable
.You can access optional values by access attributes directly.
now
isLogin
is optional for transform classes. You can access non optional values likeperson.$isLogin
. It can automatically gives you default value of elsefatalError
if not found because of programming error. Make sure you provide default value for custom objects if you want to access non-optional value.Custom initializer for tranformable
Defaut Transformer Classes
Omit [En/De]coding
There are some cases when you don’t want to encode to decode attributes. You can do it by simply add variable and don’t use the property wrappers for that attribute.
Classes to Dictionary
There is time we need to add custom function or computed property to provide dictionary from object as we write it manually. Now it is possible by conforming your class to
NSObject
orNSObjectProtocol
and get dictionary withobject.swiftDictionary
or you can convert any class and struct to dictionary by conforming toSGDictionaryConverter
.Strip Null or Default Values
If you want to omit optional or default values like empty string or nil, you can use
strippingNullOrDefaults()
to anyArray
orDictionary
.it will omit default and nil values which can be useful while performing network request.
End Result:
Inheritance and Composition works out of the box with
swiftDictionary
andstrippingNullOrDefaults()
Helper Functions for [De/En]coding
Define class/struct using required protocol
SGCodable
.Decoable Helpers
or you can use like this:
Encodable Helpers
Contribute
This is only a tip of the iceberg of what we can achieve using Property Wrappers and how it can be improved [de/en]coding of JSON using property wrappers in Swift. If there is a any custom tranformation that could be added feel free to open an issue requesting it and/or submit a pull request with the new option.