HandyJSON repository : https://github.com/alibaba/HandyJSON
HandyJSON
It took me a day and a half to read and learn about Alibaba's open source HandyJSON
libraries. It can only be said that I have a brief understanding of the Swift Runtime
relevant code. However, the harvest is still full. I have always wanted to learn more about protocol-oriented development. Idea, HandyJSON has done a good job in this regard. HandyJSON is a completely protocol-oriented library. The main logic functions are encapsulated in different layers of protocols, and the defined protocols basically provide default implementations.
1. Hierarchy of HandyJSON
First look at the directory structure of HandyJSON as shown below.
Transforms
The folder mainly contains methods for converting between swift types and json supported types, such as URL type to json supported String type, urlString to URL type. The protocol hierarchy is shown in the following figure:
Reflection
Folder, mainly contains the specific implementation process of the mapping (not read carefully at present).
The rest of the files mainly include some configuration files, tool classes, specific classification and forwarding recursive mapping of json to model, etc. As shown in the following figure:
2. The main files in HandyJSON and their functions
2.1 Export.swift
The Export file throws out several protocols:
- HandyJSON: implements serialization, deserialization, etc.;
- HandyJSONEnum: serialization and deserialization of enum;
- HandyJSONCustomTransformable: Implement the protocol to customize the conversion method;
2.2 Configuration.swift
- DeserializeOptions: Define deserialization options, including
caseInsensitive
twodefaultOptions
values. - HandyJSONConfiguration: HandyJSON configuration options. Including debug mode and deserialization settings. Variables are all static types.
2.3 Logger.swift
According to the setting of HandyJSONConfiguration, print logo, Error or Debug or Verbose.
2.4 Serializer.swift
Extend the HandyJSON protocol and convert the HandyJSON type to JSON type. You can convert the type that complies with the HandyJSON protocol to JSONType or JSONString. The process of converting jsonStirng calls the toJSON() method. That is to say, convert it to a json type object first, and then convert it into a json type string.
In addition Collection
, the code is as follows. The advantage of the code below is that it contains recursive calls. This way, no matter how many levels of json are, all the values can be easily retrieved.
public extension Collection where Iterator.Element: HandyJSON {
public func toJSON() -> [[String: Any]?] {
return self.map{ $0.toJSON() }
}
public func toJSONString(prettyPrint: Bool = false) -> String? {
let anyArray = self.toJSON()
if JSONSerialization.isValidJSONObject(anyArray) {
do {
let jsonData: Data
if prettyPrint {
jsonData = try JSONSerialization.data(withJSONObject: anyArray, options: [.prettyPrinted])
} else {
jsonData = try JSONSerialization.data(withJSONObject: anyArray, options: [])
}
return String(data: jsonData, encoding: .utf8)
} catch let error {
InternalLogger.logError(error)
}
} else {
InternalLogger.logDebug("\(self.toJSON()) is not a valid JSON Object")
}
return nil
}
}
2.5 Deserializer.swift
Extends the SwiftJSON type to serialize a json string or json object into a class object.
There are many methods provided in the extension. deserialize
The methods here do some basic judgment processing, and then the specific mapping process is put in the _ExtendCustomModelType
protocol.
2.6 TransformType.swift
Defines the data types supported by json and the conversion of data types in swift.
2.7 Measuable.swift
_Measurable
Protocol, which defines the basic methods that support manipulating memory.
2.8 Transformable.swift
_Transformable
Inherited from _Measurable
, the main function is to distribute different types of conversion processes to different implementations.
learning and advancement
1. associatedtype和typealias
When defining a protocol, it is very useful to associatedtype
declare one or more associated types as part of the protocol definition through keywords, and then typealias
assign values to the associated types in the implementation of the protocol. This can delay the declaration of the specific types of method parameters in the protocol until The specific method is implemented.
2. URL transcoding
Sometimes if the url contains a man, it will cause troubles such as not being able to open the web page, unable to find the URL, etc. You can use the following method addingPercentEncoding
to transcode the String, so that the man will become a symbol with a % sign, so that you can access it normally. .
guard let escapedURLString = URLString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) else {
return nil
}
3. About the usage of the agreement
The following code is RawRepresentable
the definition and partial implementation of Apple API. This code is good to define the RawValue
type in the protocol, and delay the definition of the specific type of RawValue to the specific implementation. When implementing the protocol, you can use where
keywords to judge the type of RawValue and do different implementations.
public protocol RawRepresentable {
associatedtype RawValue
public init?(rawValue: Self.RawValue)
public var rawValue: Self.RawValue { get }
}
extension RawRepresentable where Self.RawValue == Bool {
public func encode(to encoder: Encoder) throws
}
4. Self
Keywords
'Self' is only available in a protocol or as the result of a method in a class
- Self can be used to restrict related types in a protocol
- Self can be used in a class (Class) to act as the return type of a method
//第一种用法
protocol Copyable {
func copy() -> Self
func clamp(intervalToClamp: Self) -> Self
}
//第二种用法
class A: Copyable {
class func calssFunc() -> Self {
let type = self
print(type)
let result = type.init()
return result
}
}
Self
Representing an instance of this type in a protocol Self.self
can represent a type itself.
5.RawRepresentable protocol
A type that can be used to represent another type. enum implements the RawRepresentable protocol, so it class EnumTransform<T: RawRepresentable>
can T
be seen as an enum type (of course, it can be any type that implements the RawRepresentable protocol).
//RawRepresentable协议的声明
public protocol RawRepresentable {
associatedtype RawValue
public init?(rawValue: Self.RawValue)
public var rawValue: Self.RawValue { get }
}
6. swift precompile settings
As shown in the following code, it can be passed in swift #if
, , #else
and #endif
this method can judge different systems, mobile phone models, etc.
#if os(iOS) || os(tvOS) || os(watchOS)
import UIKit
#else
import Cocoa
#endif
7. Declare Protocol Standards
As shown below, if a protocol has a default implementation, in general, we can put all the definitions and implementations of the protocol methods in the extension, so that we do not need to implement any methods when complying with the protocol (the methods in the protocol in pure swift must be fully implemented. ). I have seen that the swift language API and many third-party libraries do this.
protocol _TestProtocol {}
extension _TestProtocol {
func testRun() {
print("run")
}
}
extension UIView: _TestProtocol {
func testRun() {
print("UIView run")
}
}
8. associatedtype and typealias keywords
8.1 Rename
typealias
Keywords always redefine names for existing types. By naming, you can make the code more clear. The main function is to increase the readability of the code.
typealias与泛型
, typealias is single, that is to say, you must assign a specific type to a new name through typealias, instead of renaming the entire generic type.
//如下代码是不能编译通过的
class Person<T> {}
typealias Woker = Person
typealias Worker = Person<T>
//如下, 如果我们在别名中也引入泛型, 则是可以编译通过的
class Person<T> {}
typealias Woker = Person
typealias Worker<T> = Person<T>
8.2 Combination Protocol Types
As shown below, multiple protocols can be grouped together and renamed via &
operators.
protocol Cat {}
protocol Dog {}
typealias Pat = Cat & Dog
8.3 associatedtype
When defining a protocol, it is sometimes useful to declare one or more associated types as part of the protocol. An associated type provides a placeholder name (or alias) for a type in the protocol, which represents the actual type in the protocol Specified only when adopted. You can associatedtype
specify the associated type by keyword.
//模型
struct Model {
let age: Int
}
//协议,使用关联类型
protocol TableViewCell {
associatedtype T
func updateCell(_ data: T)
}
//遵守TableViewCell
class MyTableViewCell: UITableViewCell, TableViewCell {
typealias T = Model
func updateCell(_ data: Model) {
// do something ...
}
}
doubt
func transformFromJSON(_ value: Any?) -> Object?
1. Why doesn't the value in the TransformType protocol need to JSON
be declared? JSON
After using the declaration, isn't it necessary to convert the type when following the protocol?
public protocol TransformType {
associatedtype Object
associatedtype JSON
func transformFromJSON(_ value: Any?) -> Object?
func transformToJSON(_ value: Object?) -> JSON?
}