Codable is a new feature for Swift4 that allows faster coding and decoding with less code
JSON
A text-based format for presenting information that can be easily read and written by humans and computers and is the most widely used standard
Simply write a JSON text
Let the json = "" "{" name ":" xiaoming ", "age" : 18, "score" : 99.5} ""." the data (using: utf8)!Copy the code
We use the above JSON to simulate the results of the network data request, and then build the model and parse it
Build the model
We create a Swift type that fits this JSON representation, model, and match the values in json with the types defined in the SWIFT system.
- Objects in JSON are unordered key-value pairs, and are contained in Swift
String
的Dictionary
similar - JSON represents numbers as a series of numbers, independent of any semantics. It does not distinguish between integer and floating point numbers, fixed and non-fixed length numbers, or whether they are binary or decimal. Each implementation decides how to interpret these numbers.
- Codable can automatically map NULL to nil for types with Optional attributes.
struct Student : Codable {
var name : String
var age : Int
var score : Double
}
Copy the code
Let’s first define a struct Student to correspond to the top level object in the data. We’ll use the struct here, but it doesn’t matter if we use Class.
Introduction to Codable Agreement
Student, the structure above, follows the Codable protocol, which greatly improves the experience of translating objects and their representations back and forth.
The best way to understand Codable is to look at its definition:
typealias Codable = Decodable & Encodable
Codable is a hybrid type made up of Decodable and Encodable protocols.
The Decodable protocol defines an initialization function:
init(from decoder: Decoder) throws
A Decodable protocol compliant type can be initialized using any Decoder object.
The Encodable protocol defines a method that:
func encode(to encoder: Encoder) throws
Any Encoder object can create a presentation that complies with the Encodable protocol type.
Only when a type satisfies all the requirements of that protocol can we say that the type complies with that protocol. The only requirement for Decodable is the init(from:) initialization method.
The init(from:) initialization method takes a Decoder parameter. The Decoder protocol needs to clarify the requirements for decoding the representation of a Decodable object into an object. To accommodate various data exchange formats, both decoders and encoders use an abstraction called a container. Containers are used to store values. They can store one value or multiple values. They can have keys for values, like dictionaries, or they can have no keys, like arrays. .
Since the top layer of the JSON representation above has an object, we create a container with keys and use different keys to decode each property.
We create a CodingKeys enumeration that defines the mapping between the property names and the container’s keys. This enumeration declares that its primitive type is String and that the CodingKey protocol is used. Because each name is the same as the key in JSON, we do not need to provide an explicit raw value for this enumeration.
struct Student: Decodable {
// ...
private enum CodingKeys: String, CodingKey {
case name
case age
case score
}
}...
Copy the code
Next, in the init(from:) initialization function we create a keyed container, call the container(keyedBy:) method of the decoder, and pass in CodingKeys as an argument.
Finally, we call the Decode (_:,forKey:) method of the Container to initialize each attribute.
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.age = try container.decode(Int.self, forKey: .age)
self.score = try container.decode(Double.self, forKey: .score)
}...
Copy the code
Now that we have a Student model and Decodable protocol compliance, we can create a Student object from the JSON representation
Decode JSON into model objects
Start by referring to Foundation to use JSONDecoder and JSONEncoder.
import Foundation
Create a JSONDecoder object and call its decode(_:from:) method
let decoder = JSONDecoder()
let student = try! decoder.decode(Student.self, from: json)
Copy the code
Test the success of conversion
print(student.name)
print(student.age)
print(student.score)
Copy the code
Encode the model object as JSON
Now we implement the encode(to:) method required for Encodable protocol. Encode (to:) and init(from:) work like a pair of mirror images. Create a container by calling encoder’s container(keyedBy:) method, passing in the codingkeys.self argument as in the previous step. Here we treat container as a variable (using var instead of let) because this method fills in the encoder’s parameters and needs to be modified. We call encode(_:forKey:) once for each attribute and pass in the value of the attribute and its corresponding key. .
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.name, forKey: .name)
try container.encode(self.age, forKey: .age)
try container.encode(self.score, forKey: .score)
}...
Copy the code
let encoder = JSONEncoder()
let reencodedJSON = try! encoder.encode(stuent)
print(String(data: reencodedJSON, encoding: .utf8)!) .Copy the code
JSON is insensitive to Spaces and whitespace, but you might care. If you find the output unappealing, just set the outputFormatting property of the JSONEncoder to.prettyprinted
Remove unnecessary code
Delete all the Codable implementations we wrote. This should leave only the structure and attribute definitions.
struct Student : Codable {
var name : String
var age : Int
var score : Double
}
Copy the code
Run the code directly and try encoding and decoding JSON. Has anything changed? Not at all. Which brings us to Codable’s killer feature:
Swift automatically integrates Decodable and Encodable consistency.
A type simply adopts the protocol in its declaration, that is, there is no extra work to be done in the extension, as long as one of its attributes conforms to its protocol, everything else is done automatically.
Try parsing JSON text in various formats
JSON with an array at the top:
let jsonModels = """[{"name":"xiaoming","age": 18,"score": 99.5}, {"name":"daxiong","age: 19, ""score": 66}]""".data(using: .utf8)!
Copy the code
Parsing it into an array of Student objects is simple: call decode(_:from:) as before, and replace student.self with [Student].self to parse the Student array instead of individual objects.
let students = try! decoder.decode([Student].self, from: jsonModels)
Why is it so easy? This is thanks to another new feature in Swift 4: conditional consistency.
// swift/stdlib/public/core/Codable.swift.gyb
extension Array : Decodable where Element : Decodable {
// ...
}
Copy the code
Let’s wrap a dictionary around the array:
let jsonComplex = """{"students": [{"name":"xiaoming","age": 18,"score": 99.5}, {"name":"daxiong","age: 19, ""score": 66}]}""".data(using: .utf8)!
Copy the code
If both Array and Dictionary contain keyTypes and valueTypes that are Decodable compliant, then such arrays or dictionaries are themselves Decodable compliant:
// swift/stdlib/public/core/Codable.swift.gyb
extension Dictionary : Decodable where Key : Decodable,
Value : Decodable {
// ...
}...
Copy the code
So you can replace decode(_:from:) with [String: [Student]]. Self (Dictionary
) :
let jsonData = try! decoder.decode([String: [Student]].self, from: jsonComplex)
let students = jsonData["students"]
Copy the code
Alternatively, you can create a Decodable new type, give it a property named [Student] that corresponds to the key name in JSON, and pass the new type into decode(_:from:) :
struct Brige: Decodable {
var students: [Student]
}
let Brige = try! decoder.decode(Brige.self, from: json)
let students = Brige.students...
Copy the code
Codable
Is a hybrid type, made up ofDecodable
和Encodable
Agreement composition.- As long as every attribute in a type complies with the protocol and is declared during the type definition, Swift will automatically integrate
Decodable
和Encodable
. - if
Array
或Dictionary
Each element in theCodable
Type, then they themselves will followCodable