Skip to content

techsay/Codable

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Codable

Swift Codable、 safe Codable 支持空安全、类型自动转化、model、Array、Enum

使用:添加SafeCodable文件夹

Usage:

//
//  SafeCodableTests.swift
//  SafeCodableTests
//
//  Created by Xiaobo Nie on 2024/5/18.
//

import XCTest

@testable import SafeCodable

final class SafeCodableTests: XCTestCase {
    typealias S = Safety // 非空
    typealias O = Option // 可选
    
    struct TestResponseModel: Codable {
       
        @S.type var code: Int
        @S.type var message: String
        
        // model
        @O.model<TestPersonInfo> var student
        
        // 模型数组
        @S.array<TestPersonInfo> var values
        @O.array<TestPersonInfo> var infos
        
        // Int数组
        @O.array<Int> var ids
        
        // 枚举 String
        @S.enums<Sex> var sex
        // 枚举 Int
        @O.enums<Sex2> var sex2
        
        
        // 字典 对应json 中 dic_a 和 dic_b
        @S.dict<String, AnyCodable> var dicA
        @S.dict<Int, String> var dicB
        
        // 元祖不支持直接解析,使用方法实现
        @O.string var street
        @O.string var city
        func address() -> (street: String?, city: String?) {
            (street, city)
        }
        
    }

    struct TestPersonInfo : Codable {
       
        // 使用统一风格
        @O.type var uid: Int?

        // 使用特定类型
        @O.int var type
        @O.string var country
        @O.bool var close

        // 使用统一风格
        @S.type var suc: Bool

        // 使用特定类型
        @S.string var name
        @S.int var age
        @S.bool var like

        // 设置默认值,对象init构建时生效,解析不生效
        @S.string var weightUnit = "kg"
        @S.float var weight = 55

        // 指定其他key进行解析(uid->id) ,尽量使用原始key,除非涉及关键词
        // 属性编辑完成后,通过方法补全设置CodingKeys, 后续增删都需要对应修改,不在CodingKeys范围内的均不会参与解析
        enum CodingKeys: String, CodingKey {
            case uid = "id"
            case type
            case country
            case close
            case suc
            case name
            case age
            case like
            case weightUnit
            case weight
        }
    }

    enum Sex: String, RawDefaultCodable {
        static func defaultValue() -> Sex {
            .unknown
        }
        case unknown
        case male
        case female
    }
    
    enum Sex2: Int, RawDefaultCodable {
        static func defaultValue() -> Sex2 {
            .unknown
        }
        case unknown = 0
        case male = 1
        case female = 2
    }
    
    var result: TestResponseModel?
    var resultNew: TestResponseModel?
    
    let resDic = """
      {
        "id": 2,
        "code": "200",
        "values": [
            {
                "id": 3.0,
                "type": 4.0,
                "country": "China",
                "suc": true
            },
            {
                "id": "9.0",
                "type": "8",
                "country": "USA",
                "suc": 0
            }
        ],
        "ids": [
            1,
            "2"
        ],
        "dic_a": {
            "id": 1,
            "name": {
                        "id": 1,
                        "name": "std 1"
                    }
        },
        "dic_b": {
            "1": 1,
            "2": "std 1",
            "3": "std 2"
        },
        "students": [
            {
                "id": 2,
                "name": "std 2"
            },
            {
                "id": 3,
                "name": "std 3"
            }
        ],
        "student": {
            "id": 1,
            "name": "std 1"
        },
        "street": "Longyang load.",
        "city": "ShangHai",
        "sex": "male",
        "sex2": "1"
    }
    """
    override func setUpWithError() throws {
        result = nil
    }

    override func tearDownWithError() throws {
        check(result: self.result, tip: "原始json数据解码")
        check(result: self.resultNew, tip: "编码后json数据解码")
        
        func check(result: TestResponseModel?, tip: String) {
            guard let result else {
                XCTFail(tip + " - 解析失败")
                return
            }
            XCTAssertEqual(result.code, 200, "类型不匹配解析失败")
            XCTAssertEqual(result.message, "", "String: key空设置默认值失败")
            XCTAssert(result.values.count > 0, "Array: 列表解析失败")
            // 空Array
            XCTAssertEqual(result.infos?.count, nil, "Array: 可选&空失败")
            XCTAssert(result.values.count == 2, "Array: 列表解析缺失")
            
            for (index, model) in result.values.enumerated() {
                switch index {
                case 0:
                    XCTAssert(model.suc, "Bool: <- true 解析失败")
                    XCTAssertEqual(model.type, 4, "Int <- 4.0  解析失败")
                    XCTAssertEqual(model.country, "China", "String 解析失败")
                    break
                case 1:
                    XCTAssert(!model.suc, "Bool: <- 0 解析失败")
                    XCTAssertEqual(model.type, 8, "Int <- \"8\"  解析失败")
                    break
                default:
                    break
                }
            }
            XCTAssert(result.ids?.count ?? 0 > 0, "ids: 列表解析缺失")
            XCTAssert(result.student != nil, "model: 解析缺失")
            XCTAssert(result.sex == .male, "enum: 解析缺失")
            XCTAssert(result.sex2 == .male, "enum: 解析缺失")
            
            XCTAssert(result.dicA.count > 0, "[String: AnyCodable]: 解析失败")
            XCTAssert(result.dicB.count > 0, "[Int: String]: 解析失败")
        }
    }

    func testExample() throws {
        do {
            let jsonData = resDic.data(using: .utf8)
            if let jsonData {
                
                let decoder = JSONDecoder()
                // 处理驼峰命名法的属性
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                
                // 原始json to model 解码
                result = try decoder.decode(TestResponseModel.self, from: jsonData)
                
                let encoder = JSONEncoder()
                encoder.outputFormatting = .prettyPrinted
                do {
                    let jsonData = try encoder.encode(result)
                    // model to json 编码
                    if let jsonString = String(data: jsonData, encoding: .utf8) {
                        print(jsonString)
                        
                        // 编码后json to model 解码
                        resultNew = try decoder.decode(TestResponseModel.self, from: jsonData)
                        dump(resultNew)
                    }
                } catch {
                    XCTFail("Encoding failed: \(error.localizedDescription)")
                }
            }
        } catch {
            XCTFail("Error decoding JSON: \(error)")
        }
    }

    func testPerformanceExample() throws {
        // This is an example of a performance test case.
        self.measure {
            // Put the code you want to measure the time of here.
        }
    }

}

About

Swift Codable、 safe Codable 支持空安全、类型自动转化、model、Array、Enum

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages