This is the 16th day of my participation in the Gwen Challenge.More article challenges

This article focuses on parsing JSON using the Codable protocol

How to Parse JSON (continued)

Swift4.0 – Golden Age

Let’s start with a simple example

Swift4, the official team launched the Codable protocol to take the JSON-to-Model development experience one step further.

The ease of use and fool-proof usability of Codable protocols reduced the workload for the Web request portion of the Model definition to almost zero.

Let’s use the following example to illustrate.

JSON to parse:

{
    "ret_code":"0",
    "ret_msg":"success",
    "timestamp":"2021-06-04 11:36:55",
    "response_data":{
        "access_token":"some token",
        "expires_in":86400,
        "refresh_token_expires_in":2592000,
        "refresh_token":"some refresh token"
    }
}
Copy the code

Convert to Model:

import Foundation

struct Token : Codable {

    let retCode: String?
    let retMsg: String?
    let timestamp: String?
    let responseData: TokenData?

    enum CodingKeys: String, CodingKey {
        case retCode = "ret_code"
        case retMsg = "ret_msg"
        case timestamp
        case responseData = "response_data"
    }
}

struct TokenData : Codable {

    let accessToken: String?
    let expiresIn: Int?
    let refreshTokenExpiresIn: Int?
    let refreshToken: String?

    enum CodingKeys: String, CodingKey {
        case accessToken = "access_token"
        case expiresIn = "expires_in"
        case refreshTokenExpiresIn = "refresh_token_expires_in"
        case refreshToken = "refresh_token"
    }
}
Copy the code

This JSON backend typically uses the snake naming method to name attributes, while the hump naming is the Swift style. However, it doesn’t matter. The tool software or App helped me do it well.

Through the CodingKey protocol, the snake name is converted to the hump name.

CodingKeys: String (struct); CodingKeys: String (struct);

struct Token : Codable {

    let ret_code: String?
    let ret_msg: String?
    let timestamp: String?
    let response_data: TokenData?
    
}

struct TokenData : Codable {

    let access_token: String?
    let expires_in: Int?
    let refresh_token_expires_in: Int?
    let refresh_token: String?
}
Copy the code

Each structure or class complies with Codable JSON attributes and types.

Considerations for the Model definition

Here’s why we use an optional type to attach each JSON attribute, and whether we use let or var to decorate the attribute:

  • Use the optional type is my personal safe consideration, I have been in the commissioning and production, seen too many such abnormalities, backstage pass a null, and then to pick up, don’t use the optional type App directly to collapse, the background is no guarantee that it passed every parameter has a value, or even impossible to predict some abnormal situation including the background, So the optional type is a little bit more difficult to determine later in the code, but it’s better trouble than a crash.

  • Whether to use let or var to modify attributes depends on whether the attributes in the Model will change in the business and whether the App will actively assign new values to the Model. If there is no change, let is recommended. In addition, var can be used to set an initial value for the attributes of the Model, so that optional types can not be used. Even if a null is sent in the background, the default value is still there.

Continue to grow

If you haven’t noticed, JSON has two fields:

{
    "expires_in":86400,
    "refresh_token_expires_in":2592000
}
Copy the code

Look a little bit at the name of the property and the number that follows it, and you can see that this is a timestamp, whereas in the Model above, we’re taking an Int, right? To pick it up, consider using Date? Why don’t you go get it? Can the data be returned smoothly?

TokenData we changed to this:

struct TokenData : Codable { let accessToken: String? let expiresIn: Date? let refreshTokenExpiresIn: Date? let refreshToken: String? // CodingKeys enumeration is the same as above.Copy the code

Take a screenshot to see the print result:

Int? For the Date? I can still parse it out. Codable is good.

If you need more specific resolution of time, customize the DateDecodingStrategy property in the JSONDecoder class.

Parsing [JSON]

In the previous article, I mentioned that ObjectMapper uses two apis to parse JSON and [JSON], and two types to write BaseModel, but this is not possible in Codable protocol.

import Foundation struct BaseModel<T: Codable>: Codable { let data : T? let errorCode : Int? let errorMsg : String? } struct Item: Codable {/// attribute omitted}Copy the code

When used, BaseModel

or BaseModel<[Itme]>, only the accepted JSON data format meets the requirements, can be resolved, simplified to write BaseModel type and judgment logic, a bus!

There’s a little point here: arrays of elements are Codable, and arrays of elements are Codable.

The value in JSON is converted to an enumeration of Swift

In Codable, we can convert JSON strings, numbers, and other objects into Swift enumerations directly, which is equivalent to one step in one step. Let’s look at the code below:

Struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = struct OnPunchMan = string (); Codable { var name: String? var birthday: Date? var sex: Sex? var skill: [Skill]? var vocation: [Vocation]? // Vocation must be an array if JSON is an array [0, 1, 2]. } /// enumeration of rawValue as String /// /// -man: male // -woman: female // -unknown: unknown enum Sex: String, Cidcidr {case man = "man" case woman = "woman" case unknown = "unknown"} // cidr {case man = "man" case woman = "woman" case unknown = "unknown"} // cidr {case man = "man" case woman = "woman" case unknown = "unknown" VeryCare = "noCare", case veryCare = "veryCare", and so on /// -nocare: veryCare general series /// -haha: veryCare Enum Skill: String, Codable {case noCare case veryCare case haha} // occupation struct Vocation: OptionSet, Codable { typealias RawValue = Int var rawValue: Int init(rawValue: Int) { self.rawValue = rawValue } static let saber = Vocation(rawValue: 1 << 0) static let archer = Vocation(rawValue: 1 << 1) static let lancer = Vocation(rawValue: 1 << 2) static let rider = Vocation(rawValue: 1 << 3) static let berserker = Vocation(rawValue: 1 << 4) static let caster = Vocation(rawValue: 1 << 5) static let assassin = Vocation(rawValue: 1 << 6) }Copy the code

Examples are as follows:

func jsonToModelEnum() {
        let onePunchManString = #"{"name": "sola", "birthday": 562608000, "sex": "unknown", "skill": ["noCare", "veryCare", "haha"], "vocation": [0, 1, 2]}"#
        let onePunchManStringData = onePunchManString.data(using: .utf8)!
        let onePunchMan = try? JSONDecoder().decode(OnPunchMan.self, from: onePunchManStringData)
        print(onePunchMan)
}
Copy the code

Print result:

Optional(OnPunchMan(name: Optional("sola"), birthday: Optional(2018-10-30 16:00:00 +0000), sex: Optional(Sex.unknown), skill: Optional([Skill.noCare, Skill.veryCare,Skill.haha]), vocation: Optional([Vocation(rawValue: 0),Vocation(rawValue: 1),Vocation(rawValue: 2)])))
Copy the code

conclusion

JSON parsing in Swift has gone through ups and downs, and in Codable it’s almost unified.

Codable comes with an official halo that eliminates the need for a third library framework, making it easy to use and the first choice for Swift JSON parsing.

For your homework, check out the Codable source code:

public typealias Codable = Decodable & Encodable /// A type that can encode itself to an external representation. public  protocol Encodable { /// Encodes this value into the given encoder. /// /// If the value fails to encode anything, `encoder` will encode an empty /// keyed container in its place. /// /// This function throws an error if any values are  invalid for the given /// encoder's format. /// /// - Parameter encoder: The encoder to write data to. func encode(to encoder: Encoder) throws } /// A type that can decode itself from an external representation. public protocol Decodable { /// Creates a new instance by decoding from the given decoder. /// /// This initializer throws an error if reading from the decoder fails, or /// if the data read is corrupted or otherwise invalid. /// /// - Parameter decoder: The decoder to read data from. init(from decoder: Decoder) throws }Copy the code

In JSON to Model, we actually use the Decodable protocol for Encodable, which is for binary objects. In general, we use Codable to combine the two protocols because Model objects may need both sides.

Links to resources and tools:

Here are some reference articles and JSON to Model tools.

Swift-codable tips for use

JSONExport

quicktype

Tomorrow to continue

The preparation for some project development and the introduction of the foundation basically come to an end, and then focus on the preparation and example of playing Android App, which is actually stacking code, everyone come on.